تخطٍ إلى المحتوى الرئيسي

تحويلات رموز ERC-20 والموافقة عليها من عقد سوليديتي ذكي

العقود الذكيه
tokens
Solidity
erc-20
المستوى المتوسط
jdourlens
7 أبريل 2020
6 دقيقة قراءة

في الدرس التعليمي السابق درسنا تشريح رمز ERC-20 في سوليديتي على بلوكتشين إيثريوم. في هذه المقالة، سنرى كيف يمكننا استخدام عقد ذكي للتفاعل مع رمز باستخدام لغة سوليديتي.

لهذا العقد الذكي، سننشئ منصة تداول لامركزية وهمية حقيقية حيث يمكن للمستخدم تداول الإيثر مقابل رمز ERC-20 الذي نشرناه حديثًا.

في هذا الدرس التعليمي، سنستخدم النص البرمجي الذي كتبناه في الدرس التعليمي السابق كأساس. ستقوم منصة التداول اللامركزية الخاصة بنا بإنشاء مثيل للعقد في الدالة الإنشائية الخاصة به وتنفيذ العمليات التالية:

  • استبدال الرموز بالإيثر
  • استبدال الإيثر بالرموز

سنبدأ النص البرمجي لمنصة التداول اللامركزية الخاصة بنا بإضافة قاعدة التعليمات البرمجية البسيطة الخاصة بـ 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 event Transfer(address indexed from, address indexed to, uint256 value);
14 event Approval(address indexed owner, address indexed spender, uint256 value);
15}
16
17contract ERC20Basic is IERC20 {
18
19 string public constant name = "ERC20Basic";
20 string public constant symbol = "ERC";
21 uint8 public constant decimals = 18;
22
23 mapping(address => uint256) balances;
24
25 mapping(address => mapping (address => uint256)) allowed;
26
27 uint256 totalSupply_ = 10 ether;
28
29 constructor() {
30 balances[msg.sender] = totalSupply_;
31 }
32
33 function totalSupply() public override view returns (uint256) {
34 return totalSupply_;
35 }
36
37 function balanceOf(address tokenOwner) public override view returns (uint256) {
38 return balances[tokenOwner];
39 }
40
41 function transfer(address receiver, uint256 numTokens) public override returns (bool) {
42 require(numTokens <= balances[msg.sender]);
43 balances[msg.sender] = balances[msg.sender]-numTokens;
44 balances[receiver] = balances[receiver]+numTokens;
45 emit Transfer(msg.sender, receiver, numTokens);
46 return true;
47 }
48
49 function approve(address delegate, uint256 numTokens) public override returns (bool) {
50 allowed[msg.sender][delegate] = numTokens;
51 emit Approval(msg.sender, delegate, numTokens);
52 return true;
53 }
54
55 function allowance(address owner, address delegate) public override view returns (uint) {
56 return allowed[owner][delegate];
57 }
58
59 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {
60 require(numTokens <= balances[owner]);
61 require(numTokens <= allowed[owner][msg.sender]);
62
63 balances[owner] = balances[owner]-numTokens;
64 allowed[owner][msg.sender] = allowed[owner][msg.sender]-numTokens;
65 balances[buyer] = balances[buyer]+numTokens;
66 emit Transfer(owner, buyer, numTokens);
67 return true;
68 }
69}
70
إظهار الكل

سيقوم عقدنا الذكي الجديد الخاص بمنصة التداول اللامركزية بنشر 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 // سيتم التنفيذ لاحقاً
14 }
15
16 function sell(uint256 amount) public {
17 // سيتم التنفيذ لاحقاً
18 }
19
20}
إظهار الكل

لذا، لدينا الآن منصة التداول اللامركزية الخاصة بنا وكل احتياطي الرموز متاح لديها. يحتوي العقد على وظيفتين:

  • الشراء: يمكن للمستخدم إرسال الإيثر والحصول على رموز في المقابل
  • البيع: يمكن للمستخدم أن يقرر إرسال الرموز لاستعادة الإيثر

