크립토 키티 소스 코드 분석 5번째 컨트랙트 KittyOwnership

in kr •  7 years ago  (edited)

ERC721 컨트랙트에는 인터페이스 정의만 나와있고 실질적으로 내용은 KittyOwnership에 작성되어있다. KittyOwnership은 컨트랙트 이름대로 키티의 소유권(ERC721 토큰의 소유권)에 관한 내용이다.

KittyCore <- KittyMinting <- KittyAuction <- KittyBreeding <- KittyOwnership <- KittyBase, ERC721

KittyBase <- KittyAccessControl

다시한번쓰지만 ERC721토큰은 일반적인 ERC20토큰과 다르게 각각의 토큰에 대해 고유한 값(특성)을 가지고 있어서 서로 교환이 불가능한 토큰이다, Non-fungible token (가치 교환을 하려면 동일한 가치여야하는 데 그렇지 않다. 예를 들어 우리집 강아지랑 옆집 강아지랑 바로 맞바꿀 수 없는 것을 생각해보면된다. 하지만 동일한 천원짜리 지폐(아니면 오백원짜리 동전 2개하고)는 옆집하고 우리집하고 바꿀 수 있다)

KittyOwnership에 정의된 함수(Method)는 다음과 같다. 하나하나 보도록 하자(external 혹은 public만 보도록 한다. 어차피 internal과 private으로 정의된 것은 external/public 함수에서 이용하는 목적에 불과하니...)

    string public constant name = "CryptoKitties";
    string public constant symbol = "CK";
    ERC721Metadata public erc721Metadata;

   bytes4 constant InterfaceSignature_ERC165 =  bytes4(keccak256('supportsInterface(bytes4)'));

    bytes4 constant InterfaceSignature_ERC721 =
      bytes4(keccak256('name()')) ^
      bytes4(keccak256('symbol()')) ^
      bytes4(keccak256('totalSupply()')) ^
      bytes4(keccak256('balanceOf(address)')) ^
      bytes4(keccak256('ownerOf(uint256)')) ^
      bytes4(keccak256('approve(address,uint256)')) ^
      bytes4(keccak256('transfer(address,uint256)')) ^
      bytes4(keccak256('transferFrom(address,address,uint256)')) ^
      bytes4(keccak256('tokensOfOwner(address)')) ^
      bytes4(keccak256('tokenMetadata(uint256,string)'));

function supportsInterface(bytes4 _interfaceID) external view returns (bool)
function setMetadataAddress(address _contractAddress) public onlyCEO {
function balanceOf(address _owner) public view returns (uint256 count) {
function transfer( address _to,   uint256 _tokenId ) external   whenNotPaused {
function approve( address _to,   uint256 _tokenId ) external   whenNotPaused  {
function transferFrom( address _from,  address _to,  uint256 _tokenId ) external  whenNotPaused  {
function totalSupply() public view returns (uint) { 
function ownerOf(uint256 _tokenId)  external   view   returns address owner)   {
function tokensOfOwner(address _owner) external view returns(uint256[] ownerTokens) {
function tokenMetadata(uint256 _tokenId, string _preferredTransport) external view returns (string infoUrl) {
  1. name, symbol : 우선 namesymbol은 ERC20과 마찬가지로 볼 수 있는 것으로 이름과 심볼을 지정했다.

  2. ERC721Metadata라는 컨트랙트를 사용하기 위해 정의함, ERC721Metadata는 키티의 메타 데이터를 저장하기 위한 별도의 컨트랙트인데 왠지 실제로는 사용하고 있지 않는 듯함.

  3. bytes4 constant InterfaceSignature_ERC165 및 bytes4 constant InterfaceSignature_ERC721 : ERC165 및 ERC721 인터페이스 확인을 위한 고정값. ERC165는 여기에 보면 올해 1월에 stadard가 된 것으로 각각의 컨트랙트들이 어떠한 인터페이스를 가지는 지를 확인하기 위한 인터페이스 규격. 예를 들어 다른 컨트랙트가 이 크립토 키티 컨트랙트의 인터페이스를 확인하기 위해 저렇게 Kecaak256해시로 하고 일부 앞 부분만 따서 ID라 하고 비교해서 확인함. 그러면 여기서 constant로 정해놓은 인터페이스가 있다는 말이 되니 인터페이스 확인이 가능함. 그런데 실제로 크립토 키티 게임에서는 사용하지 않는 듯함.

  4. supportsInterface(bytes4 _interfaceID) : 3번에서 실제 확인하기 위한 external로 정의된 함수.

  5. setMetadataAddress (address _contractAddress) : ERC721Metadata의 컨트랙트 주소를 넣기 위한 함수

  1. balanceOf(address _owner) : ERC721 규격에 맞는 함수. _owner가 보유한 키티의 갯수를 반환함

  2. transfer( address _to, uint256 _tokenId ) : 키티를 _to에게 보내는 함수

  3. approve( address _to, uint256 _tokenId ) : 함수를 call하는 사람이 키티의 주인이고 _to에게 해당하는 키티를 다른 곳에 보내는 것이 가능하도록 허가하는 함수. 허가를 시키면 kittyIndexToApproved에 해당하는 키티가 _to의 주소와 함께 추가된다.

  4. transferFrom( address _from, address _to, uint256 _tokenId ) : 8에서 허가한 키티를 실제로 보내는 함수. 이 함수를 call한 사람이 _to가 되어야한다 (받는 사람이 이 함수를 call한다.) 즉, 8에서 _to한테 1번 키티를 보내는 것을 허가하고 나중에 _to는 본 함수를 call하여 1번 키티를 받는다.

  5. totalSupply() public view returns (uint) : 전체 키티의 갯수를 반환한다. 지금 글을 쓰는 현재, 739054 로 나온다.

  6. ownerOf(uint256 _tokenId) external view returns address owner) : 해당하는 키티의 소유자 주소가 나온다. 가장 첫 키티는 주소가 0x0으로 나오고 그 후로 생성된 키티의 첫 번째 키티의 주소를 0x79bd592415ff6c91cfe69a7f9cd091354fc65a18로 나온다.

  7. tokensOfOwner(address _owner) external view returns(uint256[] ownerTokens) : _owner가 보유한 키티(ERC721 토큰)의 index(id)가 array 형태로 나온다. for문으로 키티 id가 1부터 (0은 제네시스 키티로 소유자가 없다!)
    그리고 여기, Allocation Memory Arrays를 보면 dynamic array로 memory는 쓸 수 없어서 일단 uint256[] memory result = new uint256[](tokenCount);처럼 다 할당을 해버린다. 그러면 문제는 전체 tokenCount가 늘어날 수록 result에 할당하는 메모리양이 엄청나게 많아진다. (tokenCount는 현재 739054). 그리고 나서 for문으로 하나하나 찾는다. 상당히 gas를 많이 소모하는 함수이다. 그리고 컨트랙에서 이 함수를 누가 실행을 시킬까 모르겠지만 dynamic array는 return이 컨트랙에서 컨트랙트로 가져올 수 없어서 실행시키면 안된다고 써있다. 관련되서 EIP211가 있는데 아직 구현이 안된 듯하다.

  8. tokenMetadata(uint256 _tokenId, string _preferredTransport) external view returns (string infoUrl) : 토큰 메타데이터를 받는 함수

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!