[마스터링 이더리움] 10장 : 토큰

728x90

 

 

 

토큰은 일반적으로 사소한 내재가치를 지닌, 사적으로 발행된 특수 용도의 동전 같은 물건을 지칭하기 위해 사용되는 단어다.

최근 블록체인에서 관리되는 ‘토큰’은 소유할 수 있고, 자산, 화폐, 혹은 접근 권한 등 블록체인 기반의 추상화된 의미로 재정의되고 있다.

블록체인 토큰은 전 세계적으로 다양한 용도로 사용되며, 서로 교환되거나 전 세계 유동 시장에ㅓ 다른 화폐로 거래될 수 있다.

이 장에서는 토큰의 다양한 용도와 함께 토큰이 어떻게 생성되는 지 살펴본다.

또한 대체성(fungibility) 및 내재성(intrinsicality) 같은 토큰의 속성에 대해서도 논의한다.

마지막으로, 토큰의 기반 기술과 표준을 검토하고 자체 토큰을 만들어 실험을 해본다.

 

 

 

토큰은 어떻게 사용되는가?

토큰의 가장 분명한 사용처는 디지털 개인 화폐다.

토큰은 다양한 사용법이 있고, 종종 다양한 기능을 제공하도록 프로그래밍할 수 있다.

 

토큰의 기능은 다음과 같다.

  • 화폐(currency)
  • 자원(resource)
  • 자산(asset)
  • 접근(access)
  • 지분(equity)
  • 투표(voting)
  • 수집(collectible)
  • 신원(identify)
  • 증명(attestation)
  • 유틸리티(utility)

종종 단일 토큰이 위와 같은 여러 기능을 포함할 수 있다.

현실세계에서는 서로 구분하기 힘든 기능이 있는 데, 이는 상응하는 것들이 연관성을 지니고 있기 때문이다.

예로, 현실 세계에서 운전면허증(증명)은 신원 서류(신원)이며, 이 두 가지를 분리할 수 없다.

하지만 디지털 영역에서는 이전에 합쳐진 기능을 분리할 수 있으며, 독립적으로 개발할 수 있다.

(예: 익명 증명)

 

 

 

토큰과 대체성

💡 대체성이란?
개별 단위가 본질적으로 서로 호환성을 가지고 있는 재화나 상품의 속성

 

토큰은 단일 단위를 값이나 기능의 차이 없이 다른 토큰으로 대체할 수 있는 경우에 대체 가능하다.

(=fungible)

만약, 토큰의 과거 출처를 추적 관리할 수 있다면 그 토큰은 완전히 대체 가능 하진 않다.

왜냐면 토큰의 과거 출처나 기록을 알게 되면, 더 좋은 토큰(흔히 말하는 화리)과 더 안좋은 토큰(블랙리스트)로 구분되어

대체성(기능차이가 없이 교환가능한 성질)이 줄어들거나 사라지기 때문이다.

 

대체 가능하지 않은 토큰은 고유한 유형 또는 무형의 항복을 나타내는 토큰이므로 상호 교환 불가능

예를 들어, 반 고흐의 그림의 소유권을 나타내는 토큰과 피카소 그림의 소유권을 나타내는 토큰은 교환 불가능

각각의 대체 가능하지 않은 토큰은 시리얼 번호처럼 유일한 식별자와 같다.

 

 

거래상대방 위험

💡 거래상대방 위험(counterparty risk)이란?
트랜잭션에서 상대방이 자신의 의무를 이행하지 못하는 위험

 

종류에 따라 트랜잭션에 추가적인 거래상대방 위험이 존재하는데,

주체가 많을 수록 거래상대방 위험이 높다

(어찌보면 당연한 것으로 실패 가능성이 한 사람 더 늘어나기 때문)

 

예시를 들어보자.

금에 대한 거래를 한다고 했을 때, 해당 트랜잭션에는 최소한 3명의 관련자가 있다.

  • 판매자
  • 구매자
  • 금 수탁자 (판매자가 금을 팔아달라고 맡기고 실제로 금을 가지고 있게 됨)

여기서 물리적 자산을 수탁한 사람이 필연적으로 트랜잭션 수행의 당사자가 되고,

실제로 현재 소유하고 있는 사람이므로

이 자산과 관련된 모든 트랜잭션에 수탁자에 대한 거래상대방 위험이 추가된다.

 

즉, 일반적으로 자산의 소유권이 토큰 교환을 통해서 간접적으로 거래되는 경우,

자산 수탁자에 대한 추가적인 거래상대방 위험이 있다.

 

아래와 같은 위험을 예로 들 수 있다.

  • 이들이 진짜 자산을 가지고 있는가?
  • 토큰(인증서, 디지털 토큰 등) 전송을 기반으로 소유권 이전을 이들이 인정하는가?

