Ruka kwenda kwenye maudhui makuu

Mkataba wa ERC-20: Maelezo ya Kina

Solidity
erc-20
Mwanzo
Ori Pomerantz
9 Machi 2021
25 soma ndani ya dakika

Utangulizi

Moja ya matumizi yanayojulikana sana ya Ethereum ni pale kikundi cha watu kinapotengeneza tokeni inayoweza kuuzwa, kwa maana nyingine, sarafu yao wenyewe. Tokeni hizi kwa kawaida hufuata kiwango, ERC-20. Kiwango hiki hufanya iwezekanavyo kuandika zana, kama vile vidimbwi vya ukwasi na mikoba, ambavyo hufanya kazi na tokeni zote za ERC-20. Katika makala haya tutachambua utekelezaji wa OpenZeppelin Solidity ERC20 (opens in a new tab), pamoja na ufafanuzi wa kiolesura (opens in a new tab).

Hii ni msimbo chanzo uliotiwa maelezo. Ikiwa unataka kutekeleza ERC-20, soma mafunzo haya (opens in a new tab).

Kiolesura

Madhumuni ya kiwango kama ERC-20 ni kuruhusu utekelezaji wa tokeni nyingi ambazo zinaweza kufanya kazi katika mifumo mbalimbali, kama vile mikoba na exchange zilizogatuliwa. Ili kufanikisha hilo, tunatengeneza kiolesura (opens in a new tab). Msimbo wowote unaohitaji kutumia mkataba wa tokeni unaweza kutumia ufafanuzi uleule katika kiolesura na uendane na mikataba yote ya tokeni inayoutumia, iwe ni mkoba kama MetaMask, mfumo uliotawanywa kama etherscan.io, au mkataba tofauti kama vile kidimbwi cha ukwasi.

Kielelezo cha kiolesura cha ERC-20

Ikiwa wewe ni mtayarishaji programu mwenye uzoefu, huenda unakumbuka kuona miundo inayofanana katika Java (opens in a new tab) au hata katika faili za vichwa vya C (opens in a new tab).

Huu ni ufafanuzi wa Kiolesura cha ERC-20 (opens in a new tab) kutoka OpenZeppelin. Ni tafsiri ya kiwango kinachosomeka na binadamu (opens in a new tab) kuwa msimbo wa Solidity. Bila shaka, kiolesura chenyewe hakifafanui jinsi ya kufanya chochote. Hilo limeelezwa katika msimbo chanzo wa mkataba hapo chini.

 

1// SPDX-License-Identifier: MIT

Faili za Solidity zinapaswa kujumuisha kitambulisho cha leseni. Unaweza kuona orodha ya leseni hapa (opens in a new tab). Ikiwa unahitaji leseni tofauti, lieleze tu kwenye maoni.

 

1pragma solidity >=0.6.0 <0.8.0;

Lugha ya Solidity bado inabadilika haraka, na matoleo mapya huenda yasiendane na msimbo wa zamani (tazama hapa (opens in a new tab)). Kwa hivyo, ni wazo zuri kubainisha si tu toleo la chini kabisa la lugha, bali pia toleo la juu zaidi, la hivi karibuni ambalo ulifanyia majaribio msimbo.

 

1/**
2 * @dev Kiolesura cha kiwango cha ERC20 kama kilivyofafanuliwa katika EIP.
3 */

@dev katika maoni ni sehemu ya umbizo la NatSpec (opens in a new tab), linalotumika kutoa nyaraka kutoka kwa msimbo chanzo.

 

