BlockChain

(BlockChain) ERC721Enumerable 구현

JJeongHyun 2023. 3. 12. 13:32
반응형

https://developerjjh.tistory.com/173

 

(BlockChain) ERC721 구현

https://developerjjh.tistory.com/171 (BlockChain) IERC721Metadata 구현 interface IERC721Metadata.sol 전체 코드 // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; interface IERC721Metadata { function name() external view returns (string memory);

developerjjh.tistory.com

ERC721Enumerable 구현 코드

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "./ERC721.sol";

contract ERC721Enumerable is ERC721 {
  uint[] private _allTokens;

  mapping(address => mapping(uint => uint)) private _ownedTokens;
  mapping(uint => uint) private _ownedTokensIndex;

  constructor(
    string memory _name,
    string memory _symbol
  ) ERC721(_name, _symbol) {}

  function mint(address _to) public {
    _mint(_to, _allTokens.length);
  }

  function _beforeTokenTransfer(
    address _from,
    address _to,
    uint _tokenId
  ) internal override {
    if (_from == address(0)) _allTokens.push(_allTokens.length);
    else {
      uint latestTokenIndex = ERC721.balanceOf(_from) - 1;
      uint tokenIndex = _ownedTokensIndex[_tokenId];
      if (tokenIndex != latestTokenIndex) {
        uint latestTokenId = _ownedTokens[_from][latestTokenIndex];
        _ownedTokens[_from][tokenIndex] = latestTokenId;
        _ownedTokensIndex[latestTokenId] = tokenIndex;
      }
      delete _ownedTokens[_from][latestTokenIndex];
      delete _ownedTokensIndex[_tokenId];
    }
    uint length = ERC721.balanceOf(_to);
    _ownedTokens[_to][length] = _tokenId;
    _ownedTokensIndex[_tokenId] = length;
  }

  function totalSupply() public view returns (uint) {
    return _allTokens.length;
  }

  function tokenByIndex(uint _index) public view returns (uint) {
    require(_index < _allTokens.length);
    return _allTokens[_index];
  }

  function tokenOfOwnerByIndex(
    address _owner,
    uint _index
  ) public view returns (uint) {
    require(_index < balanceOf(_owner));
    return _ownedTokens[_owner][_index];
  }
}
  • uint[] private _allTokens : minting(생성)된 모든 토큰의 tokenId 배열

  • mapping (address => mapping (uint => uint )) private _ownedTokens : 소유자의 토큰의 Index를 통해 tokenId를 접근
    • address => ( index => tokenId )
  • mapping ( uint => uint ) private _ownedTokenIndex : token의 Id에 대한 index
    • tokenId = > index
  • function mint (address _to) : token의 Id는 자동 생성되니 mint 해서 넣을 _to의 주소값만 매개변수로 받는다

  • function _beforeTokenTranfer(address _from, address _to, uint _tokenId) : mint, transferFrom 메서드에서 호출
    • _mint() 함수에서 호출 되어서 실행됐을 때 ( _from == address(0) )
      • 새로운 토큰 발행 시 모든 토큰 배열(allTokens)에 추가 (push 메서드)
    • transferFrom() 함수에서 호출되어서 실행됐을 때 ( _from != address(0) )
      • uint latestTokenIndex = ERC721.balanceOf(_from) -1 : 소유자의 토큰의 마지막 index 저장

      • uint tokenIndex = _ownedTokensIndex[_tokenId] : _tokenId에 대한 소유자의 기준 index를 저장

      • if (tokenIndex != latestTokenIndex) : 보내려는 토큰이 마지막 토큰이 아니라면

      • uint latestTokenId = _ownedTokens[_from][latestTokenIndex] : 소유자의 마지막 토큰의 Id를 저장

      • _ownedTokens[_from][tokenIndex] = latestTokenId : 보낸 계정의 토큰들 중 tokenIndex에 해당하는 값을 위에서 저장한 값으로 정의

      • 보내려는 토큰이 마지막 토큰인지 비교하는 조건문이 끝나고

      • delete _ownedTokens[_from][latestTokenIndex]
        delete _ownedTokensIndex[_tokenId] : 소유자 기준의 토큰 삭제 (보내는 토큰)

      • 이후 받는 계정 쪽에 보낼 토큰에 정보를 추가

      • uint length = ERC721.balanceOf(_to)
        _ownedTokens[_to][length] = _tokenId
        _ownedTokensIndex[_tokenId] = length
  • function totalSupply() : 모든 토큰 개수, 길이를 반환한다

  • function tokenByIndex(uint _index) : _index를 기준으로 전체 토큰 목록 중 해당하는 token을 반환

  • function tokenOfOwnerByIndex (address _owner, uint _index) : 소유자(_owner)와 소유자가 가진 토큰의 _index로 token의 Id를 찾아 반환해 준다