실제로 이는 현재 내가 공부하고 있는 RWA(Real-world asset)에서

발생하는 Proof of Ownership문제이기도 하다.

 

토큰이 표상하는 자산을 누가 수탁하고 있는 지와

그 자산에 어떤 규칙이 적용되는 지를 이해하는 것이 중요하다

 

 

 

토큰과 내재성

일부 토큰은 블록체인에 내재적인 아이템을 나타낸다.

내가 이해한 바로 쉽게 말해보자면,

그 가치 자체가 토큰에 있고, 블록체인 내에 존재한다는 의미로 이해했다.

 

이러한 디지털 자산은 토큰과 마찬가지로 합의 규칙에 의해 관리된다.

내재적 자산을 나타내는 토큰에는 추가적인 거래상대방 위험이 없다

 

예를 들어,

내가 크립토펑크에 대한 키를 보유하고 있다면,

이는 내가 크립토펑크를 소유하고 있는 것이다.

블록체인 합의 규칙이 적용되고, 개인키의 소유권은 자산의 소유권과 같으며 중개자가 없다.

 

이와 반대로, 많은 토큰은 부동산, 투표권이 있는 회사 주식, 상표, 금과 같이

‘외재적인’ 것을 나타내는 데 사용된다. (RWA라 할 수 있다.)

 

블록체인 내부에 있지 않은 이런 아이템의 소유권은 합의 규칙과 별도로 법률, 관습 및 정책에 의해 관리된다.

이런 자산의 경우, 외재적 자산은 수탁자가 보유하거나, 외부 레지스트리에 기록되거나,

블록체인 외부의 법률 및 정책에 의해 통제되므로 추가적인 거래상대방 위험이 있다.

 

블록체인 기반 토큰의 가장 중요한 파급 효과 중 하나는

외재적 자산을 내재적 자산으로 변환하여 거래상대방 위험을 제거할 수 있는 능력이다.

예) 회사의 지분(외재적)을 DAO 또는 투표 토큰으로 전환

 

 

 

 

토큰 사용: 유틸리티 또는 지분

현재 이더리움에 있는 거의 모든 프로젝트가 일종의 토큰으로 시작한다.

원칙적으로 토큰의 사용은 궁긍적인 관리 도구나 조직 도구로 볼 수 있다.

 

토큰의 역할은 두 가지로 나뉜다.

1️⃣ 유틸리티 토큰 (utility token)

2️⃣ 지분 토큰 (equity token)

 

보통은 두 가지 방법 중 하나로 토큰을 사용하지만,

종종 하나로 융합해서 사용하기도 한다.

 

 

1️⃣ 유틸리티 토큰 (utility token)

어플리케이션 또는 자원에 접근이 요구되는 곳에 사용

예) 공유 스토리지 같은 자원을 나타내는 토큰, SNS 같은 서비스에 접근하는 토큰

 

 

2️⃣ 지분 토큰 (equity token)

스타트업 같은 곳의 소유권에 대한 지분을 나타내는 토큰

배당금 및 이익 분배를 위한 무의결권 주식으로 제한되거나,

탈중앙화된 자율 조직의 투표 지분으로 확장될 수 있다.

여기서 플랫폼의 관리는 토큰 보유자들의 투표에 기반을 둔

상당히 복잡한 거버넌스 시스템을 통해 이루어진다.

 

 

It’s a Duck!

많은 스타트업이 겪는 문제에 토큰의 증권성 이슈가 있다.

토큰을 훌륭한 모금 메커니즘이지만,

대중에서 증권(지분)을 제공한다는 점에 있어서 증권성 이슈를 피할 수 없다.

실제 대부분의 국가에서 증권을 제공하는 것은 규제되고 있고,

상당수의 스타트업은 지분 토큰을 유틸리티 토큰으로 위장함으로써

이러한 규제 제한을 극복하고자 한다.

 

2023년 현재도 증권성 이슈가 굉장히 많고

이 문제는 지켜봐야할 문제이다!

 

 

유틸리티 토큰: 누가 필요한가?

유틸리티 토큰이 스타트업에 상당한 위험과 채택 장벽을 초래한다는 문제점이 있다.

이게 무슨 소리냐면,

스타트업은 이미 혁신을 시도하는 기업일 가능성이 커서 충분히 외로운 길인데,

이러한 혁신에 유틸리티 토큰을 추가하고 사용자가 서비스를 사용하기 위해

토큰을 채택하도록 요구하면 위험이 증가하고 채택 장벽이 높아진다는 말이다.

즉, 더 외로워진다.

 

토큰을 사용함으로써 초기 수용자(즉, 사용자)에게

두 개의 완전히 새로운 기술을 채택하도록 요구하고 있는 셈이 되는 것이다.

