Lompat ke konten utama

Transfer dan persetujuan token ERC-20 dari kontrak pintar solidity

kontrak pintar
token
Solidity
erc-20
Menengah
jdourlens
7 April 2020
7 menit baca

Pada tutorial sebelumnya kita telah mempelajari anatomi token ERC-20 di Solidity pada blockchain Ethereum. Dalam artikel ini kita akan melihat bagaimana kita dapat menggunakan kontrak pintar untuk berinteraksi dengan token menggunakan bahasa Solidity.

Untuk kontrak pintar ini, kita akan membuat pertukaran terdesentralisasi tiruan yang nyata di mana pengguna dapat menukar ether dengan token ERC-20 kita yang baru saja disebarkan.

Untuk tutorial ini kita akan menggunakan kode yang kita tulis pada tutorial sebelumnya sebagai dasar. DEX kita akan membuat instansiasi kontrak di konstruktornya dan melakukan operasi:

  • menukar token menjadi ether
  • menukar ether menjadi token

Kita akan memulai kode pertukaran terdesentralisasi kita dengan menambahkan basis kode ERC20 sederhana kita:

1pragma solidity ^0.8.0;
2
3interface IERC20 {
4
5 function totalSupply() external view returns (uint256);
6 function balanceOf(address account) external view returns (uint256);
7 function allowance(address owner, address spender) external view returns (uint256);
8
9 function transfer(address recipient, uint256 amount) external returns (bool);
10 function approve(address spender, uint256 amount) external returns (bool);
11 function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
12
13
14 event Transfer(address indexed from, address indexed to, uint256 value);
15 event Approval(address indexed owner, address indexed spender, uint256 value);
16}
17
18
19contract ERC20Basic is IERC20 {
20
21 string public constant name = "ERC20Basic";
22 string public constant symbol = "ERC";
23 uint8 public constant decimals = 18;
24
25
26 mapping(address => uint256) balances;
27
28 mapping(address => mapping (address => uint256)) allowed;
29
30 uint256 totalSupply_ = 10 ether;
31
32
33 constructor() {
34 balances[msg.sender] = totalSupply_;
35 }
36
37 function totalSupply() public override view returns (uint256) {
38 return totalSupply_;
39 }
40
41 function balanceOf(address tokenOwner) public override view returns (uint256) {
42 return balances[tokenOwner];
43 }
44
45 function transfer(address receiver, uint256 numTokens) public override returns (bool) {
46 require(numTokens <= balances[msg.sender]);
47 balances[msg.sender] = balances[msg.sender]-numTokens;
48 balances[receiver] = balances[receiver]+numTokens;
49 emit Transfer(msg.sender, receiver, numTokens);
50 return true;
51 }
52
53 function approve(address delegate, uint256 numTokens) public override returns (bool) {
54 allowed[msg.sender][delegate] = numTokens;
55 emit Approval(msg.sender, delegate, numTokens);
56 return true;
57 }
58
59 function allowance(address owner, address delegate) public override view returns (uint) {
60 return allowed[owner][delegate];
61 }
62
63 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {
64 require(numTokens <= balances[owner]);
65 require(numTokens <= allowed[owner][msg.sender]);
66
67 balances[owner] = balances[owner]-numTokens;
68 allowed[owner][msg.sender] = allowed[owner][msg.sender]-numTokens;
69 balances[buyer] = balances[buyer]+numTokens;
70 emit Transfer(owner, buyer, numTokens);
71 return true;
72 }
73}
74
75
Tampilkan semua

Kontrak pintar DEX baru kita akan menyebarkan ERC-20 dan mendapatkan semua pasokan:

1contract DEX {
2
3 IERC20 public token;
4
5 event Bought(uint256 amount);
6 event Sold(uint256 amount);
7
8 constructor() {
9 token = new ERC20Basic();
10 }
11
12 function buy() payable public {
13 // TODO // TODO
14 }
15
16 function sell(uint256 amount) public {
17 // TODO // TODO
18 }
19
20}
Tampilkan semua

