
openzeppelin을 이용하여 쉽게 구현하기 위해 설치한다.
$ sudo npm -g install @openzeppelin/contracts
소스 상단에 필요한 라이브러리를 로드한다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
NFT 생성하는 행위를 민팅이라 하며 이를 위한 변수들을 정의한다.
contract NFTMarketplace is ERC721URIStorage {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds; // 발행된 NFT 토큰 수
Counters.Counter private _itemsSold; // 판매된 NFT 토큰 수
struct Token {
string title; // NFT 제목
uint256 price; // NFT 가격
bool sold; // 판매 중인지 식별 값
}
mapping(uint256 => Token) private _idToToken; // 마켓에 등록된 토큰 맵
constructor() ERC721("MyNFTToken", "MNT") {} // 생성자
}
민팅은 openzeppelin 내 함수를 이용한다.
첫 번째 인자는 NFT를 소유할 주소이고, 두 번째 인자는 NFT를 식별할 수 있는 정수 타입의 변수이다.
_safeMint를 통해 블록체인에 민팅하고, _setTokenURI를 통해 NFT 토큰에 데이터 URI를 부여할 수 있다.
contract NFTMarketplace is ERC721URIStorage {
...
constructor() ERC721("MyNFTToken", "MNT") {}
// 토큰 생성
function createToken(string memory tokenURI, string memory title, uint256 price) public {
// 토큰 아이디 계산
_tokenIds.increment();
uint256 tokenId = _tokenIds.current();
// 토큰 추가
_idToToken[tokenId] = Token(title, price, true);
// 판매자에게 토큰 발행
_safeMint(msg.sender, tokenId);
// 토큰에 URI 추가
_setTokenURI(tokenId, tokenURI);
}
}
_ownerOf 함수에 NFT 토큰 아이디값을 전달하면 해당 아이디를 소유한 주소를 반환받을 수 있다.
이후 NFT 토큰 소유자에게 transfer 함수에 지불할 양만큼 전달하면 된다.
contract NFTMarketplace is ERC721URIStorage {
...
constructor() ERC721("MyNFTToken", "MNT") {}
// 토큰 생성
function createToken(string memory tokenURI, string memory title, uint256 price) public {
...
}
// 토큰 구매
function buyToken(uint256 tokenId) public payable {
address owner = _ownerOf(tokenId);
uint256 price = _idToToken[tokenId].price;
require(msg.value == price, "Not enough ether to cover asking price");
// 판매자에게 가격 지불
payable(owner).transfer(msg.value);
// 구매자에게 토큰 전송
_transfer(owner, msg.sender, tokenId);
_idToToken[tokenId].sold = false;
// 판매된 NFT 토큰 수 증가
_itemsSold.increment();
}
}
NFT 토큰 정보를 수정하여 판매중으로 변경한다.
해당 식별 값을 통해서 조회가 가능하게 만들것이다.
contract NFTMarketplace is ERC721URIStorage {
...
constructor() ERC721("MyNFTToken", "MNT") {}
// 토큰 생성
function createToken(string memory tokenURI, string memory title, uint256 price) public {
...
}
// 토큰 구매
function buyToken(uint256 tokenId) public payable {
...
}
// 토큰 재판매
function resellToken(uint256 tokenId, uint256 price) public {
require(_ownerOf(tokenId) == msg.sender, "Only item owner can perform this operation");
// 물건을 정보 변경
_idToToken[tokenId].sold = true;
_idToToken[tokenId].price = price;
_itemsSold.decrement();
}
}
스마트 컨트랙트 내 저장된 배열을 반환하여 NFT 토큰 정보를 조회할 수 있다.
contract NFTMarketplace is ERC721URIStorage {
...
constructor() ERC721("MyNFTToken", "MNT") {}
// 토큰 생성
function createToken(string memory tokenURI, string memory title, uint256 price) public {
...
}
// 토큰 구매
function buyToken(uint256 tokenId) public payable {
...
}
// 토큰 재판매
function resellToken(uint256 tokenId, uint256 price) public {
...
}
// 마켓에서 판매중인 토큰 조회
function getListedTokens() public view returns (Token[] memory) {
// 발행된 토큰 수
uint itemCount = _tokenIds.current();
// 판매되지 않은 토큰 수 = 발행된 토큰 수 - 판매된 토큰 수
uint unsoldItemCount = _tokenIds.current() - _itemsSold.current();
// 판매되지 않은 토큰 정보만 저장할 변수 생성
Token[] memory items = new Token[](unsoldItemCount);
uint currentIndex = 0;
// 모든 토큰 조회
for (uint i = 0; i <= itemCount; i++) {
// 판매중인경우
if (_idToToken[i].sold == true) {
// 변수에 저장
items[currentIndex] = _idToToken[i];
currentIndex += 1;
}
}
return items;
}
// 내가 산 토큰 조회
function getMyTokens() public view returns (Token[] memory) {
// 발행된 토큰 수
uint256 totalItemCount = _tokenIds.current();
// 구매한 토큰 수
uint256 itemCount = 0;
// 모든 토큰 조회
for (uint256 i = 0; i <= totalItemCount; i++) {
// 토큰이 판매등록 중이지 않다면
if (_ownerOf(i) == msg.sender && _idToToken[i].sold == false) {
itemCount += 1;
}
}
// 구매한 토큰 정보만 저장할 변수 생성
Token[] memory items = new Token[](itemCount);
uint currentIndex = 0;
// 모든 토큰 조회
for (uint i = 0; i <= totalItemCount; i++) {
// 토큰의 소유자가 호출자인 경우
if (_ownerOf(i) == msg.sender && _idToToken[i].sold == false) {
// 변수에 저장
items[currentIndex] = _idToToken[i];
currentIndex += 1;
}
}
return items;
}
// 내가 등록한 토큰 조회
function getMyListedTokens() public view returns (Token[] memory) {
// 발행된 토큰 수
uint totalItemCount = _tokenIds.current();
// 판매중인 토큰 수
uint itemCount = 0;
// 모든 토큰 조회
for (uint i = 0; i <= totalItemCount; i++) {
// 토큰의 판매자가 호출자인 경우
if (_ownerOf(i) == msg.sender && _idToToken[i].sold == true) {
itemCount += 1;
}
}
// 판매중인 토큰 정보만 저장할 변수 생성
Token[] memory items = new Token[](itemCount);
uint currentIndex = 0;
// 모든 토큰 조회
for (uint i = 0; i <= totalItemCount; i++) {
// 토큰의 소유자가 호출자인 경우
if (_ownerOf(i) == msg.sender && _idToToken[i].sold == true) {
// 변수에 저장
items[currentIndex] = _idToToken[i];
currentIndex += 1;
}
}
return items;
}
}