바로 (1) 새로운 어플리케이션/플랫폼/서비스

그리고 (2) 토큰 경제다.

 

대부분의 토큰 가치가 크지 않은 근본적인 이유는

토큰이 단지 하나의 회사와 같이 매우 좁은 환경에서만 사용할 수 있기 때문이다.

이렇게 제한된 유동성, 제한된 적용 가능성 및 높은 전환 비용은

토큰 가치를 단지 ‘토큰’으로서만의 가치로 떨어뜨린다.

 

아래와 같은 상황이면 토큰을 사용해라.

(1) 토큰을 사용하지 않으면 어플리케이션이 작동하지 않을 때

(2) 토큰이 근본적인 시장 장벽이나 접근 권한 문제를 해결할 때

 

하지만 단지 자금을 빨리 조성할 수 있고

그것이 주식공모가 아닌 것처럼 위장하기 위한 것이라면

토큰을 사용하지 말아라.

 

 

 

이더리움 토큰

첫 번째 블록체인 화폐인 비트코인은 그 자체가 토큰이다.

 

토큰은 이더와 다르다. 토큰 자체에 대한 정보를 이더리움 프로토콜은 가지고 있지 않다.

즉, 이더 전송은 이더리움 플랫폼의 본질적인 동작이지만 토큰을 보내거나 소유하는 것은 다른 행위다.

  • 이더리움 계정의 이더 잔액은 프로토콜 수준에서 처리
  • 이더리움 계정의 토큰 잔액은 스마트 컨트랙트 수준에서 처리

 

이더리움에서 새 토큰을 만들려면 새로운 스마트 컨트랙트를 만들어야 한다.

배포된 스마트 컨트랙트는 소유권, 이전 및 접근 권한을 포함한 모든 것을 처리한다.

토큰 생성 시 원하는 방법대로 스마트 컨트랙트를 작성할 순 있지만, 기준 표준을 따르는 것이 가장 바람직하다.

토큰 표준에 대해 알아보고 이 표준의 장단점에 관해 이야기 해볼 것이다.

 

 

 

ERC20 토큰 표준

2015년 11월 파비안 보겔스텔라가 ERC(Ethereum Request for Comments)를 발표했고,

깃허브 이슈번호 20이 자동으로 할당되어 ‘ERC20 토큰’이 되었다.

대다수의 토큰은 현재 ERC20 표준을 기반으로 하며

사실 의겸 수렴을 통해 ERC20 요청은 EIP-20(Ethereum Improvement Proiposal)이 되었지만

여전히 ERC20으로 불린다.

💡 ERC20 이란?
대체 가능한 토큰(fungible token)의 표준

✔︎ 토큰의 단위가 다르더라도 같은 ERC20이면 상호 교환이 가능하다
✔︎ 고유한 특성이 없다 

 

ERC20 표준은 토큰을 구현하는 컨트랙트에 대한 공통 인터페이스를 정의해

모든 호환 가능한 토큰에 같은 방식으로 접근하고 사용할 수 있다.

인터페이스 = (개발자가 추가할 수 있는 선택적 기능과 속성) + (표준을 구현하는 데 필요한 여러 함수들)

 

 

 

ERC20 필수 함수와 이벤트

ERC20을 준수한 토큰 컨트랙트는 다음 함수 및 이벤트를 제공해야 한다.

 

필수 구현 :

  • totalSupply : 현재 존재하는 해당 토큰의 전체 개수를 리턴
  • balanceOf : 주소가 주어지면 해당 주소의 토큰 잔액을 반환
  • transfer : 주소와 금액이 주어지면 해당 주소로 금액만큼의 토큰의 양을 전송
    (From은 전송을 실행하는 주소가 됨)
  • transferFrom : 보낸 사람, 받는 사람, 금액이 주어지면 한 계정에서 다른 계정으로 토큰을 전송
    (transfer와 다른 점은 From을 지정한다는 점)
    approve와 함께 사용한다.
  • approve : 수취인 주소와 금액이 주어지면 그 주소가 승인을 한 계정에서 최대 금액까지 여러 번 송금할 수 있도록 승인
  • allowance : 소유자 주소와 지출자 주소가 주어지면, 지출자가 출금할 수 있도록 소유자가 승인한 잔액을 리턴
  • Transfer : 전송(transfer transferFrom 호출)이 성공하면 이벤트가 트리거 된다(0값 전송도 포함)
  • Approval : approve를 성공적으로 호출하면 이벤트가 기록된다.

선택적 함수 :

  • name : 사람이 읽을 수 있는 토큰의 이름을 반환 (예: 달러)
  • symbol : 사람이 읽을 수 있는 기호를 반환 (예: $)
  • decimals : 토큰 양을 나눌 수 있는 소수 자릿수를 반환한다.
    예를 들어, decimals = 2 이면, 토큰양을 10^2인 100으로 나눠 표현한다.

 

