Ruka kwenda kwenye maudhui makuu

Uhamisho na uidhinishaji wa tokeni za ERC-20 kutoka kwa mkataba-erevu wa solidity

mikataba erevu
tokeni
uimara
erc-20
Intermediate
jdourlens
7 Aprili 2020
7 minute read

Katika somo lililopita tulijifunza anatomia ya tokeni ya ERC-20 katika Solidity kwenye mnyororo wa bloku wa Ethereum. Katika makala hii tutaona jinsi tunavyoweza kutumia mkataba-erevu kuingiliana na tokeni kwa kutumia lugha ya Solidity.

Kwa ajili ya mkataba-erevu huu, tutaunda mfumo halisi wa majaribio wa kubadilishana uliogatuliwa ambapo mtumiaji anaweza kubadilisha ether kwa tokeni yetu mpya ya ERC-20 iliyotumwa.

Kwa somo hili tutatumia msimbo tuliouandika katika somo lililopita kama msingi. DEX yetu itaanzisha mfano wa mkataba katika kiunda chake na kufanya shughuli za:

  • kubadilishana tokeni kwa ether
  • kubadilishana ether kwa tokeni

Tutaanzisha msimbo wetu wa kubadilishana uliogatuliwa kwa kuongeza msimbo wetu rahisi wa ERC20:

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
Onyesha yote

Mkataba-erevu wetu mpya wa DEX utatumia ERC-20 na kupata yote yaliyotolewa:

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}
Onyesha yote

Kwa hiyo sasa tuna DEX yetu na ina hifadhi yote ya tokeni inayopatikana. Mkataba una vipengele viwili:

  • nunua: Mtumiaji anaweza kutuma ether na kupata tokeni badala yake
  • uza: Mtumiaji anaweza kuamua kutuma tokeni ili kurudishiwa ether

Kipengele cha kununua

Tuandike msimbo wa kipengele cha kununua. Kwanza tutahitaji kuangalia kiasi cha ether kilichomo kwenye ujumbe na kuthibitisha kuwa mikataba inamiliki tokeni za kutosha na kwamba ujumbe una kiasi fulani cha ether ndani yake. Ikiwa mkataba unamiliki tokeni za kutosha, utamtumia mtumiaji idadi ya tokeni na kutoa tukio la Bought.

Kumbuka kwamba tukiita kipengele cha require katika kisa cha kosa ether iliyotumwa itarejeshwa moja kwa moja na kurudishwa kwa mtumiaji.

Ili kurahisisha mambo, tunabadilishana tokeni 1 kwa Wei 1.

1function buy() payable public {
2 uint256 amountTobuy = msg.value;
3 uint256 dexBalance = token.balanceOf(address(this));
4 require(amountTobuy > 0, "Unahitaji kutuma kiasi fulani cha ether");
5 require(amountTobuy <= dexBalance, "Hakuna tokeni za kutosha katika hifadhi");
6 token.transfer(msg.sender, amountTobuy);
7 emit Bought(amountTobuy);
8}

Katika hali ambapo ununuzi unafanikiwa tunapaswa kuona matukio mawili katika muamala: Transfer ya tokeni na tukio la Bought.

Matukio mawili katika muamala: Transfer na Bought

Kipengele cha kuuza

Kipengele kinachohusika na uuzaji kwanza kitahitaji mtumiaji awe ameidhinisha kiasi kwa kuita kipengele cha approve kabla. Kuidhinisha uhamisho kunahitaji tokeni ya ERC20Basic iliyoanzishwa na DEX iitwe na mtumiaji. Hii inaweza kufikiwa kwa kwanza kuita kipengele cha token() cha mkataba wa DEX ili kupata anwani ambapo DEX ilituma mkataba wa ERC20Basic unaoitwa token. Kisha tunaunda mfano wa mkataba huo katika kipindi chetu na kuita kipengele chake cha approve. Kisha tunaweza kuita kipengele cha sell cha DEX na kubadilisha tokeni zetu na kupata ether. Kwa mfano, hivi ndivyo inavyoonekana katika kipindi cha maingiliano cha brownie:

1#### Python katika koni ya maingiliano ya brownie...
2
3# tuma DEX
4dex = DEX.deploy({'from':account1})
5
6# ita kipengele cha kununua ili kubadilisha ether kwa tokeni
7# 1e18 ni ether 1 katika wei
8dex.buy({'from': account2, 1e18})
9
10# pata anwani ya utumaji ya tokeni ya ERC20
11# ambayo ilitumwa wakati wa uundaji wa mkataba wa DEX
12# dex.token() inarudisha anwani iliyotumwa ya tokeni
13token = ERC20Basic.at(dex.token())
14
15# ita kipengele cha kuidhinisha cha tokeni
16# idhinisha anwani ya dex kama mtumiaji
17# na ni tokeni zako ngapi inaruhusiwa kutumia
18token.approve(dex.address, 3e18, {'from':account2})
19
Onyesha yote

Kisha kipengele cha sell kinapoitwa, tutaangalia kama uhamisho kutoka kwa anwani ya mpigaji kwenda kwa anwani ya mkataba ulifanikiwa na kisha tutatuma Ethers kurudi kwa anwani ya mpigaji.

1function sell(uint256 amount) public {
2 require(amount > 0, "Unahitaji kuuza angalau tokeni kadhaa");
3 uint256 allowance = token.allowance(msg.sender, address(this));
4 require(allowance >= amount, "Angalia ruhusa ya tokeni");
5 token.transferFrom(msg.sender, address(this), amount);
6 payable(msg.sender).transfer(amount);
7 emit Sold(amount);
8}

Ikiwa kila kitu kitafanya kazi unapaswa kuona matukio 2 (Transfer na Sold) katika muamala na salio lako la tokeni na salio la ether likisasishwa.

Matukio mawili katika muamala: Transfer na Sold

Kutoka kwa somo hili tumeona jinsi ya kuangalia salio na ruhusa ya tokeni ya ERC-20 na pia jinsi ya kuita Transfer na TransferFrom ya mkataba-erevu wa ERC20 kwa kutumia kiolesura.

Mara tu unapofanya muamala tuna somo la JavaScript la kusubiri na kupata maelezo kuhusu miamala (opens in a new tab) ambayo ilifanywa kwa mkataba wako na somo la kusimbua matukio yaliyotokana na uhamisho wa tokeni au matukio mengine yoyote (opens in a new tab) mradi tu una ABI.

Huu hapa msimbo kamili wa somo:

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, "Unahitaji kutuma kiasi fulani cha ether");
92 require(amountTobuy <= dexBalance, "Hakuna tokeni za kutosha katika hifadhi");
93 token.transfer(msg.sender, amountTobuy);
94 emit Bought(amountTobuy);
95 }
96
97 function sell(uint256 amount) public {
98 require(amount > 0, "Unahitaji kuuza angalau tokeni kadhaa");
99 uint256 allowance = token.allowance(msg.sender, address(this));
100 require(allowance >= amount, "Angalia ruhusa ya tokeni");
101 token.transferFrom(msg.sender, address(this), amount);
102 payable(msg.sender).transfer(amount);
103 emit Sold(amount);
104 }
105
106}
Onyesha yote

Ukurasa ulihaririwa mwisho: 21 Agosti 2025

Umesaidika na mafunzo haya?