1interface IERC20 {

Kwa kimapokeo, majina ya kiolesura huanza na I.

 

1 /**
2 * @dev Hurejesha kiasi cha tokeni zilizopo.
3 */
4 function totalSupply() external view returns (uint256);

Kazi hii ni ya nje, ikimaanisha inaweza tu kuitwa kutoka nje ya mkataba (opens in a new tab). Hurejesha jumla ya tokeni zilizo katika mkataba. Thamani hii inarejeshwa kwa kutumia aina ya kawaida zaidi katika Ethereum, biti 256 zisizo na ishara (biti 256 ni ukubwa asilia wa neno wa EVM). Kazi hii pia ni ya kuangalia, ambayo ina maana kwamba haibadilishi hali, hivyo inaweza kutekelezwa kwenye nodi moja badala ya kila nodi katika mnyororo wa bloku kuitekeleza. Aina hii ya kazi haitengenezi muamala na haigharimu gesi.

Kumbuka: Kinadharia inaweza kuonekana kuwa muundaji wa mkataba anaweza kudanganya kwa kurudisha jumla ndogo kuliko thamani halisi, na kufanya kila tokeni ionekane kuwa ya thamani zaidi kuliko ilivyo. Hata hivyo, hofu hiyo inapuuza asili halisi ya mnyororo wa bloku. Kila kitu kinachotokea kwenye mnyororo wa bloku kinaweza kuthibitishwa na kila nodi. Ili kufanikisha hili, msimbo wa lugha ya mashine na hifadhi ya kila mkataba hupatikana kwenye kila nodi. Ingawa hauhitajiki kuchapisha msimbo wa Solidity kwa mkataba wako, hakuna mtu atakayekuchukulia kwa uzito isipokuwa uchapishe msimbo chanzo na toleo la Solidity ambalo lilitumiwa kuukusanya, ili uweze kuthibitishwa dhidi ya msimbo wa lugha ya mashine ulioutoa. Kwa mfano, angalia mkataba huu (opens in a new tab).

 

1 /**
2 * @dev Hurejesha kiasi cha tokeni zinazomilikiwa na `akaunti`.
3 */
4 function balanceOf(address account) external view returns (uint256);

Kama jina linavyosema, balanceOf inarejesha salio la akaunti. Akaunti za Ethereum zinatambuliwa katika Solidity kwa kutumia aina ya anwani, ambayo inashikilia biti 160. Pia ni ya nje na ya kuangalia.

 

1 /**
2 * @dev Huhamisha `kiasi` cha tokeni kutoka kwa akaunti ya mpigaji simu hadi kwa `mpokeaji`.
3 *
4 * Hurudisha thamani ya boolean inayoonyesha kama operesheni imefanikiwa.
5 *
6 * Hutoa tukio la {Uhamisho}.
7 */
8 function transfer(address recipient, uint256 amount) external returns (bool);

Kazi ya kuhamisha huhamisha tokeni kutoka kwa mpigaji simu hadi anwani tofauti. Hii inahusisha mabadiliko ya hali, kwa hivyo si ya kuangalia. Mtumiaji anapoita kazi hii huunda muamala na kugharimu gesi. Pia hutoa tukio, Uhamisho, ili kuwajulisha wote kwenye mnyororo wa bloku kuhusu tukio hilo.

Kazi hii ina aina mbili za matokeo kwa aina mbili tofauti za wapigaji simu:

  • Watumiaji wanaoita kazi moja kwa moja kutoka kwa kiolesura cha mtumiaji. Kwa kawaida mtumiaji huwasilisha muamala na hasubiri jibu, ambalo linaweza kuchukua muda usiojulikana. Mtumiaji anaweza kuona kilichotokea kwa kutafuta risiti ya muamala (ambayo inatambuliwa na hashi ya muamala) au kwa kutafuta tukio la Uhamisho.
  • Mikataba mingine, ambayo huita kazi kama sehemu ya muamala mkuu. Mikataba hiyo hupata matokeo mara moja, kwa sababu huendeshwa katika muamala mmoja, kwa hivyo wanaweza kutumia thamani ya urejeshaji wa kazi.

Aina sawa ya matokeo huundwa na kazi zingine zinazobadilisha hali ya mkataba.

 

Posho huruhusu akaunti kutumia baadhi ya tokeni za mmiliki mwingine. Hii ni muhimu, kwa mfano, kwa mikataba inayofanya kazi kama wauzaji. Mikataba haiwezi kufuatilia matukio, kwa hivyo ikiwa mnunuzi angehamisha tokeni kwa mkataba wa muuzaji moja kwa moja mkataba huo haungejua umelipwa. Badala yake, mnunuzi huruhusu mkataba wa muuzaji kutumia kiasi fulani, na muuzaji huhamisha kiasi hicho. Hii inafanywa kupitia kazi ambayo mkataba wa muuzaji huita, ili mkataba wa muuzaji uweze kujua kama imefanikiwa.

1 /**
2 * @dev Hurejesha idadi iliyobaki ya tokeni ambazo `mtumiaji` atakuwa
3 * anaruhusiwa kutumia kwa niaba ya `mmiliki` kupitia {transferFrom}. Hii ni
4 * sifuri kwa chaguo-msingi.
5 *
6 * Thamani hii hubadilika wakati {approve} au {transferFrom} zinapoitwa.
7 */
8 function allowance(address owner, address spender) external view returns (uint256);

Kazi ya posho inaruhusu mtu yeyote kuuliza ili kuona ni posho gani anwani moja (mmiliki) inaruhusu anwani nyingine (mtumiaji) kutumia.

 

1 /**
2 * @dev Huweka `kiasi` kama posho ya `mtumiaji` juu ya tokeni za mpigaji simu.
3 *
4 * Hurudisha thamani ya boolean inayoonyesha kama operesheni imefanikiwa.
5 *
6 * MUHIMU: Jihadharini kwamba kubadilisha posho kwa njia hii huleta hatari
7 * kwamba mtu anaweza kutumia posho ya zamani na mpya kwa bahati mbaya
8 * ya mpangilio wa muamala. Suluhisho moja linalowezekana la kupunguza hali hii ya mbio
9 * ni kwanza kupunguza posho ya mtumiaji hadi 0 na kuweka
10 * thamani inayotakiwa baadaye:
11 * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
12 *
13 * Hutoa tukio la {Uidhinishaji}.
14 */
15 function approve(address spender, uint256 amount) external returns (bool);
Onyesha yote

Kazi ya kuidhinisha huunda posho. Hakikisha unasoma ujumbe kuhusu jinsi inaweza kutumiwa vibaya. Katika Ethereum unadhibiti mpangilio wa miamala yako mwenyewe, lakini huwezi kudhibiti mpangilio ambao miamala ya watu wengine itatekelezwa, isipokuwa usipowasilisha muamala wako mwenyewe hadi uone muamala wa upande mwingine umetokea.

 

1 /**
2 * @dev Huhamisha tokeni za `kiasi` kutoka kwa `mtumaji` hadi kwa `mpokeaji` kwa kutumia
3 * mfumo wa posho. `kiasi` kisha kinakatwa kutoka kwa posho ya mpigaji simu
4 * .
5 *
6 * Hurudisha thamani ya boolean inayoonyesha kama operesheni imefanikiwa.
7 *
8 * Hutoa tukio la {Uhamisho}.
9 */
10 function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
Onyesha yote

Mwishowe, transferFrom hutumiwa na mtumiaji kutumia posho halisi.

 

1
2 /**
3 * @dev Hutolewa wakati tokeni za `thamani` zinapohamishwa kutoka akaunti moja (`kutoka`) kwenda
4 * nyingine (`kwenda`).
5 *
6 * Kumbuka kuwa `thamani` inaweza kuwa sifuri.
7 */
8 event Transfer(address indexed from, address indexed to, uint256 value);
9
10 /**
11 * @dev Hutolewa wakati posho ya `mtumiaji` kwa `mmiliki` imewekwa na
12 * wito kwa {approve}. `thamani` ni posho mpya.
13 */
14 event Approval(address indexed owner, address indexed spender, uint256 value);
15}
Onyesha yote

