크립토 좀비 - 5 : ERC721 & 크립토 수집품

장원령·2021년 3월 18일
0

크립토좀비

목록 보기
5/6

https://cryptozombies.io/ko/course
위 사이트의 내용을 요약하였습니다.

1. 토큰

: 이더리움에서 토큰은 기본적으로 그저 몇몇 공통 규약을 따르는 스마트 컨트랙트이다. 즉 다른 모든 토큰 컨트랙트가 사용하는 표준 함수 집합을 구현하는 것이다.
: 아래와 같은 함수들을 가진다.

transfer(address _to, uint256 _value)
balanceOf(address _owner) 

: 요약하자면, 토큰은 하나의 컨트랙트이고, 몇몇 함수로 전송 등이 가능하게 한다.

  • 종류
  1. ERC20 토큰은 화폐로 적절.
  2. ERC721 토큰은 각각의 토큰이 유일하고 분할이 불가능. 주로 게임에 쓰임 Ex) 크립토키티

2. ERC721 표준, 다중 상속

: ERC271의 표준은 아직 정확히 정의되지 않았다.
: 이에 참고를 위해 ERC20 표준을 가져와 보았다.

// Grabbed from: https://github.com/ethereum/EIPs/issues/20
contract ERC20 {
   function totalSupply() constant returns (uint theTotalSupply);
   function balanceOf(address _owner) constant returns (uint balance);
   function transfer(address _to, uint _value) returns (bool success);
   function transferFrom(address _from, address _to, uint _value) returns (bool success);
   function approve(address _spender, uint _value) returns (bool success);
   function allowance(address _owner, address _spender) constant returns (uint remaining);
   event Transfer(address indexed _from, address indexed _to, uint _value);
   event Approval(address indexed _owner, address indexed _spender, uint _value);
}

: 그 외에 상속의 경우, contract A is B, C등 두개가 상속이 가능하다.

3. balanceOf & ownerOf

A. balanceOf

: address를 받아 그 address가 가진 토큰을 반환

B. ownerOf

: 토큰 ID를 받아 소유하는 사람의 address를 반환

4. 리팩토링

: 제어자와 함수의 이름은 겹칠 수 없어서, 변경해줘야 한다.

5 ~ 6. ERC721: 전송 로직

: 한 사람이 다른 사람에게 소유권을 넘기는 것을 구현하는데 2가지 방식이 있음

  1. 토큰의 소유자가 전송 상대의 address, 전송하고자 하는 _tokenId와 함께 transfer 함수를 호출하는 것이다.
  1. 토큰의 소유자가 먼저 전송 상대의 address, 전송하고자 하는 _tokenId와 함께 approve를 호출한다. 컨트랙트에 누가 해당 토큰을 가질 수 있도록 허가를 받았는지 저장한다(보통 mapping (uint256 => address을 사용함). 이후 누군가 takeOwnership을 호출하면, 해당 컨트랙트는 이 msg.sender가 소유자로부터 토큰을 받을 수 있게 허가를 받았는지 확인하고, 그리고 허가를 받았다면 해당 토큰을 전송한다.

이는 동일한 전송로직이지만, 순서만 반대인 것이다. 전자는 보내는 사람이 함수를 호출하고, 후자는 받는 사람이 호출한다.

*mapping이란? 제공된 key를 가지고 value를 얻어내는 것

: 여기서는 아래와 같이 두 개의 방식에서 모두 쓰일수 있는 _transfer함수를 사용하였습니다.

function _transfer(address _from, address _to, uint256 _tokenId) private {
    ownerZombieCount[_to]++;
    ownerZombieCount[_from]--;
    zombieToOwner[_tokenId] = _to;
    Transfer(_from, _to, _tokenId);
  }

7. ERC721: Approve

: 앞서 봤듯이 approve / takeOwnership을 사용하는 전송은 2단계로 나뉜다.

8. ERC721: takeOwnership

  1. zombieApprovals의 _tokenId 요소가 msg.sender와 같은지 확인한다.
    : require를 이용하여 기존의 if문과는 달리 에러가 발생했을시 앞서 진행한 모든 과정들을 백지화 하는 특징을 지닌다.
  2. _transfer(ownerOf(_tokenId), msg.sender, _tokenId); 토큰을 소유한 사람의 주소,

9. 오버플로우와 언더플로우 막기

먼저, 오버플로우와 언더플로우 개념은 다음과 같다.
1. 오버플로우 : 변수보다 큰 수를 저장할 경우
2. 언더플로우 : 변수보다 작은 수를 저장할 경우

이를 방지하기 위하여 SafeMath 라이브러리를 사용함(단위는 uint256).

using SafeMath for uint

10. safemath 활용 1

: add, sub, mul, div 4가지가 있음.

uint test = 2; // 첫번째 인수는 자동으로 전달 
test = test.mul(3); // test *= 3
test = test.add(5); // test += 5

11. safemath 활용 2

: uint32와 uint16을 혼용할 경우, 오버플로우를 막아주지 못하는 문제점 텍스트(기본적인 연산의 단위가 uint256이기 때문)

: 이에 오버플로우/언더플로우를 막기 위해 라이브러리를 각각 새로 만들어야 함.
: 아래에는 uint32를 단위로 계산할 경우의 예시

library SafeMath32 {

  function mul(uint32 a, uint32 b) internal pure returns (uint32) {
    if (a == 0) {
      return 0;
    }
    uint32 c = a * b;
    assert(c / a == b);
    return c;
  }

  function div(uint32 a, uint32 b) internal pure returns (uint32) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    uint32 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return c;
  }

  function sub(uint32 a, uint32 b) internal pure returns (uint32) {
    assert(b <= a);
    return a - b;
  }

  function add(uint32 a, uint32 b) internal pure returns (uint32) {
    uint32 c = a + b;
    assert(c >= a);
    return c;
  }
}

5-13) 주석처리

natspec
: 솔리디티 커뮤니티에서 주석을 사용할때 표준으로 쓰이는 형식
: @notice는 사용자에게 컨트랙트/함수가 무엇을 하는지 설명
: @dev는 개발자에게 추가적인 상세 정보를 설명, 웬만하면 남기면 좋음
: @param과 @return은 함수의 매개 변수와 반환값

// , /**/ 한줄, 여러줄

0개의 댓글