مرکزی مواد پر جائیں

ایک Solidity اسمارٹ کانٹریکٹ سے ERC-20 ٹوکنز کی منتقلی اور منظوری

اسمارٹ کانٹریکٹس
ٹوکنز
Solidity
erc-20
درمیانی
jdourlens
۷ اپریل، ۲۰۲۰
8 منٹ کی پڑھائی

پچھلے ٹیوٹوریل میں ہم نے Ethereum بلاک چین پر Solidity میں ERC-20 ٹوکن کی ساخت کا مطالعہ کیا تھا۔ اس مضمون میں ہم دیکھیں گے کہ ہم Solidity زبان کا استعمال کرتے ہوئے کسی ٹوکن کے ساتھ تعامل کرنے کے لیے اسمارٹ کانٹریکٹ کا استعمال کیسے کر سکتے ہیں۔

اس اسمارٹ کانٹریکٹ کے لیے، ہم ایک حقیقی ڈمی ڈی سینٹرلائزڈ ایکسچینج (DEX) بنائیں گے جہاں ایک صارف ہمارے نئے ڈیپلائے کیے گئے ERC-20 ٹوکن کے بدلے ether کی تجارت کر سکتا ہے۔

اس ٹیوٹوریل کے لیے ہم اس کوڈ کو بطور بنیاد استعمال کریں گے جو ہم نے پچھلے ٹیوٹوریل میں لکھا تھا۔ ہمارا DEX اپنے کنسٹرکٹر میں کانٹریکٹ کی ایک مثال (instance) بنائے گا اور درج ذیل کام انجام دے گا:

  • ٹوکنز کو ether میں تبدیل کرنا
  • ether کو ٹوکنز میں تبدیل کرنا

ہم اپنے سادہ 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

ہمارا نیا DEX اسمارٹ کانٹریکٹ ERC-20 کو ڈیپلائے کرے گا اور فراہم کردہ تمام ٹوکنز حاصل کرے گا:

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}

تو اب ہمارے پاس اپنا DEX ہے اور اس کے پاس تمام ٹوکن ریزرو دستیاب ہیں۔ کانٹریکٹ کے دو فنکشنز ہیں:

  • buy: صارف ether بھیج سکتا ہے اور بدلے میں ٹوکن حاصل کر سکتا ہے
  • sell: صارف ether واپس حاصل کرنے کے لیے ٹوکن بھیجنے کا فیصلہ کر سکتا ہے

buy فنکشن

آئیے buy فنکشن کا کوڈ لکھتے ہیں۔ ہمیں سب سے پہلے پیغام میں موجود ether کی مقدار کو چیک کرنے کی ضرورت ہوگی اور اس بات کی تصدیق کرنی ہوگی کہ کانٹریکٹ کے پاس کافی ٹوکنز ہیں اور پیغام میں کچھ ether موجود ہے۔ اگر کانٹریکٹ کے پاس کافی ٹوکنز ہیں تو یہ صارف کو ٹوکنز کی تعداد بھیجے گا اور Bought ایونٹ کو خارج (emit) کرے گا۔

نوٹ کریں کہ اگر ہم کسی خرابی کی صورت میں require فنکشن کو کال کرتے ہیں تو بھیجا گیا ether براہ راست واپس (revert) ہو جائے گا اور صارف کو لوٹا دیا جائے گا۔

چیزوں کو سادہ رکھنے کے لیے، ہم صرف 1 ٹوکن کا تبادلہ 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}

اس صورت میں جہاں خریداری کامیاب ہو جاتی ہے، ہمیں ٹرانزیکشن میں دو ایونٹس دیکھنے چاہئیں: ٹوکن Transfer اور Bought ایونٹ۔

ٹرانزیکشن میں دو ایونٹس: Transfer اور Bought

sell فنکشن