Matukio haya hutolewa wakati hali ya mkataba wa ERC-20 inapobadilika.

Mkataba Halisi

Huu ndio mkataba halisi unaotekeleza kiwango cha ERC-20, umechukuliwa kutoka hapa (opens in a new tab). Haikusudiwi kutumiwa kama ilivyo, lakini unaweza kurithi (opens in a new tab) kutoka kwayo ili kuipanua iwe kitu kinachoweza kutumika.

1// SPDX-License-Identifier: MIT
2pragma solidity >=0.6.0 <0.8.0;

 

Taarifa za Kuingiza

Mbali na ufafanuzi wa kiolesura hapo juu, ufafanuzi wa mkataba unaingiza faili zingine mbili:

1
2import "../../GSN/Context.sol";
3import "./IERC20.sol";
4import "../../math/SafeMath.sol";
  • GSN/Context.sol ni ufafanuzi unaohitajika kutumia OpenGSN (opens in a new tab), mfumo unaowaruhusu watumiaji wasio na ether kutumia mnyororo wa bloku. Kumbuka kuwa hili ni toleo la zamani, ikiwa unataka kuunganisha na OpenGSN tumia mafunzo haya (opens in a new tab).
  • Maktaba ya SafeMath (opens in a new tab), ambayo inazuia kufurika/kupungua kwa hesabu kwa matoleo ya Solidity <0.8.0. Katika Solidity ≥0.8.0, operesheni za hesabu hurejea kiotomatiki kwenye kufurika/kupungua, na kufanya SafeMath isiwe ya lazima. Mkataba huu unatumia SafeMath kwa uoanifu wa nyuma na matoleo ya zamani ya mkusanyaji.

 

Maoni haya yanaelezea madhumuni ya mkataba.

1/**
2 * @dev Utekelezaji wa kiolesura cha {IERC20}.
3 *
4 * Utekelezaji huu haujali jinsi tokeni zinavyoundwa. Hii inamaanisha
5 * kwamba mfumo wa usambazaji unapaswa kuongezwa katika mkataba uliochukuliwa kwa kutumia {_mint}.
6 * Kwa mfumo wa jumla tazama {ERC20PresetMinterPauser}.
7 *
8 * DONDOO: Kwa maandishi ya kina tazama mwongozo wetu
9 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[Jinsi
10 * ya kutekeleza mifumo ya usambazaji].
11 *
12 * Tumefuata miongozo ya jumla ya OpenZeppelin: kazi hurudisha badala
13 * ya kurudisha `false` kwenye kutofaulu. Tabia hii hata hivyo ni ya kawaida
14 * na haipingani na matarajio ya mifumo ya ERC20.
15 *
16 * Zaidi ya hayo, tukio la {Uidhinishaji} hutolewa kwenye wito kwa {transferFrom}.
17 * Hii inaruhusu mifumo kujenga upya posho kwa akaunti zote tu
18 * kwa kusikiliza matukio yaliyosemwa. Utekelezaji mwingine wa EIP hauwezi kutoa
19 * matukio haya, kwani haihitajiki na vipimo.
20 *
21 * Mwishowe, kazi zisizo za kawaida za {decreaseAllowance} na {increaseAllowance}
22 * zimeongezwa ili kupunguza maswala yanayojulikana karibu na kuweka
23 * posho. Tazama {IERC20-approve}.
24 */
Onyesha yote

Ufafanuzi wa Mkataba

