BlockChain
(BlockChain) ERC721 구현
JJeongHyun
2023. 3. 12. 13:29
반응형
https://developerjjh.tistory.com/171
https://developerjjh.tistory.com/172
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 IERC721, IERC721Metadata : 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 컨트랙트 안에서 구현