이전글
[솔리디티] 17. ERC-20 토큰
이전글 [솔리디티] 16. import, 라이브러리 이전글 [솔리디티] 15. 인터페이스 이전글 [솔리디티] 14. 상속, 오버라이딩, 다중 상속 이전글 [솔리디티] 13. receive, fallback, delegatecall 이전글 [솔리디티] 12.
kwjdnjs.tistory.com
ERC-721 NFT
이번 글에서는 NFT 표준인 ERC-721에 대해 알아보겠습니다.
1. NFT
NFT는 대체 불가능한 토큰(Non-fungible token)의 약자입니다. NFT에 대한 보다 자세한 내용은 아래 글을 참조해 주세요.
[이더리움과 월드 컴퓨터] 3-2. NFT
이전글 [이더리움과 월드 컴퓨터] 3-1. 토큰 이전글 [이더리움과 월드 컴퓨터] 2-5. 솔리디티 이전글 [이더리움과 월드 컴퓨터] 2-4. 컨트랙트 계정 이전글 [이더리움과 월드 컴퓨터] 2-3. EVM 이전글 [
kwjdnjs.tistory.com
2. ERC-721
일반적인 이더리움 토큰이 ERC-20 같은 표준을 지키는 것처럼, NFT도 호환성을 위해 표준을 지켜야 합니다. 이더리움 NFT의 대표적인 표준에는 ERC-721이 있습니다.
ERC-721은 다음과 같은 함수와 이벤트를 포함해야 합니다.
function balanceOf(address _owner) external view returns (uint256);
function ownerOf(uint256 _tokenId) external view returns (address);
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
function approve(address _approved, uint256 _tokenId) external payable;
function setApprovalForAll(address _operator, bool _approved) external;
function getApproved(uint256 _tokenId) external view returns (address);
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
직접 함수를 구현하여 NFT 컨트랙트를 제작하는 방법도 있지만, ERC-20과 마찬가지로 OpenZeppelin을 이용하면 보다 쉽게 NFT 컨트랙트를 생성할 수 있습니다.
// contracts/GameItem.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract MyNft is ERC721URIStorage {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
constructor() ERC721("MyNft", "MNT") {}
function mint(address addr, string memory tokenURI)
public
returns (uint256)
{
uint256 newItemId = _tokenIds.current();
_mint(addr, newItemId);
_setTokenURI(newItemId, tokenURI);
_tokenIds.increment();
return newItemId;
}
}
위 코드로 MyNft라는 ERC-721 컨트랙트를 생성할 수 있습니다. 컨트랙트 배포 이후 mint 함수를 이용하여 컨트랙트 내부에서 NFT를 생성할 수 있습니다.
위 코드를 조금 더 자세하게 살펴보겠습니다.
import 부분을 보면 OpenZeppelin에서 제공하는 Counters를 추가로 이용하고 있음을 알 수 있습니다. 또한 using을 사용해 Counter 타입에 Counters 라이브러리를 할당하고 있음을 알 수 있습니다.
Counters는 숫자의 증가나 감소 또는 리셋 기능만을 가지고 있는 자료형을 제공합니다. Counters를 이용하면 쉽게 id를 추적할 수 있습니다. 실제로 Counter 타입으로 생성된 _tokenIds 상태 변수가 mint 함수 내부에서 current 함수로 발행할 id 값을 확인하고, increment 함수를 사용하여 값을 증가시키고 있음을 확인할 수 있습니다.
새로운 NFT를 생성하고 싶다면 _mint를 사용하면 됩니다. NFT를 받게 될 계정의 주소와 발행할 id 값을 입력하면, 새로운 NFT가 발행됩니다.
특정 NFT에 URI 데이터를 할당하고 싶다면 _setTokenURI를 사용하면 됩니다. NFT id와 할당할 URI 문자열을 입력하면 값이 할당됩니다.
실제로 NFT를 발행해보겠습니다. 컨트랙트를 배포한 후, mint 함수를 실행합니다.
트랜잭션이 정상적으로 실행되면 0번 id를 가진 NFT가 생성됩니다. 다음과 같이 ownerOf 함수에 NFT id를 넣으면 NFT의 소유주를 확인할 수 있습니다.
tokenURI 함수를 이용하면 해당 NFT에 할당된 URI를 확인할 수 있습니다.
balanceOf를 이용하면 특정 계정이 몇 개의 NFT를 가지고 있는지 확인할 수 있습니다.
추가적으로 NFT를 다른 계정으로 전송하기 위한 함수들에 대해 알아보겠습니다.
NFT를 전송하기 위한 가장 기본적인 함수는 transferFrom입니다. ERC-20에서의 transferFrom과는 다르게 NFT를 전송하는 주소가 함수를 사용할 수 있으며, 이 경우 전송을 위한 허용 설정이 필요하지 않습니다.
NFT를 받는 주소가 transferFrom을 실행하기 위해 전송 허용을 설정해야 할 경우 approve 함수나 setApprovalForAll 함수를 사용하면 됩니다. approve 함수의 경우 ERC-20과 사용하는 방식이 동일합니다. NFT를 전송받을 주소와 NFT id를 입력하여 사용하면 됩니다. 특정 NFT가 전송이 허용된 주소를 확인하고 싶다면 getApproved 함수를 사용하면 됩니다. NFT id를 입력하면 허용된 주소가 반환됩니다.
setApprovalForAll 함수는 특정 계정이 자신이 가지고 있는 모든 NFT에 접근할 수 있도록 하는 함수입니다. true와 false를 이용하여 허용 여부를 변경할 수 있습니다.
setApprovalForAll 함수를 통해 허용된 여부를 확인하고 싶다면 isApprovedForAll 함수를 사용하면 됩니다.
추가적으로 transfer 함수 외에 두 개의 safeTransferFrom 함수가 존재하는 것을 확인할 수 있습니다. safeTransferFrom은 NFT가 잘못된 주소로 전송돼 영원히 되찾을 수 없게 되는 경우를 방지하기 위해, EOA나 IERC721Receiver 인터페이스를 상속한 CA로만 NFT를 전송할 수 있도록 하는 함수입니다. safeTransferFrom을 사용하면 NFT가 잘못된 CA로 전송돼 소실되는 문제를 방지할 수 있지만, 재진입 공격의 위험성이 있다는 단점이 있습니다.
safeTransferFrom의 사용방법은 transfer 함수와 동일하며, data 매개변수가 있는 safeTransferFrom 함수의 경우 EOA가 아닌 CA로 NFT를 보낼 때 사용됩니다.
지금까지 NFT와 ERC-721 표준에 대해 알아봤습니다. 감사합니다.
다음글
[솔리디티] 19. 메타마스크, 테스트넷 배포
이전글 [솔리디티] 18. ERC-721 NFT 이전글 [솔리디티] 17. ERC-20 토큰 이전글 [솔리디티] 16. import, 라이브러리 이전글 [솔리디티] 15. 인터페이스 이전글 [솔리디티] 14. 상속, 오버라이딩, 다중 상속 이전
kwjdnjs.tistory.com
'이더리움 > 솔리디티' 카테고리의 다른 글
[솔리디티] 19. 메타마스크, 테스트넷 배포 (0) | 2023.08.27 |
---|---|
[솔리디티] 17. ERC-20 토큰 (0) | 2023.08.12 |
[솔리디티] 16. import, 라이브러리 (0) | 2023.08.10 |
[솔리디티] 15. 인터페이스 (0) | 2023.08.09 |
[솔리디티] 14. 상속, 오버라이딩, 다중 상속 (0) | 2023.08.07 |