Jadi sekarang kita memiliki DEX kita dan ia memiliki semua cadangan token yang tersedia. Kontrak tersebut memiliki dua fungsi:

  • buy: Pengguna dapat mengirim ether dan mendapatkan token sebagai gantinya
  • sell: Pengguna dapat memutuskan untuk mengirim token untuk mendapatkan ether kembali

Fungsi buy

Mari kita kodekan fungsi buy. Pertama-tama kita perlu memeriksa jumlah ether yang terkandung dalam pesan dan memverifikasi bahwa kontrak memiliki cukup token dan bahwa pesan tersebut memiliki sejumlah ether di dalamnya. Jika kontrak memiliki cukup token, ia akan mengirimkan sejumlah token kepada pengguna dan memancarkan event Bought.

Perhatikan bahwa jika kita memanggil fungsi require jika terjadi kesalahan, ether yang dikirim akan langsung dibatalkan (revert) dan diberikan kembali kepada pengguna.

Agar tetap sederhana, kita hanya menukar 1 token dengan 1 Wei.

1function buy() payable public {
2 uint256 amountTobuy = msg.value;
3 uint256 dexBalance = token.balanceOf(address(this));
4 require(amountTobuy > 0, "You need to send some ether");
5 require(amountTobuy <= dexBalance, "Not enough tokens in the reserve");
6 token.transfer(msg.sender, amountTobuy);
7 emit Bought(amountTobuy);
8}

Jika pembelian berhasil, kita akan melihat dua event dalam transaksi: Transfer token dan event Bought.

Dua event dalam transaksi: Transfer dan Bought

Fungsi sell

Fungsi yang bertanggung jawab untuk penjualan pertama-tama akan mengharuskan pengguna untuk menyetujui jumlah tersebut dengan memanggil fungsi approve sebelumnya. Menyetujui transfer mengharuskan token ERC20Basic yang diinstansiasi oleh DEX dipanggil oleh pengguna. Hal ini dapat dicapai dengan terlebih dahulu memanggil fungsi token() dari kontrak DEX untuk mengambil alamat di mana DEX menyebarkan kontrak ERC20Basic yang disebut token. Kemudian kita membuat instansiasi dari kontrak tersebut di sesi kita dan memanggil fungsi approve-nya. Kemudian kita dapat memanggil fungsi sell dari DEX dan menukar token kita kembali menjadi ether. Sebagai contoh, seperti inilah tampilannya dalam sesi brownie interaktif:

1#### Python in interactive brownie console... # ### Python di konsol interaktif brownie...
2
3# deploy the DEX # deploy DEX
4dex = DEX.deploy({'from':account1})
5
6# call the buy function to swap ether for token # panggil fungsi buy untuk menukar ether dengan token
7# 1e18 is 1 ether denominated in wei # 1e18 adalah 1 ether dalam denominasi wei
8dex.buy({'from': account2, 1e18})
9
10# get the deployment address for the ERC20 token # dapatkan alamat deployment untuk token ERC20
11# that was deployed during DEX contract creation # yang di-deploy selama pembuatan kontrak DEX
12# dex.token() returns the deployed address for token # dex.token() mengembalikan alamat yang di-deploy untuk token
13token = ERC20Basic.at(dex.token())
14
15# call the token's approve function # panggil fungsi approve dari token
16# approve the dex address as spender # setujui alamat dex sebagai spender
17# and how many of your tokens it is allowed to spend # dan berapa banyak token Anda yang diizinkan untuk dibelanjakan
18token.approve(dex.address, 3e18, {'from':account2})
19
Tampilkan semua

Kemudian ketika fungsi sell dipanggil, kita akan memeriksa apakah transfer dari alamat pemanggil ke alamat kontrak berhasil dan kemudian mengirimkan Ether kembali ke alamat pemanggil.

1function sell(uint256 amount) public {
2 require(amount > 0, "You need to sell at least some tokens");
3 uint256 allowance = token.allowance(msg.sender, address(this));
4 require(allowance >= amount, "Check the token allowance");
5 token.transferFrom(msg.sender, address(this), amount);
6 payable(msg.sender).transfer(amount);
7 emit Sold(amount);
8}

Jika semuanya berfungsi, Anda akan melihat 2 event (Transfer dan Sold) dalam transaksi dan saldo token serta saldo ether Anda diperbarui.