솔리디티에서 ERC20 인터페이스 정의

솔리디티에서는 ERC20 인터페이스 사양을 아래와 같이 정의했다.

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);
}

 

 

ERC20 데이터 구조

ERC20은 아래와 같은 2개의 데이터 구조를 포함하고 있다.

  • 잔고 추적할 때 사용하는 것
  • 사용할 수 있게 허용한 양 추적할 때 사용하는 것

 솔리디티에서 위 두 개는 모두 데이터 매핑(data mapping)을 통해 구현된다.

 

 

1️⃣ balances

mapping(address => uint256) balances;

첫 번째 데이터 매핑인 balances는 소유자별로 토큰 잔액을 내부 테이블로 구현한다.

이렇게 함으로써 토큰 컨트랙트에서 토큰을 소유한 사람을 추적할 수 있다.

balances[address] = (unit256)balances값

위와 같이 key-value값이라고 생각하면 편하다.

 

 

2️⃣ allowed

mapping (address => mapping (address => uint256)) public allowed;

두 번째 데이터 구조는 허용량의 데이터 매핑이다.

ERC20 토큰을 사용하면 토큰 소유자가 권한을 위임자에게 위임하여

소유자의 잔액에서 특정 금액(허용 한도, allowed)을 지출할 수 있게 된다.

 

컨트랙트는 2차원 매핑을 허용하기 때문에 위와 같은 매핑도 가능하다.

위의 구조를 보면 먼저 첫번째 매핑 키인 address는 토큰 소유자의 주소이고,

번째 매핑에서의 address는 지출자의 주소, uint256은 허용한도이다.

 

 

ERC20 Workflow: ‘transfer’와 ‘approve & transferFrom’

ERC20 토큰 표준에는 두 가지 transfer 함수가 있다.

 

1️⃣ transfer 함수를 사용하는 단일 트랜잭션인 간단한 워크플로우

지갑에서 다른 지갑으로 토큰을 보내는 데 사용되는 워크플로우로,

대다수의 토큰 트랜잭션은 transfer 워크플로와 더불어 일어난다.

트랜잭션을 실행하는 주소가 발신인이 된다.

 

예) A가 B에게 10개의 토큰을 전송하려고 할 때,

인수로 B의 지갑 주소와 10을 토큰 컨트랙트에 전송하면

토큰 컨트랙트는 A의 잔액을 -10, B의 잔액을 +10하고

Transfer event를 발생시킨다.

 

 

2️⃣ approvetransferFrom을 사용하는 두 단계 트랜잭션 워크플로우

토큰 소유자가 제어를 다른 주소에 위임할 수 있다.

보통 거래소와 토큰 배포 컨트랙트에 제어권을 위임할 때 많이 사용한다.

 

예) 회사가 ICO를 위해 토큰을 판매하는 경우,

특정 양의 토큰을 배포하기 위해 크라우드세일(crowdsale) 컨트랙트 주소를 approve할 수 있다.

💡 ICO 란?

기업 및 조직에서 토큰을 판매하여 자금을 모으는 데 사용하는 크라우드 펀딩 메커니즘

용어는 IPO에서 파생되었으며 IPO시장과 달리

개방적이고 국제적이며 정형화되어 있지 않다.

 

Alice가 일반 구매자에게 AliceCoin의 50%를 판매하도록 AliceICO 컨트랙트를 허용하려 한다고 가정해보자.

과정은 아래와 같다.

  1. 우선 Alice는 AliceCoin ERC20 컨트랙트를 배포해 모든 앨리스코인을 자신의 주소로 발급한다.
  2. 앨리스는 이더용 토큰을 판매할 수 있는 AliceICO 컨트랙트를 실행한다.
  3. approve 함수의 인수로 (1) AliceICO 주소(2) totalSupply의 50%인 500을 넣어
    approve를 호출하는 트랜잭션을 AliceCoin 컨트랙트로 보낸다.
  4. 위의 실행 결과로 Approval event를 트리거한다.
  5. 이제 AliceICO 컨트랙트는 앨리스코인을 판매할 수 있다. (approve받은 양만큼)
  6. AliceICO 컨트랙트가 Bob으로부터 이더를 받으면, 밥에게 앨리스코인을 보내준다.
    이더와 앨리스코인 간에 환율이 이미 정해져있다. (AliceICO 컨트랙트를 만들 때 앨리스가 설정)
  7. AliceICO 컨트랙트에서 앨리스코인 transferFrom 함수를 호출하고 이 때 인수는
    (1) 보낸 사람 주소(앨리스 주소) (2) 받는 사람 주소(Bob 주소) (3) value(전송할 양)
  8. AliceCoin 컨트랙트는 앨리스 주소에서 밥의 주소로 잔액을 전송하고 Transfer 이벤트를 트리거한다.