1contract ERC20 is Context, IERC20 {

Mstari huu unabainisha urithi, katika kesi hii kutoka IERC20 kutoka juu na Context, kwa OpenGSN.

 

1
2 using SafeMath for uint256;
3

Mstari huu unaunganisha maktaba ya SafeMath na aina ya uint256. Unaweza kupata maktaba hii hapa (opens in a new tab).

Ufafanuzi wa Vigezo

Ufafanuzi huu unabainisha vigezo vya hali ya mkataba. Vigezo hivi vimetangazwa kuwa vya faragha, lakini hiyo inamaanisha tu kwamba mikataba mingine kwenye mnyororo wa bloku haiwezi kuvisoma. Hakuna siri kwenye mnyororo wa bloku, programu kwenye kila nodi ina hali ya kila mkataba katika kila bloku. Kwa kimapokeo, vigezo vya hali vinaitwa _<something>.

Vigezo viwili vya kwanza ni ramani (opens in a new tab), maana yake vina tabia sawa na safu unganishi (opens in a new tab), isipokuwa kwamba funguo ni thamani za nambari. Hifadhi inatengwa tu kwa maingizo ambayo yana thamani tofauti na chaguo-msingi (sifuri).

1 mapping (address => uint256) private _balances;

Ramani ya kwanza, _balances, ni anwani na salio zao za tokeni hii. Ili kupata salio, tumia sintaksia hii: _balances[<address>].

 

1 mapping (address => mapping (address => uint256)) private _allowances;

Kigezo hiki, _allowances, huhifadhi posho zilizoelezwa mapema. Faharasa ya kwanza ni mmiliki wa tokeni, na ya pili ni mkataba wenye posho. Ili kupata kiasi ambacho anwani A inaweza kutumia kutoka kwa akaunti ya anwani B, tumia _allowances[B][A].

 

1 uint256 private _totalSupply;

Kama jina linavyopendekeza, kigezo hiki hufuatilia jumla ya tokeni zote.

 

1 string private _name;
2 string private _symbol;
3 uint8 private _decimals;

Vigezo hivi vitatu vinatumika kuboresha usomaji. Viwili vya kwanza vinajieleza, lakini _decimals haieleweki.

Kwa upande mmoja, Ethereum haina vigezo vya nambari za desimali au sehemu. Kwa upande mwingine, binadamu wanapenda kuweza kugawanya tokeni. Sababu moja watu walitumia dhahabu kama sarafu ilikuwa ni kwamba ilikuwa ngumu kutoa chenji wakati mtu alitaka kununua bata lenye thamani ya sehemu ya ng'ombe.

Suluhisho ni kufuatilia nambari kamili, lakini kuhesabu badala ya tokeni halisi tokeni ya sehemu ambayo haina karibu thamani yoyote. Katika kesi ya ether, tokeni ya sehemu inaitwa wei, na 10^18 wei ni sawa na moja ETH. Wakati wa kuandika, 10,000,000,000,000 wei ni takriban senti moja ya Dola ya Marekani au Euro.

Mifumo inahitaji kujua jinsi ya kuonyesha salio la tokeni. Ikiwa mtumiaji ana 3,141,000,000,000,000,000 wei, je, hiyo ni 3.14 ETH? 31.41 ETH? 3,141 ETH? Katika kesi ya ether imefafanuliwa kuwa 10^18 wei ni sawa na ETH, lakini kwa tokeni yako unaweza kuchagua thamani tofauti. Ikiwa kugawanya tokeni hakuna maana, unaweza kutumia thamani ya _decimals ya sifuri. Ikiwa unataka kutumia kiwango sawa na ETH, tumia thamani 18.

Mjenzi

1 /**
2 * @dev Huweka thamani za {name} na {symbol}, na huanzisha {decimals} na
3 * thamani ya msingi ya 18.
4 *
5 * Ili kuchagua thamani tofauti kwa {decimals}, tumia {_setupDecimals}.
6 *
7 * Thamani hizi zote tatu hazibadiliki: zinaweza kuwekwa mara moja tu wakati
8 * wa ujenzi.
9 */
10 constructor (string memory name_, string memory symbol_) public {
11 // Katika Solidity ≥0.7.0, 'public' ni dhahiri na inaweza kuachwa.
12
13 _name = name_;
14 _symbol = symbol_;
15 _decimals = 18;
16 }
Onyesha yote

Mjenzi huitwa wakati mkataba unapoundwa kwa mara ya kwanza. Kwa kimapokeo, vigezo vya kazi huitwa <something>_.

Kazi za Kiolesura cha Mtumiaji

1 /**
2 * @dev Hurejesha jina la tokeni.
3 */
4 function name() public view returns (string memory) {
5 return _name;
6 }
7
8 /**
9 * @dev Hurejesha alama ya tokeni, kawaida toleo fupi la
10 * jina.
11 */
12 function symbol() public view returns (string memory) {
13 return _symbol;
14 }
15
16 /**
17 * @dev Hurejesha idadi ya desimali zinazotumika kupata uwakilishi wake kwa mtumiaji.
18 * Kwa mfano, ikiwa `decimals` ni sawa na `2`, salio la tokeni `505` linapaswa
19 * kuonyeshwa kwa mtumiaji kama `5,05` (`505 / 10 ** 2`).
20 *
21 * Tokeni kawaida huchagua thamani ya 18, zikiiga uhusiano kati
22 * ya ether na wei. Hii ndiyo thamani ambayo {ERC20} hutumia, isipokuwa {_setupDecimals}
23 * ikiitwa.
24 *
25 * KUMBUKA: Habari hii inatumika tu kwa madhumuni ya _kuonyesha_: haiathiri
26 * kwa njia yoyote hesabu zozote za mkataba, ikiwa ni pamoja na
27 * {IERC20-balanceOf} na {IERC20-transfer}.
28 */
29 function decimals() public view returns (uint8) {
30 return _decimals;
31 }
Onyesha yote

Kazi hizi, name, symbol, na decimals husaidia violesura vya mtumiaji kujua kuhusu mkataba wako ili waweze kuuonyesha vizuri.

Aina ya urejeshaji ni string memory, ikimaanisha kurudisha mfuatano ambao umehifadhiwa kwenye kumbukumbu. Vigezo, kama vile mifufulizo, vinaweza kuhifadhiwa katika maeneo matatu:

Muda wa MaishaUfikiaji wa MkatabaGharama ya Gesi
KumbukumbuWito wa kaziSoma/AndikaMakumi au mamia (juu zaidi kwa maeneo ya juu zaidi)
CalldataWito wa kaziSoma PekeeHaiwezi kutumika kama aina ya urejeshaji, tu aina ya kigezo cha kazi
GhalaHadi kubadilishwaSoma/AndikaJuu (800 kwa kusoma, 20k kwa kuandika)

Katika kesi hii, memory ni chaguo bora zaidi.

Soma Taarifa za Tokeni

Hizi ni kazi zinazotoa habari kuhusu tokeni, ama jumla ya usambazaji au salio la akaunti.

1 /**
2 * @dev Tazama {IERC20-totalSupply}.
3 */
4 function totalSupply() public view override returns (uint256) {
5 return _totalSupply;
6 }

Kazi ya totalSupply inarejesha jumla ya usambazaji wa tokeni.

 

1 /**
2 * @dev Tazama {IERC20-balanceOf}.
3 */
4 function balanceOf(address account) public view override returns (uint256) {
5 return _balances[account];
6 }

Soma salio la akaunti. Kumbuka kuwa mtu yeyote anaruhusiwa kupata salio la akaunti ya mtu mwingine. Hakuna maana ya kujaribu kuficha habari hii, kwa sababu inapatikana kwenye kila nodi hata hivyo. Hakuna siri kwenye mnyororo wa bloku.

Hamisha Tokeni

1 /**
2 * @dev Tazama {IERC20-transfer}.
3 *
4 * Mahitaji:
5 *
6 * - `mpokeaji` hawezi kuwa anwani ya sifuri.
7 * - mpigaji simu lazima awe na salio la angalau `kiasi`.
8 */
9 function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
Onyesha yote

Kazi ya kuhamisha inaitwa ili kuhamisha tokeni kutoka kwa akaunti ya mtumaji hadi kwa nyingine tofauti. Kumbuka kwamba ingawa inarudisha thamani ya boolean, thamani hiyo daima ni kweli. Ikiwa uhamisho utashindwa mkataba unarudisha wito.

 

1 _transfer(_msgSender(), recipient, amount);
2 return true;
3 }