وظيفة الشراء

لنقم بكتابة النص البرمجي لوظيفة الشراء. سنحتاج أولاً إلى التحقق من كمية الإيثر التي تحتويها الرسالة والتحقق من أن العقد يمتلك رموزًا كافية وأن الرسالة تحتوي على بعض الإيثر. إذا كان العقد يمتلك رموزًا كافية، فسيرسل عدد الرموز إلى المستخدم ويصدر حدث Bought.

لاحظ أنه إذا استدعينا وظيفة require في حالة حدوث خطأ، فسيتم إرجاع الإيثر المرسل مباشرة وإعادته إلى المستخدم.

لتبسيط الأمور، سنقوم بتبديل رمز واحد مقابل 1 واي.

1function buy() payable public {
2 uint256 amountTobuy = msg.value;
3 uint256 dexBalance = token.balanceOf(address(this));
4 require(amountTobuy > 0, "تحتاج إلى إرسال بعض الإيثر");
5 require(amountTobuy <= dexBalance, "لا توجد رموز كافية في الاحتياطي");
6 token.transfer(msg.sender, amountTobuy);
7 emit Bought(amountTobuy);
8}

في حالة نجاح عملية الشراء، يجب أن نرى حدثين في المعاملة: حدث Transfer للرمز وحدث Bought.

حدثان في المعاملة: Transfer و Bought

وظيفة البيع

الوظيفة المسؤولة عن البيع ستتطلب أولاً من المستخدم أن يكون قد وافق على المبلغ عن طريق استدعاء وظيفة approve مسبقاً. تتطلب الموافقة على التحويل أن يتم استدعاء رمز ERC20Basic الذي تم إنشاؤه بواسطة منصة التداول اللامركزية من قِبل المستخدم. يمكن تحقيق ذلك عن طريق استدعاء وظيفة token() الخاصة بعقد منصة التداول اللامركزية أولاً لاسترداد العنوان الذي نشرت فيه منصة التداول اللامركزية عقد ERC20Basic المسمى token. ثم نقوم بإنشاء مثيل لهذا العقد في جلستنا ونستدعي وظيفة approve الخاصة به. بعد ذلك، يمكننا استدعاء وظيفة sell الخاصة بمنصة التداول اللامركزية واستبدال رموزنا مرة أخرى بالإيثر. على سبيل المثال، هكذا يبدو هذا في جلسة Brownie تفاعلية:

1#### بايثون في وحدة تحكم Brownie التفاعلية...
2
3# نشر منصة التداول اللامركزية (DEX)
4dex = DEX.deploy({'from':account1})
5
6# استدعاء وظيفة الشراء لمبادلة الإيثر بالرمز
7# 1e18 هو 1 إيثر مُقوّم بالـ wei
8dex.buy({'from': account2, 1e18})
9
10# الحصول على عنوان النشر لرمز ERC20
11# الذي تم نشره أثناء إنشاء عقد DEX
12# dex.token() تُرجع عنوان النشر للرمز
13token = ERC20Basic.at(dex.token())
14
15# استدعاء وظيفة الموافقة الخاصة بالرمز
16# الموافقة على عنوان DEX كمنفق
17# وعدد الرموز التي يُسمح له بإنفاقها
18token.approve(dex.address, 3e18, {'from':account2})
19
إظهار الكل

بعد ذلك، عند استدعاء وظيفة البيع، سنتحقق مما إذا كان التحويل من عنوان المتصل إلى عنوان العقد قد تم بنجاح، ثم نعيد إرسال الإيثر إلى عنوان المتصل.

1function sell(uint256 amount) public {
2 require(amount > 0, "تحتاج إلى بيع بعض الرموز على الأقل");
3 uint256 allowance = token.allowance(msg.sender, address(this));
4 require(allowance >= amount, "تحقق من بدل الرمز");
5 token.transferFrom(msg.sender, address(this), amount);
6 payable(msg.sender).transfer(amount);
7 emit Sold(amount);
8}