이 때, AliceICO 컨트랙트는 앨리스가 설정한 승인 한도를 초과하지 않는 한 계속 transferFrom을 호출할 수 있다. 또한 allowance 함수를 호출해 판매할 수 있는 앨리스코인 토큰의 수를 관리할 수도 있다.

 

 

ERC20 구현

솔리디티 코드 약 30줄 정도로 ERC20 호환 토큰을 구현할 수는 있으나, 대부분의 구현은 더 복잡하다.

 

EIP-20 표준에서 언급한 두 가지 구현이 있다.

 

 

 

자체 ERC20 토큰 출시

트러플 프레임워크를 사용해서 자체 토큰을 만들고 실행해 보자.

트러플 콘솔을 사용한 METoken과의 상호작용

컨트랙트 주소에 ERC20 토큰 보내기

‘approve & transferFrom’ Workflow 시연

(실습 생략)

 

 

ERC20 토큰 문제

컨트랙트 주소로 토큰을 전송하는 문제에서 알 수 있듯이 몇 가지 잠재적인 함정이 있다.

 

1️⃣ 토큰은 실제로 움직이지 않고, 상태변화만 각 토큰 컨트랙트가 관리한다.

첫 번째 문제는 토큰과 이더 자체 사이의 미묘한 차이와 관련이 있다.

이더는 수신자의 주소를 목적지로 가지고 있는 트랜잭션에 의해 전송이 일어나는 반면,

토큰 전송은 특정한 토큰 컨트랜트 상태 안에서 일어나고 수신자의 주소가 아닌 토큰 컨트랙트를 목적지로 한다.

토큰 컨트랙트는 밸런스를 관리하고 이벤트를 발생시킨다.

토큰 전송에서 트랜잭션이 토큰 수신자에게 실제로 보내는 것이 아니다. (즉, 토큰이 실제로 보내지는 게 아니라는 의미)

대신, 받는 사람의 주소가 토큰 컨트랙트 자체의 맵에 추가된다.

이더를 주소로 보내는 트랜잭션은 실제 그 주소의 상태를 변경하지만,

토큰을 주소로 전송하는 트랜잭션은 수신자 주소의 상태를 변경하지 않고 단지 토큰 컨트랙트의 상태만 변경한다.

 

위와 같은 문제로, ERC20 토큰을 지원하는 지갑조차도 모든 토큰 잔액을 인식하지 못한다.

일부 인기 있는 토큰 컨트랙트를 모니터링해서 해당 토큰에 대한 잔액을 감지해 지갑 기능을 해주지만,

그 외의 토큰은 보통 사용자가 명시적으로 토큰 컨트랙트를 추가해야만 토큰 잔액을 인식할 수 있다.

 

하지만 사실 사용자는 모든 ERC20 토큰 컨트랙트에서 잔액을 추적하기 원하지 않는다.

스팸과 유사한 형태로, 사용자를 유치하기 위해 일부 ‘듣보’ 토큰이

이더 활동이 있는 계정에 대해 밸런스를 자동으로 생성하기 때문이다.

따라서 모든 토큰에 대해 잔액 추적을 한다면 지갑은 ‘쓰레기’ 토큰으로 가득차있을 수도 있다.

(정확히는 내 계정이 실제로 차있는 것이 아닌 토큰 컨트랙트에 차있는 것이긴 하다.)

 

토큰은 이더와 같은 방식으로 동작하지 않는다.

💡 이더 vs 토큰
  • 이더
    • send 함수에 의해 전송
    • 컨트랙트에 있는 payable 함수 또는 외부 소유 주소에 의해 수신
  • 토큰
    • ERC20 컨트랙트에 존재하는 함수인 transfer 또는 approve + transferFrom에 의해 전송
    • 수령인 컨트랙트에서 payable 함수를 트리거하지 않음 

 

2️⃣ 토큰을 보내는 데도 ‘이더’가 필요하다.

이더를 보내거나, 이더리움 컨트랙트를 사용하기 위해선 이더가 필요한데

어떤 종류의 토큰이든 토큰을 보내는 데도 이더가 필요하게 된다.

트랜잭션의 가스를 토큰으로 지급할 수 없고, 토큰 컨트랙트는 가스를 지급할 수 없다는 뜻이다.

이 점은 사용자 경험 측면에서 부정적인 영향을 미친다.

 

3️⃣ 이더리움 내의 추상화 및 인터페이스 경계와 관련된 일반적인 문제점

이 부분은 해결 가능하지 않을 수도 있으며

해결 가능해도 이더리움 내의 기본 구조 자체를 변경해야 할 수도 있다.

 

