Mkataba wa ERC-20: Maelezo ya Kina
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.
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: MITFaili 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` atakuwa3 * anaruhusiwa kutumia kwa niaba ya `mmiliki` kupitia {transferFrom}. Hii ni4 * 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 hatari7 * kwamba mtu anaweza kutumia posho ya zamani na mpya kwa bahati mbaya8 * ya mpangilio wa muamala. Suluhisho moja linalowezekana la kupunguza hali hii ya mbio9 * ni kwanza kupunguza posho ya mtumiaji hadi 0 na kuweka10 * thamani inayotakiwa baadaye:11 * https://github.com/ethereum/EIPs/issues/20#issuecomment-26352472912 *13 * Hutoa tukio la {Uidhinishaji}.14 */15 function approve(address spender, uint256 amount) external returns (bool);Onyesha yoteKazi 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 kutumia3 * mfumo wa posho. `kiasi` kisha kinakatwa kutoka kwa posho ya mpigaji simu4 * .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 yoteMwishowe, transferFrom hutumiwa na mtumiaji kutumia posho halisi.
12 /**3 * @dev Hutolewa wakati tokeni za `thamani` zinapohamishwa kutoka akaunti moja (`kutoka`) kwenda4 * nyingine (`kwenda`).5 *6 * Kumbuka kuwa `thamani` inaweza kuwa sifuri.7 */8 event Transfer(address indexed from, address indexed to, uint256 value);910 /**11 * @dev Hutolewa wakati posho ya `mtumiaji` kwa `mmiliki` imewekwa na12 * wito kwa {approve}. `thamani` ni posho mpya.13 */14 event Approval(address indexed owner, address indexed spender, uint256 value);15}Onyesha yoteMatukio 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: MIT2pragma solidity >=0.6.0 <0.8.0;
Taarifa za Kuingiza
Mbali na ufafanuzi wa kiolesura hapo juu, ufafanuzi wa mkataba unaingiza faili zingine mbili:
12import "../../GSN/Context.sol";3import "./IERC20.sol";4import "../../math/SafeMath.sol";GSN/Context.solni 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 inamaanisha5 * 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 wetu9 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[Jinsi10 * ya kutekeleza mifumo ya usambazaji].11 *12 * Tumefuata miongozo ya jumla ya OpenZeppelin: kazi hurudisha badala13 * ya kurudisha `false` kwenye kutofaulu. Tabia hii hata hivyo ni ya kawaida14 * 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 tu18 * kwa kusikiliza matukio yaliyosemwa. Utekelezaji mwingine wa EIP hauwezi kutoa19 * 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 kuweka23 * posho. Tazama {IERC20-approve}.24 */Onyesha yoteUfafanuzi wa Mkataba
1contract ERC20 is Context, IERC20 {Mstari huu unabainisha urithi, katika kesi hii kutoka IERC20 kutoka juu na Context, kwa OpenGSN.
12 using SafeMath for uint256;3Mstari 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} na3 * 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 wakati8 * wa ujenzi.9 */10 constructor (string memory name_, string memory symbol_) public {11 // Katika Solidity ≥0.7.0, 'public' ni dhahiri na inaweza kuachwa.1213 _name = name_;14 _symbol = symbol_;15 _decimals = 18;16 }Onyesha yoteMjenzi 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 }78 /**9 * @dev Hurejesha alama ya tokeni, kawaida toleo fupi la10 * jina.11 */12 function symbol() public view returns (string memory) {13 return _symbol;14 }1516 /**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` linapaswa19 * kuonyeshwa kwa mtumiaji kama `5,05` (`505 / 10 ** 2`).20 *21 * Tokeni kawaida huchagua thamani ya 18, zikiiga uhusiano kati22 * 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_: haiathiri26 * kwa njia yoyote hesabu zozote za mkataba, ikiwa ni pamoja na27 * {IERC20-balanceOf} na {IERC20-transfer}.28 */29 function decimals() public view returns (uint8) {30 return _decimals;31 }Onyesha yoteKazi 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 Maisha | Ufikiaji wa Mkataba | Gharama ya Gesi | |
|---|---|---|---|
| Kumbukumbu | Wito wa kazi | Soma/Andika | Makumi au mamia (juu zaidi kwa maeneo ya juu zaidi) |
| Calldata | Wito wa kazi | Soma Pekee | Haiwezi kutumika kama aina ya urejeshaji, tu aina ya kigezo cha kazi |
| Ghala | Hadi kubadilishwa | Soma/Andika | Juu (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 yoteKazi 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 haihitajiki5 * 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 angalau12 * `kiasi`.13 */14 function transferFrom(address sender, address recipient, uint256 amount) public virtual15 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 Alice | Nonce ya Alice | Muamala wa Bill | Nonce ya Bill | Posho ya Bill | Jumla ya Mapato ya Bill kutoka kwa Alice |
|---|---|---|---|---|---|
| approve(Bill, 5) | 10 | 5 | 0 | ||
| transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | ||
| approve(Bill, 10) | 11 | 10 | 5 | ||
| transferFrom(Alice, Bill, 10) | 10,124 | 0 | 15 |
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 Alice | Nonce ya Alice | Muamala wa Bill | Nonce ya Bill | Posho ya Bill | Jumla ya Mapato ya Bill kutoka kwa Alice |
|---|---|---|---|---|---|
| approve(Bill, 5) | 10 | 5 | 0 | ||
| transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | ||
| increaseAllowance(Bill, 5) | 11 | 0+5 = 5 | 5 | ||
| transferFrom(Alice, Bill, 5) | 10,124 | 0 | 10 |
B:
| Muamala wa Alice | Nonce ya Alice | Muamala wa Bill | Nonce ya Bill | Posho ya Bill | Jumla ya Mapato ya Bill kutoka kwa Alice |
|---|---|---|---|---|---|
| approve(Bill, 5) | 10 | 5 | 0 | ||
| increaseAllowance(Bill, 5) | 11 | 5+5 = 10 | 0 | ||
| transferFrom(Alice, Bill, 10) | 10,124 | 0 | 10 |
1 /**2 * @dev Huongeza kiatomiki posho iliyotolewa kwa `mtumiaji` na mpigaji.3 *4 * Hii ni mbadala ya {approve} ambayo inaweza kutumika kama upunguzaji wa5 * 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 yoteKazi ya a.add(b) ni nyongeza salama. Katika kesi isiyowezekana kwamba a+b>=2^256 haizunguki
kama nyongeza ya kawaida inavyofanya.
12 /**3 * @dev Hupunguza kiatomiki posho iliyotolewa kwa `mtumiaji` na mpigaji.4 *5 * Hii ni mbadala ya {approve} ambayo inaweza kutumika kama upunguzaji wa6 * 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 angalau14 * `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 yoteKazi 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 kwa5 * 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 yoteKazi 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);2Kuna njia mbili za kutumia mkataba huu:
- Itumie kama kiolezo cha msimbo wako mwenyewe
- 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`, akiongeza2 * 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 yoteHakikisha unasasisha _totalSupply wakati jumla ya idadi ya tokeni inapobadilika.
1 /**2 * @dev Huharibu tokeni za `kiasi` kutoka kwa `akaunti`, ikipunguza3 * 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");1415 _beforeTokenTransfer(account, address(0), amount);1617 _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 yoteKazi 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 kwa5 * 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");1718 _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 }3Rekebisha Kigezo cha Desimali
123 /**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 mingi7 * inayoingiliana na mikataba ya tokeni haitatarajia8 * {decimals} kubadilika, na inaweza kufanya kazi kimakosa ikibadilika.9 */10 function _setupDecimals(uint8 decimals_) internal {11 _decimals = decimals_;12 }Onyesha yoteKazi 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
12 /**3 * @dev Ndoano inayoitwa kabla ya uhamisho wowote wa tokeni. Hii inajumuisha4 * 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 yoteHii 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
uint256huzunguka. 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 naapprove,transferFrom,increaseAllowance, nadecreaseAllowance - 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