Dua event dalam transaksi: Transfer dan Sold

Dari tutorial ini kita melihat cara memeriksa saldo dan izin (allowance) dari token ERC-20 dan juga cara memanggil Transfer dan TransferFrom dari kontrak pintar ERC20 menggunakan antarmuka.

Setelah Anda melakukan transaksi, kami memiliki tutorial JavaScript untuk menunggu dan mendapatkan detail tentang transaksi (opens in a new tab) yang dilakukan ke kontrak Anda dan tutorial untuk memecahkan kode event yang dihasilkan oleh transfer token atau event lainnya (opens in a new tab) selama Anda memiliki ABI.

Berikut adalah kode lengkap untuk tutorial ini:

1pragma solidity ^0.8.0;
2
3interface IERC20 {
4
5 function totalSupply() external view returns (uint256);
6 function balanceOf(address account) external view returns (uint256);
7 function allowance(address owner, address spender) external view returns (uint256);
8
9 function transfer(address recipient, uint256 amount) external returns (bool);
10 function approve(address spender, uint256 amount) external returns (bool);
11 function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
12
13
14 event Transfer(address indexed from, address indexed to, uint256 value);
15 event Approval(address indexed owner, address indexed spender, uint256 value);
16}
17
18
19contract ERC20Basic is IERC20 {
20
21 string public constant name = "ERC20Basic";
22 string public constant symbol = "ERC";
23 uint8 public constant decimals = 18;
24
25
26 mapping(address => uint256) balances;
27
28 mapping(address => mapping (address => uint256)) allowed;
29
30 uint256 totalSupply_ = 10 ether;
31
32
33 constructor() {
34 balances[msg.sender] = totalSupply_;
35 }
36
37 function totalSupply() public override view returns (uint256) {
38 return totalSupply_;
39 }
40
41 function balanceOf(address tokenOwner) public override view returns (uint256) {
42 return balances[tokenOwner];
43 }
44
45 function transfer(address receiver, uint256 numTokens) public override returns (bool) {
46 require(numTokens <= balances[msg.sender]);
47 balances[msg.sender] = balances[msg.sender]-numTokens;
48 balances[receiver] = balances[receiver]+numTokens;
49 emit Transfer(msg.sender, receiver, numTokens);
50 return true;
51 }
52
53 function approve(address delegate, uint256 numTokens) public override returns (bool) {
54 allowed[msg.sender][delegate] = numTokens;
55 emit Approval(msg.sender, delegate, numTokens);
56 return true;
57 }
58
59 function allowance(address owner, address delegate) public override view returns (uint) {
60 return allowed[owner][delegate];
61 }
62
63 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {
64 require(numTokens <= balances[owner]);
65 require(numTokens <= allowed[owner][msg.sender]);
66
67 balances[owner] = balances[owner]-numTokens;
68 allowed[owner][msg.sender] = allowed[owner][msg.sender]-numTokens;
69 balances[buyer] = balances[buyer]+numTokens;
70 emit Transfer(owner, buyer, numTokens);
71 return true;
72 }
73}
74
75
76contract DEX {
77
78 event Bought(uint256 amount);
79 event Sold(uint256 amount);
80
81
82 IERC20 public token;
83
84 constructor() {
85 token = new ERC20Basic();
86 }
87
88 function buy() payable public {
89 uint256 amountTobuy = msg.value;
90 uint256 dexBalance = token.balanceOf(address(this));
91 require(amountTobuy > 0, "You need to send some ether");
92 require(amountTobuy <= dexBalance, "Not enough tokens in the reserve");
93 token.transfer(msg.sender, amountTobuy);
94 emit Bought(amountTobuy);
95 }
96
97 function sell(uint256 amount) public {
98 require(amount > 0, "You need to sell at least some tokens");
99 uint256 allowance = token.allowance(msg.sender, address(this));
100 require(allowance >= amount, "Check the token allowance");
101 token.transferFrom(msg.sender, address(this), amount);
102 payable(msg.sender).transfer(amount);
103 emit Sold(amount);
104 }
105
106}
Tampilkan semua

Pembaruan terakhir halaman: 3 Maret 2026

Apakah tutorial ini membantu?