이러한 문제를 해결하기 위한 다양한 제안을 살펴보자.

 

 

ERC223: 제안된 토큰 컨트랙트 인터페이스 표준

ERC223 제안은 목적지 주소가 컨트랙트인지 아닌지 여부를 감지함으로써

실수로 토큰을 컨트랙트로 전송하는 문제를 해결한다.

즉, 토큰을 지원하지 않는 컨트랙트인지 아닌지를 감지해서 실행 여부를 결정한다.

 

ERC223 토큰을 받도록 설계된 컨트랙트에 tokenFallback이라는 함수를 구현해야 한다.

전송 목적지가 컨트랙트인데, 해당 컨트랙트에서 토큰에 대한 지원을 안한다면

(즉, tokenFallback을 구현하지 않은 상황이면) 전송이 실패한다.

 

대상 주소가 컨트랙트인지 감지하기 위해 ERC223 표준 구현

다소 독창적인 방법으로 인라인 바이트코드의 작은 세그먼트를 활용한다.

function isContract(address _addr) private view returns (bool is_contract) {
 uint length;
	assembly {
	// retrieve the size of the code on target address; this needs assembly 
	length := extcodesize(_addr)
	}
    return (length>0);
 }

 

ERC223 컨트랙트 인터페이스 사양은 아래와 같다.

interface ERC223Token {
	uint public totalSupply;
	function balanceOf(address who) public view returns (uint);
	function name() public view returns (string _name);
	function symbol() public view returns (string _symbol); function decimals() public view returns (uint8 _decimals); function totalSupply() public view returns (uint256 _supply);
	function transfer(address to, uint value) public returns (bool ok);
	function transfer(address to, uint value, bytes data) public returns (bool ok); function transfer(address to, uint value, bytes data, string custom_fallback)
	public returns (bool ok);
	event Transfer(address indexed from, address indexed to, uint value, bytes indexed data);
}

ERC223은 널리 구현되지 않았으며, ERC 토론 스레드에서는 다양한 문제에 대한 논의가 계속되고 있다.

 

 

 

 

ERC777: 제안된 토큰 컨트랙트 인터페이스 표준

향상된 토큰 컨트랙트 표준에 대한 또 다른 제안은 ERC777이다.

 

이 제안의 목표는 아래와 같다.

  • ERC20 호환인터페이스제공
  • send 함수를 사용하여 토큰을 전송 (이더전송과 비슷함)
  • 토큰 컨트랙트 등록을 위해 ERC820과 호환 가능
  • 컨트랙트와 주소가 토큰을 전송하기 전에 어느 토큰을 전송할 수 있는가를 tokensToSend 함수를 통해 컨트롤 할 수 있도록 허용
  • 수신자의 tokensReceived 함수를 호출하여 컨트랙트 및 주소에 토큰의 수신사실을 통지할 수 있게 하고 컨트랙트에 tokensReceived 함수를 제공하도록 요구함으로써 컨트랙트가 잠길 확률을 줄인다.
  • 기존 컨트랙트가 tokensToSend 및 tokensReceived 함수에 대해 프록시 컨트랙트를 사용하도록 허용
  • 컨트랙트로 보내거나 EOA로 보내거나와 같은방식으로 작동
  • 토큰 발행 및 소각을 위한 특정 이벤트 제공
  • 토큰 보유자 대신 토큰을 이동시키는 운영자 (검증된 컨트랙트, 신뢰하는 제 3자)허용
  • userData 및 operatorData 필드에서 토큰 전송 트랜잭션에 대한 메타데이터를 제공
interface ERC777Token {
	function name() public constant returns (string);
	function symbol() public constant returns (string);
	function totalSupply() public constant returns (uint256);
	function granularity() public constant returns (uint256);
	function balanceOf(address owner) public constant returns (uint256);
	function send(address to, uint256 amount, bytes userData) public;
	function authorizeOperator(address operator) public; 
	function revokeOperator(address operator) public;
	function isOperatorFor(address operator, address tokenHolder)
			public constant returns (bool);
	function operatorSend(address from, address to, uint256 amount,bytes userData,bytes operatorData) public;
	event Sent(address indexed operator, address indexed from, address indexed to, uint256 amount, bytes userData, bytes operatorData);
	event Minted(address indexed operator, address indexed to, uint256 amount, bytes operatorData);
	event Burned(address indexed operator, address indexed from, uint256 amount, bytes userData, bytes operatorData); 
	event AuthorizedOperator(address indexed operator,address indexed tokenHolder);
	event RevokedOperator(address indexed operator, address indexed tokenHolder);
}

 

 

ERC777 후크

ERC777 토큰 발신자 후크 사양은 아래과 같다.

