ERC Token介紹:ERC20 & ERC721

Posted by Kubeguts on 2018-03-25

即利用當今火紅的以太坊上的智能合約,實作出代幣功能。
可擁有與虛擬貨幣作為價值傳遞功能。

好處

  • 不用自己架設公有鏈,就可以擁有自己發行的代幣
  • 以既定匯率,可以直接與以太幣進行交換。

為何需要ERC標準合約?

由於代幣是建立在以太坊上的智能合約,故其代幣也只是存在智能合約上的某筆紀錄。
故透過一些狀態改變和轉移,故ERC合約可以明確定義某個以太坊地址上的某個代幣餘額,並可以對其進行操作,而操作的規範就定義在ERC上。

故一個代幣要擁有合理的發行機制,就得符合現在所擁有的ERC20以及ERC721之類的標準。

ERC代幣有自己的錢包嗎??

由於是透過以太坊智能合約控制的,故只要透過以太坊錢包(地址)就可以了。

ERC20 介紹

記載ERC20規則 官方文件

https://theethereum.wiki/w/index.php/ERC20_Token_Standard

ERC20 Token Standard Interface

1
2
3
4
5
6
7
8
9
10
interface ERC20 { 
function totalSupply() public constant returns (uint);
function balanceOf(address tokenOwner) public constant returns (uint balance); |
function allowance(address tokenOwner, address spender) public constant returns (uint remaining); |
function transfer(address to, uint tokens) public returns (bool success); |
function approve(address spender, uint tokens) public returns (bool success); |
function transferFrom(address from, address to, uint tokens) public returns (bool success); |
event Transfer(address indexed from, address indexed to, uint tokens); |
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}

以上的Interface定義了六個Function,有Input及Output格式。
以及兩個Event,僅有Input。

其中Function為可以操縱智能合約狀態的動作,會需要消耗Gas,而可以看到有些Function後面有 constant關鍵字,表示僅做唯獨,不會更改到智能合約上的State,主要是回傳的State資訊,不會消耗到Gas。

而Event為紀錄重大事件的發生,例如在鏈上進行Token的轉移。

設定ERC Token資訊

1
2
3
string public constant name = "Token Name"; 
string public constant symbol = "TKN";
uint8 public constant decimals = 18; // 18 is the most common number of decimal places

ERC20 需要設定此 Token 的三個資訊: name、symbol、decimals

  • name 是 Token 的名字。
  • symbol 則是此 Token 會使用的代稱,像是 Binance Token 的 symbol 就是 BNB,而此 symbol 也會出現在 Etherscan上面
  • decimals 是用來設定此 Token 最小會有幾個位數,通常會設定成 18,意即最多到達小數點後 18 位數,這樣的設定跟 Ether 本身的設定也是一樣的(1 ether: 10 ^ 18 wei)。

補充說明:在 Solidity 中並沒有浮點數的存在,所有的運算都是整數,因此平常我們所說的 1 Ether,事實上在 Solidity 程式中是以wei的單位(1 ether = 10¹⁸)來撰寫。

ERC20 詳述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
contract TokenContractFragment {

// Balances for each account
mapping(address => uint256) balances;

// Owner of account approves the transfer of an amount to another account
mapping(address => mapping (address => uint256)) allowed;

// Get the token balance for account \`tokenOwner\`
function balanceOf(address tokenOwner) public constant returns (uint balance) {
return balances\[tokenOwner\];
}

// Transfer the balance from owner's account to another account
function transfer(address to, uint tokens) public returns (bool success) {
balances\[msg.sender\] = balances\[msg.sender\].sub(tokens);
balances\[to\] = balances\[to\].add(tokens);
Transfer(msg.sender, to, tokens);
return true;
}

// Send \`tokens\` amount of tokens from address \`from\` to address \`to\`
// The transferFrom method is used for a withdraw workflow, allowing contracts to send
// tokens on your behalf, for example to "deposit" to a contract address and/or to charge
// fees in sub-currencies; the command should fail unless the _from account has
// deliberately authorized the sender of the message via some mechanism; we propose
// these standardized APIs for approval:
function transferFrom(address from, address to, uint tokens) public returns (bool success) {
balances\[from\] = balances\[from\].sub(tokens);
allowed\[from\]\[msg.sender\] = allowed\[from\]\[msg.sender\].sub(tokens);
balances\[to\] = balances\[to\].add(tokens);
Transfer(from, to, tokens);
return true;
}

// Allow \`spender\` to withdraw from your account, multiple times, up to the \`tokens\` amount.
// If this function is called again it overwrites the current allowance with _value.
function approve(address spender, uint tokens) public returns (bool success) {
allowed\[msg.sender\]\[spender\] = tokens;
Approval(msg.sender, spender, tokens);
return true;
}
}

使用例子

Token Balance

我們假設這個智能合約目前有兩個地址擁有該Token
balances[‘0x123456’] = 100
balances[‘0x654321’] = 200

則 Function balanceOf 則會回傳以下資訊:
tokenContract.balanceOf(‘0x123456’) will return 100
tokenContract.balanceOf(‘0x654321’) will return 200

Transfer Token Balance

如果 0x123456想要轉移 10 tokens 給 0x654321,則呼叫
tokenContract.transfer(‘0x654321’, 10)

將得到下列結果
balances[‘0x123456’] = 90
balances[‘0x654321’] = 210

Approve And TransferFrom Token Balance

如果 0x123456 允許 0x654321 擁有轉移 30 tokens 的權利,則呼叫
tokenContract.approve(‘0x654321’, 30)

將得到下列結果
tokenContract.allowed[‘0x123456’][‘0x654321’] = 30

如果此時’0x654321’ 想要轉移 ‘0x123456’ 的 20 tokens 給自己,則呼叫
’tokenContract.transferFrom(‘0x123456’, ‘0x654321’, 20)

將得到下列結果
tokenContract.balances[‘0x123456’] = 70
tokenContract.balances[‘0x654321’] = 230
tokenContract.allowed[‘0x123456’][‘0x654321’] = 10

ERC721 介紹

ERC-721 是用来定義 Non-fungible token (不可替代的代幣)。
每個代幣是唯一的(unique),具不可分割性,不像ERC20 每個token都相同。

具體應用: 以太貓(cryptokitties)
每個以太貓,都是一個ERC721代幣。

1
2
3
4
5
6
7
8
9
10
11
interface ERC721 {
function supportsInterface(bytes4 _interfaceID) external pure returns (bool);
function ownerOf(uint256 \_deedId) external view returns (address \_owner);
function countOfDeeds() external view returns (uint256 _count);
function countOfDeedsByOwner(address \_owner) external view returns (uint256 \_count);
function deedOfOwnerByIndex(address \_owner, uint256 \_index) external view returns (uint256 _deedId);
event Transfer(address indexed from, address indexed to, uint256 indexed deedId);
event Approval(address indexed owner, address indexed approved, uint256 indexed deedId);
function approve(address \_to, uint256 \_deedId) external payable;
function takeOwnership(uint256 _deedId) external payable;
}

參考資料