Kazi ya _transfer inafanya kazi halisi. Ni kazi ya faragha ambayo inaweza tu kuitwa na kazi zingine za mkataba. Kwa kimapokeo kazi za faragha zinaitwa _<something>, sawa na vigezo vya hali.

Kawaida katika Solidity tunatumia msg.sender kwa mtumaji wa ujumbe. Hata hivyo, hiyo inavunja OpenGSN (opens in a new tab). Ikiwa tunataka kuruhusu miamala isiyo na ether na tokeni yetu, tunahitaji kutumia _msgSender(). Inarudisha msg.sender kwa miamala ya kawaida, lakini kwa isiyo na ether hurudisha mtia saini asilia na sio mkataba uliopitisha ujumbe.

Kazi za Posho

Hizi ni kazi zinazotekeleza utendaji wa posho: allowance, approve, transferFrom, na _approve. Zaidi ya hayo, utekelezaji wa OpenZeppelin unapita kiwango cha msingi kujumuisha baadhi ya vipengele vinavyoboresha usalama: increaseAllowance, na decreaseAllowance.

Kazi ya posho

1 /**
2 * @dev Tazama {IERC20-allowance}.
3 */
4 function allowance(address owner, address spender) public view virtual override returns (uint256) {
5 return _allowances[owner][spender];
6 }

Kazi ya posho inaruhusu kila mtu kuangalia posho yoyote.

Kazi ya kuidhinisha

1 /**
2 * @dev Tazama {IERC20-approve}.
3 *
4 * Mahitaji:
5 *
6 * - `mtumiaji` hawezi kuwa anwani ya sifuri.
7 */
8 function approve(address spender, uint256 amount) public virtual override returns (bool) {

Kazi hii inaitwa ili kuunda posho. Inafanana na kazi ya kuhamisha hapo juu:

  • Kazi hii huita tu kazi ya ndani (katika kesi hii, _approve) ambayo inafanya kazi halisi.
  • Kazi hii hurudisha kweli (ikiwa imefanikiwa) au hurudisha nyuma (ikiwa sivyo).

 

1 _approve(_msgSender(), spender, amount);
2 return true;
3 }

Tunatumia kazi za ndani ili kupunguza idadi ya maeneo ambapo mabadiliko ya hali hutokea. Kazi yoyote inayobadilisha hali ni hatari inayowezekana ya usalama ambayo inahitaji kukaguliwa kwa usalama. Kwa njia hii tuna nafasi chache za kukosea.

Kazi ya transferFrom

Hii ni kazi ambayo mtumiaji huita ili kutumia posho. Hii inahitaji operesheni mbili: kuhamisha kiasi kinachotumiwa na kupunguza posho kwa kiasi hicho.

1 /**
2 * @dev Tazama {IERC20-transferFrom}.
3 *
4 * Hutoa tukio la {Uidhinishaji} linaloonyesha posho iliyosasishwa. Hii haihitajiki
5 * na EIP. Tazama dokezo mwanzoni mwa {ERC20}.
6 *
7 * Mahitaji:
8 *
9 * - `mtumaji` na `mpokeaji` hawawezi kuwa anwani ya sifuri.
10 * - `mtumaji` lazima awe na salio la angalau `kiasi`.
11 * - mpigaji simu lazima awe na posho kwa tokeni za ``mtumaji`` za angalau
12 * `kiasi`.
13 */
14 function transferFrom(address sender, address recipient, uint256 amount) public virtual
15 override returns (bool) {
16 _transfer(sender, recipient, amount);
Onyesha yote

 

Wito wa kazi wa a.sub(b, "ujumbe") hufanya mambo mawili. Kwanza, inakokotoa a-b, ambayo ni posho mpya. Pili, inahakikisha kuwa matokeo haya si hasi. Ikiwa ni hasi wito unarudishwa na ujumbe uliotolewa. Kumbuka kwamba wito unapobadilishwa uchakataji wowote uliofanywa awali wakati wa wito huo hupuuzwa kwa hivyo hatuhitaji kubatilisha _transfer.

1 _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount,
2 "ERC20: kiasi cha uhamisho kinazidi posho"));
3 return true;
4 }

Nyongeza za usalama za OpenZeppelin

Ni hatari kuweka posho isiyo ya sifuri kwa thamani nyingine isiyo ya sifuri, kwa sababu unadhibiti tu mpangilio wa miamala yako, si ya mtu mwingine yeyote. Fikiria una watumiaji wawili, Alice ambaye ni mnyofu na Bill ambaye si mwaminifu. Alice anataka huduma fulani kutoka kwa Bill, ambayo anadhani inagharimu tokeni tano - kwa hivyo anampa Bill posho ya tokeni tano.