interface ERC777TokensSender {
			function tokensToSend(address operator, address from, address to,
			uint value, bytes userData, bytes operatorData) public;
}

이 인터페이스의 구현은 토큰 지불 통지, 처리 또는 예방을 원하는 모든 주소에 필요하다.

또한 이 인터페이스를 구현하는 컨트랙트의 주소

컨트랙트 자체 또는 다른 주소용 인터페이스를 구현하는 것과 관계없이 ERC820을 통해 등록해야 한다.

 

ERC777 토큰 수신자 후크 사양은 아래와 같다.

interface ERC777TokensRecipient { 
	function tokensReceived(
		address operator, address from, address to, 
			uint amount, bytes userData, bytes operatorData) public; 
}

이 인터페이스의 구현은 토큰의 수신 통지, 처리 또는 거부을 원하는 모든 주소에 필요하다.

또한 발신자 인터페이스와 마찬가지로 동일한 논리 및 요구사항이 토큰 수신자 인터페이스에도 적용되어야 한다.

즉, 수신자 컨트랙트가 토큰 잠김을 막기 위해 이러한 인터페이스를 구현해야 한다.

만약, 수신자 컨트랙트가 이 인터페이스를 구현하는 주소를 등록하지 않으면, 토큰 전송이 실패한다.

 

여기서 중요한 점은 주소당 하나의 토큰 발신자와 수신자만 등록할 수 있다는 점이다.

….

?

 

 

ERC721: 대체 불가능한 토큰(증서) 표준

ERC20 토큰 표준은 각 계정의 최종 잔액만을 추적하며 토큰의 출처를 추적하지는 않는다.

그에 반해, ERC721 제안은 증서(deed)로도 알려진 대체할 수 없는(non-fungible) 토큰에 대한 표준을 위한 것이다.

‘증서’는 ‘재산의 소유권(ownership of property)’ 부분을 반영하기 위한 것으로,

앞으로 블록체인 플랫폼에서 디지털 서명을 기반으로 한 법적 소유권이 법적으로 인정될 가능성이 있다.

 

대체할 수 없는 토큰은 게임 아이템이나 디지털 수집물 같은 고유한 아이템일 수 있다.

또한 집, 자동차, 삽화 같은 것으로 소유권을 추적하기 위한 실물일 수도 있다.

증서는 대출, 담보권, 지역권 등과 같은 가치를 지닌 것도 포함된다.

 

ERC721 표준은 증서에 의해 그 소유권이 고유하게 추적될 수 있는 한,

그 대상의 종류에 대해 어떤 제한이나 규정을 두지 않으며, 이러한 추적은 256비트 식별자에 의해 이루어진다.

 

ERC721에서 사용되는 내부 데이터 구조는 아래와 같다.

// Mapping from deed ID to owner
mapping (uint256 => address) private deedOwner;

 

ERC20의 매핑(소유자의 잔액을 추적하고 소유자가 매핑의 기본 키)과 달리,

ERC721의 매핑은 각 증서 ID와 소유권자를 추적하며 증서 ID가 매핑의 기본 키가 된다.

이 차이로부터 대체할 수 없는 토큰(NFT)의 모든 속성이 나온다.

 

 

ERC721 컨트랙트 인터페이스 사양은 아래와 같다.