إذا سار كل شيء على ما يرام، يجب أن ترى حدثين (Transfer و Sold) في المعاملة وتحديث رصيد الرمز ورصيد الإيثر لديك.

حدثان في المعاملة: Transfer و Sold

من هذا الدرس التعليمي، رأينا كيفية التحقق من الرصيد والبدل لرمز ERC-20، وكذلك كيفية استدعاء Transfer وTransferFrom لعقد ERC20 ذكي باستخدام الواجهة.

بمجرد إجراء معاملة، لدينا درس تعليمي لجافا سكريبت للانتظار والحصول على تفاصيل حول المعاملات (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 event Transfer(address indexed from, address indexed to, uint256 value);
14 event Approval(address indexed owner, address indexed spender, uint256 value);
15}
16
17contract ERC20Basic is IERC20 {
18
19 string public constant name = "ERC20Basic";
20 string public constant symbol = "ERC";
21 uint8 public constant decimals = 18;
22
23 mapping(address => uint256) balances;
24
25 mapping(address => mapping (address => uint256)) allowed;
26
27 uint256 totalSupply_ = 10 ether;
28
29 constructor() {
30 balances[msg.sender] = totalSupply_;
31 }
32
33 function totalSupply() public override view returns (uint256) {
34 return totalSupply_;
35 }
36
37 function balanceOf(address tokenOwner) public override view returns (uint256) {
38 return balances[tokenOwner];
39 }
40
41 function transfer(address receiver, uint256 numTokens) public override returns (bool) {
42 require(numTokens <= balances[msg.sender]);
43 balances[msg.sender] = balances[msg.sender]-numTokens;
44 balances[receiver] = balances[receiver]+numTokens;
45 emit Transfer(msg.sender, receiver, numTokens);
46 return true;
47 }
48
49 function approve(address delegate, uint256 numTokens) public override returns (bool) {
50 allowed[msg.sender][delegate] = numTokens;
51 emit Approval(msg.sender, delegate, numTokens);
52 return true;
53 }
54
55 function allowance(address owner, address delegate) public override view returns (uint) {
56 return allowed[owner][delegate];
57 }
58
59 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {
60 require(numTokens <= balances[owner]);
61 require(numTokens <= allowed[owner][msg.sender]);
62
63 balances[owner] = balances[owner]-numTokens;
64 allowed[owner][msg.sender] = allowed[owner][msg.sender]-numTokens;
65 balances[buyer] = balances[buyer]+numTokens;
66 emit Transfer(owner, buyer, numTokens);
67 return true;
68 }
69}
70
71contract DEX {
72
73 event Bought(uint256 amount);
74 event Sold(uint256 amount);
75
76 IERC20 public token;
77
78 constructor() {
79 token = new ERC20Basic();
80 }
81
82 function buy() payable public {
83 uint256 amountTobuy = msg.value;
84 uint256 dexBalance = token.balanceOf(address(this));
85 require(amountTobuy > 0, "تحتاج إلى إرسال بعض الإيثر");
86 require(amountTobuy <= dexBalance, "لا توجد رموز كافية في الاحتياطي");
87 token.transfer(msg.sender, amountTobuy);
88 emit Bought(amountTobuy);
89 }
90
91 function sell(uint256 amount) public {
92 require(amount > 0, "تحتاج إلى بيع بعض الرموز على الأقل");
93 uint256 allowance = token.allowance(msg.sender, address(this));
94 require(allowance >= amount, "تحقق من بدل الرمز");
95 token.transferFrom(msg.sender, address(this), amount);
96 payable(msg.sender).transfer(amount);
97 emit Sold(amount);
98 }
99
100}
إظهار الكل

آخر تحديث للصفحة: 21 أغسطس 2025

هل كانت تعليمات الاستخدام هذه مفيدة؟