Lanjut ke konten utama

Transfer dan persetujuan token ERC-20 dari kontrak pintar solidity

kontrak pintartokensoliditymemulaierc-20
Tingkat menengah
jdourlens
EthereumDev(opens in a new tab)
7 April 2020
6 bacaan singkat minute read
comp-tutorial-metadata-tip-author 0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE

Dalam tutorial sebelumnya, kita mempelajari anatomi token ERC-20 dalam Solidity di blockchain Ethereum. Dalam artikel ini, kita akan melihat cara menggunakan kontrak pintar untuk berinteraksi dengan token menggunakan bahasa Solidity.

Untuk kontrak pintar ini, kita akan membuat bursa terdesentralisasi yang sangat mirip di mana pengguna bisa memperdagangkan Ethereum dengan token ERC-20 kami yang baru digunakan.

Untuk tutorial ini, kami akan menggunakan kode yang kami tulis dalam tutorial sebelumnya sebagai dasarnya. DEX kami akan membuat instance kontrak dalam pembangunnya dan melakukan operasi:

  • penukaran token ke ether
  • penukaran ether ke token

Kita akan memulai kode bursa 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
Salin

Kontrak pintar DEX kita akan menggunakan 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
14 }
15
16 function sell(uint256 amount) public {
17 // TODO
18 }
19
20}
Tampilkan semua
Salin

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

  • buy: Penggunan bisa mengirim ether dan mendapat token sebagai gantinya
  • sell: Pengguna bisa memutuskan mengirim token untuk mendapatkan ether kembali

Fungsi pembelian

Mari kodekan fungsi pembelian. Terlebih dahulu kita akan memeriksa jumlah ether yang dimiliki message dan memverifikasi apakah kontrak memiliki cukup token dan apakah messagenya memliki beberapa ether di dalamnya. Jika kontrak memiliki cukup token, kontrak akan mengirim sejumlah token ke pengguna dan memancarkan aksi Bought.

Perhatikan bahwa jika kita memanggil fungsi yang diperlukan saat terjadi kesalahan, ether yang dikirimkan akan secara langsung dibalikkan dan diberikan kembali ke pengguna.

To keep things simple, we just exchange 1 token for 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}
Salin

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

Dua aksi dalam transaksi: Transfer dan Bought

Fungsi penjualan

Fungsi yang bertanggungjawab untuk penjualan ini pertama-tama akan mengharuskan pengguna menyetujui jumlahnya dengan memanggil fungsi yang disetujui sebelumnya. Approving the transfer requires the ERC20Basic token instantiated by the DEX to be called by the user. This can be achieved by first calling the DEX contract's token() function to retrieve the address where DEX deployed the ERC20Basic contract called token. Then we create an instance of that contract in our session and call its approve function. Then we are able to call the DEX's sell function and swap our tokens back for ether. For example, this is how this looks in an interactive brownie session:

1#### Python in interactive brownie console...
2
3# deploy the DEX
4dex = DEX.deploy({'from':account1})
5
6# call the buy function to swap ether for token
7# 1e18 is 1 ether denominated in wei
8dex.buy({'from': account2, 1e18})
9
10# get the deployment address for the ERC20 token
11# that was deployed during DEX contract creation
12# dex.token() returns the deployed address for token
13token = ERC20Basic.at(dex.token())
14
15# call the token's approve function
16# approve the dex address as spender
17# and how many of your tokens it is allowed to spend
18token.approve(dex.address, 3e18, {'from':account2})
19
Tampilkan semua
Salin

Lalu saat fungsi penjualan dipanggil, kita akan memeriksa apakah transfer dari alamat pemanggil ke alamat kontrak berhasil dan kemudian mengirim 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}
Salin

Jika semuanya berfungsi dengan baik, Anda akan melihat 2 aksi (Transfer dan Sold) dalam transaksi serta saldo token dan Ethereum Anda diperbarui.

Dua aksi dalam transaksi: Transfer dan Sold

Dari tutorial ini, kita melihat cara memeriksa saldo dan allowance token ERC-20 dan juga cara memanggil Transfer dan TransferFrom dari kontrak pintar ERC20 yang menggunakan antarmukanya.

Once you make a transaction we have a JavaScript tutorial to wait and get details about the transactions(opens in a new tab) that were made to your contract and a tutorial to decode events generated by token transfers or any other events(opens in a new tab) as long as you have the 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
Salin

Terakhir diedit: @yeremiaryangunadi(opens in a new tab), 15 Agustus 2023

Apakah tutorial ini membantu?