interface ERC721 /* is ERC165 */ {
    event Transfer(address indexed _from, address indexed _to, uint256 _deedId); 
    event Approval(address indexed _owner, address indexed _approved,uint256 _deedId);
    event ApprovalForAll(address indexed _owner, address indexed _operator,bool _approved);
    function balanceOf(address _owner) external view returns (uint256 _balance); 
    function ownerOf(uint256 _deedId) external view returns (address _owner); 
    function transfer(address _to, uint256 _deedId) external payable;
    function transferFrom(address _from, address _to, uint256 _deedId)
    external payable;
    function approve(address _approved, uint256 _deedId) external payable; 
    function setApprovalForAll(address _operateor, boolean _approved) payable; 
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

ERC721는 또한

(1) 메타데이터를 위한 인터페이스

(2) 증서 및 소유자의 열거를 위한 인터페이스

이렇게 2개의 선택적 인터페이스를 지원한다.

 

(1) 메타데이터를 위한 인터페이스

interface ERC721Metadata /* is ERC721 */ {
		function name() external pure returns (string _name);
		function symbol() external pure returns (string _symbol);
		function deedUri(uint256 _deedId) external view returns (string _deedUri);
}

 

(2) 증서 및 소유자의 열거를 위한 인터페이스

interface ERC721Enumerable /* is ERC721 */ {
		function totalSupply() external view returns (uint256 _count);
		function deedByIndex(uint256 _index) external view returns (uint256 _deedId); 
		function countOfOwners() external view returns (uint256 _count);
		function ownerByIndex(uint256 _index) external view returns (address _owner); 
		function deedOfOwnerByIndex(address _owner, uint256 _index) external view
		returns (uint256 _deedId);
}

 

 

토큰 표준 사용

표준은 정확히 무엇을 하는 지

이 표준을 사용해야만 하는 지

어떻게 사용해야 하는 지

이러한 표중 이상의 기능을 추가해야 하는 지

어떤 표준을 사용해야 하는 지

 

토큰 표준이란 무엇인가? 그 목적은 무엇인가?

토큰 표준은 구현을 위한 최소 사양이다.

즉, ERC20을 준수하려면 최소한 표준에 명시된 함수와 동작을 구현해야한다는 것이다.

 

이러한 표준의 주요 목적은 컨트랙트 간의 상호운용성(Interoperability)을 장려하는 것이다.

따라서 모든 지갑, 거래서, 사용자 인터페이스 및 기타 인프라 구성요소는

사양을 따르는 컨트랙트와 예측 가능한 방식으로 인터페이스할 수 있다.

 

표준은 규범적(prescriptive)이라기보다는 기술적(descriptive)인 것을 의도한다.

기능을 어떻게 구현할 지는 본인에게 달려있다.

특정 상황하에서 작동을 규정하는 기능적 요건들을 가지고 있지만, 구현 방법을 제시하진 않는다.

 

 

이 표준을 사용해야 하는가?

자신의 길을 만들어 기존 표준을 무시하는 경향을 “NIH(Not Invented Here)”증후군이라고 하며,

오픈 소스 문화와 상반된다.

 

 

기존 표준을 따라하면 ‘상투적’이고 혁신을 제한하는 방법이지만,

수백 개의 어플리케이션 경험을 통해 나타났기 때문에 대부분 사용 사례에 잘 들어맞는다.

 

뭐를 선택할 지는 본인의 가치에 달렸다.

 

성숙도에 의한 보안

표준의 선택을 넘어 구현의 선택이 있다.

표준을 사용하기로 결정했으면 호환 가능한 디자인 구현 방법을 결정해야 하는데,

이더리움 생태계에서 널리 사용되는 기존의 ‘참조(reference)’ 구현이 많이 있다.

이 구현 방법을 선택해도 되고, 본인이 직접 처음부터 작성할 수도 있다.

 

오픈제플린 구현의 경우, 처음부터 보안에 초점을 맞췄기 때문에 비교적 안전하다.

코드가 복잡해질수록 보안성이 떨어지게 때문에 코드 확장 시 조심해야 한다.

 

 

 

 

토큰 인터페이스 표준 확장

토큰 표준은 기능이 제한된, 매우 최소한의 인터페이스를 제공한다.

 

많은 프로젝트가 애플리케이션에 필요한 기능을 지원하기 위해 확장된 구현을 만들었고,

그 기능은 아래와 같다.

  • 소유자 제어(owner control)
    특정 주소 또는 주소 집합(예: 멀티시그), 블랙리스트, 허용 목록 작성, 작성, 복구 등과 같은 특수 기능 제공
  • 소각(burning)
    토큰을 고의로 파괴할 수 있는 주소로 전송하거나 잔액을 지우고 공급을 줄임으로써 의도적으로 토큰을 파괴(소각)하는 기능
  • 발행(minting)
    예측 가능한 비율로 또는 토큰 생성자의 ‘승인(fiat)’을 토큰 총 공급량을 추가하는 기능
  • 크라우드펀딩(corwdfunding)
    경매, 시장 판매, 역 경매 등을 통해 판매용 토큰을 제공할 수 있는 기능
  • 캡(caps) ????
    총 공급량에 대해 미리 정의된 불변의 제한을 설정할 수 있는 기능(’발행’ 기능의 반대)
  • 복구 백도어(recovery backdoors)
    자금을 복구하거나, 송금을 되돌리거나, 지정된 주소 또는 주소의 집합에 의해 활성화 될 수 있는 토큰을 소거하는 기능
  • 화이트리스트(whitelisting)
    토큰 전송 같은 작업을 특정 주소로 제한할 수 있는 기능.
    일반적으로 화리를 업데이트하는 메커니즘 존재.
  • 블랙리스트(blacklisting)
    특정 주소를 허용하지 않음으로써 토큰 전송을 제한할 수 있는 기능.
    일반적으로 블랙리스트를 업데이트하는 메커니즘 존재.

 

 

토큰 및 ICO

토큰은 이더리움 같은 스마트 컨트랙트 플랫폼에서 매우 중요한 구성요소가 될 가능성이 크다.

 

우리가 필요한 것은 단기적으로 사기를 치는 토큰 ICO 버블로부터

장기적 비전과 이 기술의 영향을 분리하는 것이다.