Kisha kitu kinabadilika na bei ya Bill inapanda hadi tokeni kumi. Alice, ambaye bado anataka huduma hiyo, hutuma muamala unaoweka posho ya Bill kuwa kumi. Papo hapo Bill anapoona muamala huu mpya katika dimbwi la miamala hutuma muamala unaotumia tokeni tano za Alice na una bei ya juu zaidi ya gesi ili ichimbwe haraka zaidi. Kwa njia hiyo Bill anaweza kutumia kwanza tokeni tano na kisha, mara posho mpya ya Alice itakapochimbwa, kutumia kumi zaidi kwa bei ya jumla ya tokeni kumi na tano, zaidi ya ambavyo Alice alikusudia kuidhinisha. Mbinu hii inaitwa Uendeshaji - wa mbele (opens in a new tab)

Muamala wa AliceNonce ya AliceMuamala wa BillNonce ya BillPosho ya BillJumla ya Mapato ya Bill kutoka kwa Alice
approve(Bill, 5)1050
transferFrom(Alice, Bill, 5)10,12305
approve(Bill, 10)11105
transferFrom(Alice, Bill, 10)10,124015

Ili kuepuka tatizo hili, kazi hizi mbili (increaseAllowance na decreaseAllowance) zinakuruhusu kurekebisha posho kwa kiasi maalum. Kwa hiyo kama Bill tayari alikuwa ametumia tokeni tano, ataweza tu kutumia tano zaidi. Kulingana na muda, kuna njia mbili hii inaweza kufanya kazi, zote mbili zikimalizika na Bill kupata tokeni kumi tu:

A:

Muamala wa AliceNonce ya AliceMuamala wa BillNonce ya BillPosho ya BillJumla ya Mapato ya Bill kutoka kwa Alice
approve(Bill, 5)1050
transferFrom(Alice, Bill, 5)10,12305
increaseAllowance(Bill, 5)110+5 = 55
transferFrom(Alice, Bill, 5)10,124010

B:

Muamala wa AliceNonce ya AliceMuamala wa BillNonce ya BillPosho ya BillJumla ya Mapato ya Bill kutoka kwa Alice
approve(Bill, 5)1050
increaseAllowance(Bill, 5)115+5 = 100
transferFrom(Alice, Bill, 10)10,124010
1 /**
2 * @dev Huongeza kiatomiki posho iliyotolewa kwa `mtumiaji` na mpigaji.
3 *
4 * Hii ni mbadala ya {approve} ambayo inaweza kutumika kama upunguzaji wa
5 * matatizo yaliyoelezwa katika {IERC20-approve}.
6 *
7 * Hutoa tukio la {Uidhinishaji} linaloonyesha posho iliyosasishwa.
8 *
9 * Mahitaji:
10 *
11 * - `mtumiaji` hawezi kuwa anwani ya sifuri.
12 */
13 function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
14 _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
15 return true;
16 }
Onyesha yote

Kazi ya a.add(b) ni nyongeza salama. Katika kesi isiyowezekana kwamba a+b>=2^256 haizunguki kama nyongeza ya kawaida inavyofanya.

1
2 /**
3 * @dev Hupunguza kiatomiki posho iliyotolewa kwa `mtumiaji` na mpigaji.
4 *
5 * Hii ni mbadala ya {approve} ambayo inaweza kutumika kama upunguzaji wa
6 * matatizo yaliyoelezwa katika {IERC20-approve}.
7 *
8 * Hutoa tukio la {Uidhinishaji} linaloonyesha posho iliyosasishwa.
9 *
10 * Mahitaji:
11 *
12 * - `mtumiaji` hawezi kuwa anwani ya sifuri.
13 * - `mtumiaji` lazima awe na posho kwa mpigaji wa angalau
14 * `subtractedValue`.
15 */
16 function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
17 _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue,
18 "ERC20: posho iliyopunguzwa chini ya sifuri"));
19 return true;
20 }
Onyesha yote

Kazi Zinazobadilisha Taarifa za Tokeni

Hizi ni kazi nne zinazofanya kazi halisi: _transfer, _mint, _burn, na _approve.

Kazi ya _transfer