فروخت (sell) کے لیے ذمہ دار فنکشن سب سے پہلے صارف سے یہ تقاضا کرے گا کہ وہ پہلے سے approve فنکشن کو کال کر کے رقم کی منظوری دے۔ منتقلی کی منظوری کے لیے ضروری ہے کہ DEX کے ذریعے بنائے گئے ERC20Basic ٹوکن کو صارف کال کرے۔ یہ سب سے پہلے DEX کانٹریکٹ کے token() فنکشن کو کال کر کے حاصل کیا جا سکتا ہے تاکہ وہ ایڈریس بازیافت کیا جا سکے جہاں DEX نے token نامی ERC20Basic کانٹریکٹ کو ڈیپلائے کیا تھا۔ پھر ہم اپنے سیشن میں اس کانٹریکٹ کی ایک مثال (instance) بناتے ہیں اور اس کے approve فنکشن کو کال کرتے ہیں۔ اس کے بعد ہم DEX کے sell فنکشن کو کال کرنے اور اپنے ٹوکنز کو واپس ether کے لیے تبدیل (swap) کرنے کے قابل ہو جاتے ہیں۔ مثال کے طور پر، ایک انٹرایکٹو brownie سیشن میں یہ کچھ اس طرح لگتا ہے:

1# ### انٹرایکٹو براؤنی کنسول میں پائتھون...
2
3# DEX کو ڈیپلائے کریں
4dex = DEX.deploy({'from':account1})
5
6# ایتھر کو ٹوکن سے تبدیل کرنے کے لیے buy فنکشن کو کال کریں
7# 1e18 کا مطلب 1 ایتھر ہے جسے wei میں ظاہر کیا گیا ہے
8dex.buy({'from': account2, 1e18})
9
10# ERC20 ٹوکن کا ڈیپلائمنٹ ایڈریس حاصل کریں
11# جو DEX کنٹریکٹ بنانے کے دوران ڈیپلائے کیا گیا تھا
12# dex.token() ٹوکن کا ڈیپلائے شدہ ایڈریس واپس کرتا ہے
13token = ERC20Basic.at(dex.token())
14
15# ٹوکن کے approve فنکشن کو کال کریں
16# dex ایڈریس کو خرچ کرنے والے (spender) کے طور پر منظور کریں
17# اور اسے آپ کے کتنے ٹوکن خرچ کرنے کی اجازت ہے
18token.approve(dex.address, 3e18, {'from':account2})
19

پھر جب sell فنکشن کو کال کیا جائے گا، تو ہم چیک کریں گے کہ آیا کالر کے ایڈریس سے کانٹریکٹ کے ایڈریس پر منتقلی کامیاب رہی اور پھر Ethers کو واپس کالر کے ایڈریس پر بھیج دیں گے۔

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}

اگر سب کچھ ٹھیک کام کرتا ہے تو آپ کو ٹرانزیکشن میں 2 ایونٹس (ایک Transfer اور Sold) نظر آنے چاہئیں اور آپ کا ٹوکن بیلنس اور ether بیلنس اپ ڈیٹ ہو جانا چاہیے۔

ٹرانزیکشن میں دو ایونٹس: Transfer اور Sold

اس ٹیوٹوریل سے ہم نے دیکھا کہ ERC-20 ٹوکن کا بیلنس اور الاؤنس کیسے چیک کیا جاتا ہے اور انٹرفیس کا استعمال کرتے ہوئے ERC20 اسمارٹ کانٹریکٹ کے Transfer اور TransferFrom کو کیسے کال کیا جاتا ہے۔

ایک بار جب آپ ٹرانزیکشن کر لیتے ہیں تو ہمارے پاس ایک JavaScript ٹیوٹوریل ہے تاکہ آپ کے کانٹریکٹ پر کی گئی ٹرانزیکشنز کا انتظار کیا جا سکے اور ان کے بارے میں تفصیلات حاصل کی جا سکیں (opens in a new tab) اور ایک ٹیوٹوریل ہے تاکہ ٹوکن کی منتقلی یا کسی دوسرے ایونٹس کے ذریعے پیدا ہونے والے ایونٹس کو ڈی کوڈ کیا جا سکے (opens in a new tab) بشرطیکہ آپ کے پاس ABI موجود ہو۔

ٹیوٹوریل کا مکمل کوڈ یہ ہے:

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}

صفحہ کی آخری اپ ڈیٹ: ۳ مارچ، ۲۰۲۶

کیا یہ ٹیوٹوریل مددگار تھا؟