[Solidity] NFT

Alexandria·2024년 3월 4일

Solidity

목록 보기
11/11
post-thumbnail

1. Minting

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);
    }
}

2. 토큰 구매

_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();
    }
}

3. 토큰 재판매

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();
    }
}

3. 토큰 정보 조회

스마트 컨트랙트 내 저장된 배열을 반환하여 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;
    }
}
profile
IT 도서관

0개의 댓글