1 /**
2 * @dev Huhamisha tokeni `kiasi` kutoka `mtumaji` hadi `mpokeaji`.
3 *
4 * Hii ni kazi ya ndani inayofanana na {transfer}, na inaweza kutumika kwa
5 * k.m., kutekeleza ada za tokeni otomatiki, mifumo ya kupunguza, n.k.
6 *
7 * Hutoa tukio la {Uhamisho}.
8 *
9 * Mahitaji:
10 *
11 * - `mtumaji` hawezi kuwa anwani ya sifuri.
12 * - `mpokeaji` hawezi kuwa anwani ya sifuri.
13 * - `mtumaji` lazima awe na salio la angalau `kiasi`.
14 */
15 function _transfer(address sender, address recipient, uint256 amount) internal virtual {
Onyesha yote

Kazi hii, _transfer, huhamisha tokeni kutoka akaunti moja hadi nyingine. Inaitwa na transfer (kwa uhamisho kutoka kwa akaunti ya mtumaji mwenyewe) na transferFrom (kwa kutumia posho kwa uhamisho kutoka kwa akaunti ya mtu mwingine).

 

1 require(sender != address(0), "ERC20: uhamisho kutoka kwa anwani ya sifuri");
2 require(recipient != address(0), "ERC20: uhamisho kwenda kwa anwani ya sifuri");

Hakuna mtu anayemiliki anwani sifuri katika Ethereum (yaani, hakuna mtu anayejua ufunguo binafsi ambao ufunguo wake wa umma unaofanana unabadilishwa kuwa anwani ya sifuri). Watu wanapotumia anwani hiyo, kawaida huwa ni hitilafu ya programu - kwa hivyo tunashindwa ikiwa anwani ya sifuri itatumika kama mtumaji au mpokeaji.

 

1 _beforeTokenTransfer(sender, recipient, amount);
2

Kuna njia mbili za kutumia mkataba huu:

  1. Itumie kama kiolezo cha msimbo wako mwenyewe
  2. Rithi kutoka kwayo (opens in a new tab), na ubadilishe tu kazi unazohitaji kurekebisha

Njia ya pili ni bora zaidi kwa sababu msimbo wa OpenZeppelin ERC-20 tayari umekaguliwa na kuonyeshwa kuwa salama. Unapotumia urithi ni wazi ni kazi gani unazorekebisha, na ili kuamini mkataba wako watu wanahitaji tu kukagua kazi hizo maalum.

Mara nyingi ni muhimu kutekeleza kazi kila wakati tokeni zinapohamishwa. Hata hivyo, _transfer ni kazi muhimu sana na inawezekana kuiandika bila usalama (tazama hapa chini), kwa hivyo ni bora si kuibadilisha. Suluhisho ni _beforeTokenTransfer, kazi ya ndoano (opens in a new tab). Unaweza kubadilisha kazi hii, na itaitwa kwenye kila uhamisho.

 

1 _balances[sender] = _balances[sender].sub(amount, "ERC20: kiasi cha uhamisho kinazidi salio");
2 _balances[recipient] = _balances[recipient].add(amount);

Hizi ni mistari inayofanya uhamisho halisi. Kumbuka kuwa hakuna kitu kati yao, na kwamba tunapunguza kiasi kilichohamishwa kutoka kwa mtumaji kabla ya kukiongeza kwa mpokeaji. Hii ni muhimu kwa sababu kama kungekuwa na wito kwa mkataba tofauti katikati, ingeweza kutumiwa kudanganya mkataba huu. Kwa njia hii uhamisho ni wa atomiki, hakuna kinachoweza kutokea katikati yake.

 

1 emit Transfer(sender, recipient, amount);
2 }

Mwishowe, toa tukio la Uhamisho. Matukio hayapatikani kwa mikataba-erevu, lakini msimbo unaoendeshwa nje ya mnyororo wa bloku unaweza kusikiliza matukio na kuyajibu. Kwa mfano, mkoba unaweza kufuatilia wakati mmiliki anapata tokeni zaidi.

Kazi za _mint na _burn

Kazi hizi mbili (_mint na _burn) hubadilisha jumla ya usambazaji wa tokeni. Ziko ndani na hakuna kazi inayowaita katika mkataba huu, kwa hivyo ni muhimu tu ikiwa utarithi kutoka kwa mkataba na kuongeza mantiki yako mwenyewe kuamua chini ya hali gani kuzalisha tokeni mpya au kuchoma zilizopo.

KUMBUKA: Kila tokeni ya ERC-20 ina mantiki yake ya biashara inayoelekeza usimamizi wa tokeni. Kwa mfano, mkataba wa usambazaji uliowekwa unaweza kuita tu _mint katika mjenzi na kamwe usiite _burn. Mkataba unaouza tokeni utaite _mint unapolipwa, na pengine utaite _burn wakati fulani ili kuepuka mfumuko wa bei usiodhibitiwa.

1 /** @dev Huunda tokeni za `kiasi` na kuzikabidhi kwa `akaunti`, akiongeza
2 * jumla ya usambazaji.
3 *
4 * Hutoa tukio la {Uhamisho} na `kutoka` ikiwa imewekwa kwenye anwani ya sifuri.
5 *
6 * Mahitaji:
7 *
8 * - `kwa` hawezi kuwa anwani ya sifuri.
9 */
10 function _mint(address account, uint256 amount) internal virtual {
11 require(account != address(0), "ERC20: zalisha kwa anwani ya sifuri");
12 _beforeTokenTransfer(address(0), account, amount);
13 _totalSupply = _totalSupply.add(amount);
14 _balances[account] = _balances[account].add(amount);
15 emit Transfer(address(0), account, amount);
16 }
Onyesha yote

Hakikisha unasasisha _totalSupply wakati jumla ya idadi ya tokeni inapobadilika.

 

1 /**
2 * @dev Huharibu tokeni za `kiasi` kutoka kwa `akaunti`, ikipunguza
3 * jumla ya usambazaji.
4 *
5 * Hutoa tukio la {Uhamisho} na `kwa` ikiwa imewekwa kwenye anwani ya sifuri.
6 *
7 * Mahitaji:
8 *
9 * - `akaunti` haiwezi kuwa anwani ya sifuri.
10 * - `akaunti` lazima iwe na angalau tokeni za `kiasi`.
11 */
12 function _burn(address account, uint256 amount) internal virtual {
13 require(account != address(0), "ERC20: choma kutoka kwa anwani ya sifuri");
14
15 _beforeTokenTransfer(account, address(0), amount);
16
17 _balances[account] = _balances[account].sub(amount, "ERC20: kiasi cha kuchoma kinazidi salio");
18 _totalSupply = _totalSupply.sub(amount);
19 emit Transfer(account, address(0), amount);
20 }
Onyesha yote

Kazi ya _burn inakaribia kufanana na _mint, isipokuwa inaenda kinyume chake.

Kazi ya _approve

Hii ni kazi inayobainisha posho. Kumbuka kuwa inaruhusu mmiliki kubainisha posho ambayo ni ya juu kuliko salio la sasa la mmiliki. Hii ni sawa kwa sababu salio huangaliwa wakati wa uhamisho, ambapo inaweza kuwa tofauti na salio wakati posho inapoundwa.

