BlockChain

(BlockChain) ERC721 구현

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

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); function symbol() external view returns (string memory); function tokenU

developerjjh.tistory.com

https://developerjjh.tistory.com/172

 

(BlockChain) IERC721 구현

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

 

ERC721 구현 코드

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

import "./IERC721.sol";
import "./IERC721Metadata.sol";

contract ERC721 is IERC721, IERC721Metadata {
  string public override name;
  string public override symbol;

  mapping(address => uint) private _balances;
  mapping(uint => address) private _owners;
  mapping(uint => address) private _tokenApprovals;
  mapping(address => mapping(address => bool)) private _operatorApprovals;

  constructor(string memory _name, string memory _symbol) {
    name = _name;
    symbol = _symbol;
  }

  function balanceOf(address _owner) public view override returns (uint) {
    require(_owner != address(0), "ERC721 : address zero is not a valid owner");
    return _balances[_owner];
  }

  function ownerOf(uint _tokeId) public view override returns (address) {
    address owner = _owners[_tokeId];
    require(owner != address(0), "ERC721 : invalid tokenId");
    return owner;
  }

  function transferFrom(
    address _from,
    address _to,
    uint _tokenId
  ) external override {
    require(_isApproveOrOwner(_from, _tokenId));
    require(_from != _to);

    _beforeTokenTransfer(_from, _to, _tokenId);

    _balances[_from] -= 1;
    _balances[_to] += 1;
    _owners[_tokenId] = _to;

    emit Transfer(_from, _to, _tokenId);
  }

  function approve(address _to, uint _tokenId) external override {
    address owner = _owners[_tokenId];
    require(_to != owner, "ERC721 : approval to current owner");
    require(
      msg.sender == owner || isApprovedForAll(owner, msg.sender),
      "ERC721 : approve caller is not token owner or approved for all"
    );

    _tokenApprovals[_tokenId] = _to;

    emit Approval(owner, _to, _tokenId);
  }

  function setApprovalForAll(
    address _operator,
    bool _approved
  ) external override {
    require(msg.sender != _operator, "ERC721 : approve to caller");
    _operatorApprovals[msg.sender][_operator] = _approved;
    emit ApprovalForAll(msg.sender, _operator, _approved);
  }

  function getApproved(uint _tokenId) public view override returns (address) {
    require(_owners[_tokenId] != address(0), "ERC721 : invalid tokenId");
    return _tokenApprovals[_tokenId];
  }

  function isApprovedForAll(
    address _owner,
    address _operator
  ) public view override returns (bool) {
    return _operatorApprovals[_owner][_operator];
  }

  function _isApproveOrOwner(
    address _spender,
    uint _tokenId
  ) private view returns (bool) {
    address owner = _owners[_tokenId];
    return (_spender == owner ||
      isApprovedForAll(owner, _spender) ||
      getApproved(_tokenId) == _spender);
  }

  function tokenURI(
    uint tokenId
  ) external view virtual override returns (string memory) {}

  function _mint(address _to, uint _tokenId) public {
    require(_to != address(0));

    address owner = _owners[_tokenId];
    require(owner == address(0));

    _beforeTokenTransfer(address(0), _to, _tokenId);

    _balances[_to] += 1;
    _owners[_tokenId] = _to;

    emit Transfer(address(0), _to, _tokenId);
  }

  function _beforeTokenTransfer(
    address _from,
    address _to,
    uint _tokenId
  ) internal virtual {}
}
  • import "./IERC721.sol"; : IERC721 가져오기

  • import"./IERC721Metadata.sol"; : IERC721Metadata 가져오기

  • contract ERC721 is IERC721IERC721Metadata : interface 상속받는다

  • string public override name, string public override symbol : IERC721Metadata에 선언이 되었기 때문에 override 옵션 속성이 필요하다

  • mapping (address => uint) private _balances : 소유자의 토큰 총 개수

  • mapping (uint => address) private _owner : 토큰에 대한 소유자

  • mapping (uint => address) private _tokenApprovals : 토큰을 위임받은 대리인
    • 토큰값을 받아서 대리인이 있는지 조회가능한 변수를 뜻 (토큰 한 개 기준)
  • mapping (address => mapping (address => bool)) private _ operatorApprovals : 모든 토큰에 대한 대리인이 권한을 위임받았는지 확인 변수

  • function balanceOf(address _owner) : 소유자(_owner)에 대한 토큰의 총개수를 반환

  • function ownerOf(uint _tokenId) : 토큰에 대한 소유자를 반환해 준다

  • function transferFrom(address _from, address _to, uint _tokenId) : 토큰을 보내는 메서드. _from > _to
    • _from이 소유자 본인인지, 대리인 인지 require를 통해 확인
  • function approve(address _to, uint _tokenId) : _to에게 _tokenId에 대한 권한을 위임
    • 토큰의 소유자를 확인 후 소유자가 본인에게 보냈는지 확인
    • 트랜잭션을 보낸 계정이 소유자이거나 위임받은 대리인 인지 확인
    • 그 이후 대리인을 설정하고 권한 위임 성공에 대한 이벤트 로그를 남긴다
  • function setApprovalForAll(address _operator, bool _approved) : 트랜잭션 보낸 계정의 모든 토큰에 대한 권한을 _operator에게 _approved로 설정한다

  • function getApproved(uint _tokenId) : 토큰(_tokenId)에 대한 대리인을 확인해서 주소 계정을 반환한다
    • 토큰에 대한 실 소유자가 null 주소값이 아니어야 하기에 require로 확인 해준다
  • function _isApproveOrOwner(address _spender, uint _tokenId) : 보내는 계정이 토큰에 대해서 권한이 있는지 확인
    • 토큰 보내기 할 때 실행(transferFrom에서 사용되는 함수)
    • 토큰(_tokenId)에 대한 실 소유자 이거나 모든 권한을 가진 대리인이거나 둘 다 아니면 해당 토큰에 대한 권한만 있는지 권한 bool값 반환
  • function tokenURI(uint tokenId) : 상속을 받아서 override 했지만 추후 다시 상속해서 재정의할 메서드이기에 virtual 옵션을 설정

  • function _mint(address _to, uint _tokenId) : 토큰 발행 함수
    • 받는 계정이 null 주소가 아닌지 확인하고 토큰에 대한 실 소유주를 가져온다
    • 그 실 소유주 또한 null 주소가 아닌지 확인
    • 모든 조건에 충족하면 토큰을 발행 후 mint에 대한 이벤트 로그를 남긴다
  • function _beforeTokenTransfer(address _from, address _to, uint _tokenId) internal virtual : _mint, transferFrom에서 사용될 함수이고 기능은 ERC721Enumerable 컨트랙트 안에서 구현