1 /**
2 * @dev Huweka `kiasi` kama posho ya `mtumiaji` juu ya tokeni za `mmiliki`.
3 *
4 * Kazi hii ya ndani ni sawa na `approve`, na inaweza kutumika kwa
5 * k.m., kuweka posho za kiotomatiki kwa mifumo fulani, n.k.
6 *
7 * Hutoa tukio la {Uidhinishaji}.
8 *
9 * Mahitaji:
10 *
11 * - `mmiliki` hawezi kuwa anwani ya sifuri.
12 * - `mtumiaji` hawezi kuwa anwani ya sifuri.
13 */
14 function _approve(address owner, address spender, uint256 amount) internal virtual {
15 require(owner != address(0), "ERC20: idhinisha kutoka kwa anwani ya sifuri");
16 require(spender != address(0), "ERC20: idhinisha kwa anwani ya sifuri");
17
18 _allowances[owner][spender] = amount;
Onyesha yote

 

Toa tukio la Uidhinishaji. Kulingana na jinsi mfumo unavyoandikwa, mkataba wa mtumiaji unaweza kuambiwa kuhusu idhini ama na mmiliki au na seva inayosikiliza matukio haya.

1 emit Approval(owner, spender, amount);
2 }
3

Rekebisha Kigezo cha Desimali

1
2
3 /**
4 * @dev Huweka {decimals} kwa thamani tofauti na ile ya msingi ya 18.
5 *
6 * ONYO: Kazi hii inapaswa kuitwa tu kutoka kwa mjenzi. Mifumo mingi
7 * inayoingiliana na mikataba ya tokeni haitatarajia
8 * {decimals} kubadilika, na inaweza kufanya kazi kimakosa ikibadilika.
9 */
10 function _setupDecimals(uint8 decimals_) internal {
11 _decimals = decimals_;
12 }
Onyesha yote

Kazi hii inarekebisha kigezo cha _decimals ambacho kinatumika kuwaambia violesura vya mtumiaji jinsi ya kutafsiri kiasi. Unapaswa kuiita kutoka kwa mjenzi. Itakuwa si uaminifu kuiita wakati wowote unaofuata, na mifumo haikuundwa kuishughulikia.

Vidokezo

1
2 /**
3 * @dev Ndoano inayoitwa kabla ya uhamisho wowote wa tokeni. Hii inajumuisha
4 * kuzalisha na kuchoma.
5 *
6 * Masharti ya wito:
7 *
8 * - wakati `kutoka` na `kwa` zote si sifuri, `kiasi` cha tokeni za ``kutoka``
9 * kitahamishiwa kwa `kwa`.
10 * - wakati `kutoka` ni sifuri, tokeni za `kiasi` zitazalishwa kwa `kwa`.
11 * - wakati `kwa` ni sifuri, `kiasi` cha tokeni za ``kutoka`` kitachomwa.
12 * - `kutoka` na `kwa` kamwe haviko zote sifuri.
13 *
14 * Ili kujifunza zaidi kuhusu ndoano, nenda kwa xref:ROOT:extending-contracts.adoc#using-hooks[Kutumia Ndoano].
15 */
16 function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
17}
Onyesha yote

Hii ni kazi ya ndoano ya kuitwa wakati wa uhamisho. Hapa ni tupu, lakini ukihitaji iwe na kitu unaibadilisha tu.

Hitimisho

Kwa mapitio, hapa kuna baadhi ya mawazo muhimu zaidi katika mkataba huu (kwa maoni yangu, yako yanaweza kutofautiana):

  • Hakuna siri kwenye mnyororo wa bloku. Taarifa yoyote ambayo mkataba-erevu unaweza kuipata inapatikana kwa ulimwengu wote.
  • Unaweza kudhibiti mpangilio wa miamala yako mwenyewe, lakini si wakati miamala ya watu wengine inatokea. Hii ndiyo sababu kubadilisha posho kunaweza kuwa hatari, kwa sababu kunamruhusu mtumiaji kutumia jumla ya posho zote mbili.
  • Thamani za aina ya uint256 huzunguka. Kwa maneno mengine, 0-1=2^256-1. Ikiwa hiyo si tabia inayotakiwa, unapaswa kuiangalia (au kutumia maktaba ya SafeMath ambayo inakufanyia). Kumbuka kuwa hii ilibadilika katika Solidity 0.8.0 (opens in a new tab).
  • Fanya mabadiliko yote ya hali ya aina maalum katika sehemu maalum, kwa sababu inafanya ukaguzi kuwa rahisi. Hii ndiyo sababu tuna, kwa mfano, _approve, ambayo inaitwa na approve, transferFrom, increaseAllowance, na decreaseAllowance
  • Mabadiliko ya hali yanapaswa kuwa ya atomiki, bila kitendo kingine chochote katikati yao (kama unavyoweza kuona katika _transfer). Hii ni kwa sababu wakati wa mabadiliko ya hali una hali isiyolingana. Kwa mfano, kati ya wakati unapopunguza kutoka kwa salio la mtumaji na wakati unapoongeza kwa salio la mpokeaji kuna tokeni chache zilizopo kuliko inavyopaswa kuwa. Hii inaweza kutumiwa vibaya ikiwa kuna operesheni kati yao, hasa wito kwa mkataba tofauti.

Sasa kwa kuwa umeona jinsi mkataba wa OpenZeppelin ERC-20 unavyoandikwa, na hasa jinsi unavyofanywa kuwa salama zaidi, nenda uandike mikataba na mifumo yako salama.

Tazama hapa kwa kazi zangu zaidi (opens in a new tab).

Ukurasa ulihaririwa mwisho: 22 Oktoba 2025

Umesaidika na mafunzo haya?