Ruka hadi kwenye maudhui makuu

Mwongozo wa mkataba wa daraja la kawaida la Optimism

solidity
bridge
layer 2
Wastani
Ori Pomerantz
30 Machi 2022
31 soma ndani ya dakika

Optimism (opens in a new tab) ni Optimistic Rollup. Optimistic rollups zinaweza kuchakata miamala kwa bei ya chini zaidi kuliko Mtandao Mkuu wa Ethereum (pia inajulikana kama safu 1 au L1) kwa sababu miamala inachakatwa na nodi chache tu, badala ya kila nodi kwenye mtandao. Wakati huo huo, data yote imeandikwa kwa L1 ili kila kitu kiweze kuthibitishwa na kujengwa upya kwa uadilifu wote na dhamana ya upatikanaji wa Mtandao Mkuu.

Ili kutumia rasilimali za L1 kwenye Optimism (au L2 nyingine yoyote), rasilimali zinahitaji kuvushwa kwa daraja. Njia moja ya kufanikisha hili ni kwa watumiaji kufunga rasilimali (ETH na tokeni za ERC-20 ndizo za kawaida zaidi) kwenye L1, na kupokea rasilimali sawa za kutumia kwenye L2. Hatimaye, yeyote atakayeishia nazo anaweza kutaka kuzivusha kwa daraja kurudi L1. Wakati wa kufanya hivi, rasilimali huchomwa kwenye L2 na kisha kurejeshwa kwa mtumiaji kwenye L1.

Hivi ndivyo daraja la kawaida la Optimism (opens in a new tab) linavyofanya kazi. Katika makala haya tunapitia msimbo chanzo wa daraja hilo ili kuona jinsi linavyofanya kazi na kulichunguza kama mfano wa msimbo wa Solidity ulioandikwa vizuri.

Mtiririko wa udhibiti

Daraja lina mitiririko mikuu miwili:

  • Kuweka (kutoka L1 hadi L2)
  • Kutoa (kutoka L2 hadi L1)

Mtiririko wa uwekaji

Safu 1

  1. Ikiwa unaweka ERC-20, mwekaji anatoa ruhusa kwa daraja kutumia kiasi kinachowekwa
  2. Mwekaji anapiga simu daraja la L1 (depositERC20, depositERC20To, depositETH, au depositETHTo)
  3. Daraja la L1 linachukua umiliki wa rasilimali iliyovushwa kwa daraja
    • ETH: Rasilimali inahamishwa na mwekaji kama sehemu ya simu
    • ERC-20: Rasilimali inahamishwa na daraja kwake yenyewe kwa kutumia ruhusa iliyotolewa na mwekaji
  4. Daraja la L1 linatumia utaratibu wa ujumbe wa vikoa tofauti kupiga simu finalizeDeposit kwenye daraja la L2

Safu 2

  1. Daraja la L2 huhakikisha simu kwa finalizeDeposit ni halali:
    • Imetoka kwenye mkataba wa ujumbe wa vikoa tofauti
    • Hapo awali ilitoka kwenye daraja kwenye L1
  2. Daraja la L2 huangalia ikiwa mkataba wa tokeni ya ERC-20 kwenye L2 ni sahihi:
  3. Ikiwa mkataba wa L2 ni sahihi, piga simu ili kutoa idadi ifaayo ya tokeni kwa anwani ifaayo. Ikiwa sivyo, anza mchakato wa kutoa ili kumruhusu mtumiaji kudai tokeni kwenye L1.

Mtiririko wa utoaji

Safu 2

  1. Mtoaji anapiga simu daraja la L2 (withdraw au withdrawTo)
  2. Daraja la L2 huchoma idadi ifaayo ya tokeni za msg.sender
  3. Daraja la L2 hutumia utaratibu wa ujumbe wa vikoa tofauti kupiga simu finalizeETHWithdrawal au finalizeERC20Withdrawal kwenye daraja la L1

Safu 1

  1. Daraja la L1 huhakikisha simu kwa finalizeETHWithdrawal au finalizeERC20Withdrawal ni halali:
    • Imetoka kwenye utaratibu wa ujumbe wa vikoa tofauti
    • Hapo awali ilitoka kwenye daraja kwenye L2
  2. Daraja la L1 huhamisha rasilimali ifaayo (ETH au ERC-20) kwa anwani ifaayo

Msimbo wa Safu 1

Huu ndio msimbo unaoendeshwa kwenye L1, Mtandao Mkuu wa Ethereum.

IL1ERC20Bridge

Kiolesura hiki kimefafanuliwa hapa (opens in a new tab). Inajumuisha kazi na ufafanuzi unaohitajika kwa kuvusha tokeni za ERC-20 kwa daraja.

// SPDX-License-Identifier: MIT

Sehemu kubwa ya msimbo wa Optimism hutolewa chini ya leseni ya MIT (opens in a new tab).

pragma solidity >0.5.0 <0.9.0;

Wakati wa kuandika toleo la hivi karibuni la Solidity ni 0.8.12. Hadi toleo la 0.9.0 litolewe, hatujui kama msimbo huu unaendana nalo au la.

Katika istilahi za daraja la Optimism kuweka kunamaanisha uhamisho kutoka L1 hadi L2, na kutoa kunamaanisha uhamisho kutoka L2 hadi L1.

        anwani iliyoorodheshwa _l1Token,
        anwani iliyoorodheshwa _l2Token,

Katika hali nyingi anwani ya ERC-20 kwenye L1 si sawa na anwani ya ERC-20 inayolingana kwenye L2. Unaweza kuona orodha ya anwani za tokeni hapa (opens in a new tab). Anwani yenye chainId 1 iko kwenye L1 (Mtandao Mkuu) na anwani yenye chainId 10 iko kwenye L2 (Optimism). Thamani zingine mbili za chainId ni za mtandao wa majaribio wa Kovan (42) na mtandao wa majaribio wa Optimistic Kovan (69).

        anwani iliyoorodheshwa _from,
        anwani _to,
        uint256 _amount,
        bytes _data
    );

Inawezekana kuongeza maelezo kwenye uhamisho, ambapo huongezwa kwenye matukio yanayoyaripoti.

    event ERC20WithdrawalFinalized(
        anwani iliyoorodheshwa _l1Token,
        anwani iliyoorodheshwa _l2Token,
        anwani iliyoorodheshwa _from,
        anwani _to,
        uint256 _amount,
        bytes _data
    );

Mkataba huo huo wa daraja hushughulikia uhamisho katika pande zote mbili. Kwa upande wa daraja la L1, hii inamaanisha uanzishaji wa uwekaji na ukamilishaji wa utoaji.

Kazi hii haihitajiki sana, kwa sababu kwenye L2 ni mkataba uliotumwa awali, kwa hivyo huwa kwenye anwani 0x4200000000000000000000000000000000000010. Iko hapa kwa ulinganifu na daraja la L2, kwa sababu anwani ya daraja la L1 si rahisi kujua.

Kigezo cha _l2Gas ni kiasi cha gesi ya L2 ambacho muamala unaruhusiwa kutumia. Hadi kikomo fulani (cha juu), hii ni bure (opens in a new tab), kwa hivyo isipokuwa mkataba wa ERC-20 unafanya jambo la ajabu sana wakati wa kutoa, haipaswi kuwa suala. Kazi hii inashughulikia hali ya kawaida, ambapo mtumiaji huvusha rasilimali kwa daraja kwenda anwani ileile kwenye mnyororo wa bloku tofauti.

Kazi hii ni karibu sawa na depositERC20, lakini inakuwezesha kutuma ERC-20 kwa anwani tofauti.

Utoaji (na ujumbe mwingine kutoka L2 hadi L1) katika Optimism ni mchakato wa hatua mbili:

  1. Muamala wa kuanzisha kwenye L2.
  2. Muamala wa ukamilishaji au kudai kwenye L1. Muamala huu unahitaji kufanyika baada ya kipindi cha changamoto ya makosa (opens in a new tab) kwa muamala wa L2 kuisha.

IL1StandardBridge

Kiolesura hiki kimefafanuliwa hapa (opens in a new tab). Faili hili lina ufafanuzi wa matukio na kazi kwa ETH. Ufafanuzi huu unafanana sana na ule uliofafanuliwa katika IL1ERC20Bridge hapo juu kwa ERC-20.

Kiolesura cha daraja kimegawanywa kati ya faili mbili kwa sababu baadhi ya tokeni za ERC-20 zinahitaji usindikaji maalum na haziwezi kushughulikiwa na daraja la kawaida. Kwa njia hii daraja maalum linaloshughulikia tokeni kama hiyo linaweza kutekeleza IL1ERC20Bridge na sio lazima pia kuvusha ETH kwa daraja.

Tukio hili ni karibu sawa na toleo la ERC-20 (ERC20DepositInitiated), isipokuwa bila anwani za tokeni za L1 na L2. Hali kadhalika kwa matukio mengine na kazi.

CrossDomainEnabled

Mkataba huu (opens in a new tab) unarithiwa na madaraja yote mawili (L1 na L2) kutuma ujumbe kwa safu nyingine.

// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.9.0;

/* Interface Imports */
import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol";

Kiolesura hiki (opens in a new tab) kinaeleza mkataba jinsi ya kutuma ujumbe kwa safu nyingine, kwa kutumia mtumaji ujumbe wa vikoa tofauti. Mtumaji ujumbe huu wa vikoa tofauti ni mfumo mwingine kabisa, na unastahili makala yake, ambayo natumai kuandika katika siku zijazo.

Kigezo kimoja ambacho mkataba unahitaji kujua, anwani ya mtumaji ujumbe wa vikoa tofauti kwenye safu hii. Kigezo hiki kinawekwa mara moja, katika kijenzi, na hakibadiliki kamwe.

Ujumbe wa vikoa tofauti unaweza kufikiwa na mkataba wowote kwenye mnyororo wa bloku ambapo unaendeshwa (iwe Mtandao Mkuu wa Ethereum au Optimism). Lakini tunahitaji daraja kwa kila upande tu kuamini ujumbe fulani ikiwa unatoka kwenye daraja kwa upande mwingine.

        require(
            msg.sender == address(getCrossDomainMessenger()),
            "OVM_XCHAIN: mkataba wa mtumaji ujumbe haujathibitishwa"
        );

Ujumbe tu kutoka kwa mtumaji ujumbe wa vikoa tofauti unaofaa (messenger, kama unavyoona hapa chini) unaweza kuaminiwa.


        require(
            getCrossDomainMessenger().xDomainMessageSender() == _sourceDomainAccount,
            "OVM_XCHAIN: mtumaji asiye sahihi wa ujumbe wa kikoa tofauti"
        );

Njia ambayo mtumaji ujumbe wa vikoa tofauti anatoa anwani iliyotuma ujumbe na safu nyingine ni kazi ya .xDomainMessageSender() (opens in a new tab). Muda wote inapoitwa katika muamala ulioanzishwa na ujumbe inaweza kutoa habari hii.

Tunahitaji kuhakikisha kuwa ujumbe tuliopokea ulitoka kwenye daraja lingine.

Kazi hii inarejesha mtumaji ujumbe wa vikoa tofauti. Tunatumia kazi badala ya kigezo messenger kuruhusu mikataba inayorithi kutoka kwa hii kutumia algoriti kubainisha ni mtumaji ujumbe gani wa vikoa tofauti wa kutumia.

Hatimaye, kazi inayotuma ujumbe kwa safu nyingine.

    ) internal {
        // slither-disable-next-line reentrancy-events, reentrancy-benign

Slither (opens in a new tab) ni kichanganuzi tuli ambacho Optimism huendesha kwenye kila mkataba kutafuta udhaifu na matatizo mengine yanayoweza kutokea. Katika kesi hii, mstari unaofuata unasababisha udhaifu mbili:

  1. Matukio ya Kuingia Tena (opens in a new tab)
  2. Kuingia tena kusiko na madhara (opens in a new tab)
        getCrossDomainMessenger().sendMessage(_crossDomainTarget, _message, _gasLimit);
    }
}

Katika kesi hii hatuna wasiwasi kuhusu kuingia tena tunajua getCrossDomainMessenger() inarejesha anwani inayoaminika, hata kama Slither haina njia ya kujua hilo.

Mkataba wa daraja la L1

Msimbo chanzo wa mkataba huu uko hapa (opens in a new tab).

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

Violesura vinaweza kuwa sehemu ya mikataba mingine, kwa hivyo vinapaswa kuunga mkono anuwai ya matoleo ya Solidity. Lakini daraja lenyewe ni mkataba wetu, na tunaweza kuwa wakali kuhusu toleo gani la Solidity linatumia.

/* Interface Imports */
import { IL1StandardBridge } from "./IL1StandardBridge.sol";
import { IL1ERC20Bridge } from "./IL1ERC20Bridge.sol";

IL1ERC20Bridge na IL1StandardBridge zimeelezwa hapo juu.

import { IL2ERC20Bridge } from "../../L2/messaging/IL2ERC20Bridge.sol";

Kiolesura hiki (opens in a new tab) kinaturuhusu kuunda ujumbe wa kudhibiti daraja la kawaida kwenye L2.

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

Kiolesura hiki (opens in a new tab) kinaturuhusu kudhibiti mikataba ya ERC-20. Unaweza kusoma zaidi kuhusu hilo hapa.

/* Library Imports */
import { CrossDomainEnabled } from "../../libraries/bridge/CrossDomainEnabled.sol";

Kama ilivyoelezwa hapo juu, mkataba huu unatumika kwa ujumbe wa kati ya safu.

import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployAddresses.sol";

Lib_PredeployAddresses (opens in a new tab) ina anwani za mikataba ya L2 ambayo huwa na anwani sawa kila wakati. Hii inajumuisha daraja la kawaida kwenye L2.

import { Address } from "@openzeppelin/contracts/utils/Address.sol";

Huduma za Anwani za OpenZeppelin (opens in a new tab). Inatumika kutofautisha kati ya anwani za mkataba na zile za akaunti zinazomilikiwa na watu wa nje (EOA).

Kumbuka kuwa hili si suluhisho kamilifu, kwa sababu hakuna njia ya kutofautisha kati ya simu za moja kwa moja na simu zilizopigwa kutoka kwa kijenzi cha mkataba, lakini angalau hii inatuwezesha kutambua na kuzuia baadhi ya makosa ya kawaida ya watumiaji.

import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

Kiwango cha ERC-20 (opens in a new tab) kinaauni njia mbili kwa mkataba kuripoti kutofaulu:

  1. Batilisha
  2. Rejesha false

Kushughulikia kesi zote mbili kungefanya msimbo wetu kuwa mgumu zaidi, kwa hivyo badala yake tunatumia SafeERC20 ya OpenZeppelin (opens in a new tab), ambayo inahakikisha kushindwa kote kunasababisha ubatilishaji (opens in a new tab).

Mstari huu ndivyo tunavyobainisha kutumia kifuniko cha SafeERC20 kila tunapotumia kiolesura cha IERC20.


    /********************************
     * Marejeleo ya Mkataba wa Nje *
     ********************************/

    anwani public l2TokenBridge;

Anwani ya L2StandardBridge.


    // Ramani za tokeni ya L1 kwa tokeni ya L2 kwa salio la tokeni ya L1 iliyowekwa
    mapping(anwani => mapping(anwani => uint256)) public deposits;

Ramani maradufu kama hii ndiyo njia unayofafanua safu ya pande mbili isiyo na mpangilio (opens in a new tab). Thamani katika muundo huu wa data zinatambuliwa kama deposit[anwani ya tokeni ya L1][anwani ya tokeni ya L2]. Thamani chaguo-msingi ni sufuri. Seli tu zilizowekwa kwa thamani tofauti ndizo huandikwa kwenye hifadhi.


    /***************
     * Kijenzi *
     ***************/

    // Mkataba huu unaishi nyuma ya wakala, kwa hivyo vigezo vya kijenzi havitatumika.
    constructor() CrossDomainEnabled(anwani(0)) {}

Ili kutaka kuweza kusasisha mkataba huu bila kulazimika kunakili vigezo vyote kwenye hifadhi. Ili kufanya hivyo tunatumia Wakala (opens in a new tab), mkataba unaotumia delegatecall (opens in a new tab) kuhamisha simu kwa mkataba tofauti ambao anwani yake imehifadhiwa na mkataba wa wakala (unaposasisha unamwambia wakala kubadilisha anwani hiyo). Unapotumia delegatecall hifadhi inabaki kuwa hifadhi ya mkataba unaopiga simu, kwa hivyo thamani za vigezo vyote vya hali ya mkataba haziathiriwi.

Moja ya athari za muundo huu ni kwamba hifadhi ya mkataba ambao ni uliopigiwa simu wa delegatecall haitumiwi na kwa hivyo thamani za kijenzi zilizopitishwa kwake hazijalishi. Hii ndiyo sababu tunaweza kutoa thamani isiyo na maana kwa kijenzi cha CrossDomainEnabled. Pia ndiyo sababu uanzishaji hapa chini umetenganishwa na kijenzi.

Jaribio hili la Slither (opens in a new tab) linatambua kazi ambazo hazipigwi simu kutoka kwa msimbo wa mkataba na kwa hivyo zinaweza kutangazwa kuwa za nje badala ya za umma. Gharama ya gesi ya kazi za nje inaweza kuwa chini, kwa sababu zinaweza kupewa vigezo katika calldata. Kazi zilizotangazwa kuwa za umma zinapaswa kufikiwa kutoka ndani ya mkataba. Mikataba haiwezi kurekebisha calldata zao wenyewe, kwa hivyo vigezo vinapaswa kuwa kwenye kumbukumbu. Wakati kazi kama hiyo inapoitwa kutoka nje, ni muhimu kunakili calldata kwenye kumbukumbu, ambayo inagharimu gesi. Katika kesi hii kazi inaitwa mara moja tu, kwa hivyo ufanisi haujalishi kwetu.

    function initialize(anwani _l1messenger, anwani _l2TokenBridge) public {
        require(messenger == address(0), "Mkataba tayari umeanzishwa.");

Kazi ya initialize inapaswa kuitwa mara moja tu. Ikiwa anwani ya mtumaji ujumbe wa vikoa tofauti wa L1 au daraja la tokeni la L2 itabadilika, tunaunda wakala mpya na daraja jipya linaloiita. Hili haliwezekani kutokea isipokuwa mfumo mzima unaposasishwa, tukio la nadra sana.

Kumbuka kuwa kazi hii haina utaratibu wowote unaozuia nani anaweza kuiita. Hii inamaanisha kuwa kwa nadharia mshambuliaji anaweza kusubiri hadi tutumie wakala na toleo la kwanza la daraja na kisha kufanya shughuli haraka (opens in a new tab) kufikia kazi ya initialize kabla ya mtumiaji halali kufanya hivyo. Lakini kuna njia mbili za kuzuia hili:

  1. Ikiwa mikataba haitumwi moja kwa moja na EOA lakini katika muamala ambao una mkataba mwingine uwaumbe (opens in a new tab) mchakato mzima unaweza kuwa wa atomiki, na kumalizika kabla ya muamala mwingine wowote kutekelezwa.
  2. Ikiwa simu halali kwa initialize itashindwa daima inawezekana kupuuza wakala na daraja jipya lililoundwa na kuunda mpya.
        messenger = _l1messenger;
        l2TokenBridge = _l2TokenBridge;
    }

Hivi ni vigezo viwili ambavyo daraja linahitaji kujua.

Hii ndiyo sababu tulihitaji huduma za Anwani za OpenZeppelin.

Kazi hii ipo kwa madhumuni ya majaribio. Kumbuka kuwa haionekani katika ufafanuzi wa kiolesura - si kwa matumizi ya kawaida.

Kazi hizi mbili ni vifuniko vinavyozunguka _initiateETHDeposit, kazi inayoshughulikia uwekaji halisi wa ETH.

Njia ambayo ujumbe wa vikoa tofauti hufanya kazi ni kwamba mkataba lengwa unaitwa na ujumbe kama calldata yake. Mikataba ya Solidity daima hutafsiri calldata zao kulingana na vipimo vya ABI (opens in a new tab). Kazi ya Solidity abi.encodeWithSelector (opens in a new tab) huunda calldata hiyo.

            IL2ERC20Bridge.finalizeDeposit.selector,
            anwani(0),
            Lib_PredeployAddresses.OVM_ETH,
            _from,
            _to,
            msg.value,
            _data
        );

Ujumbe hapa ni kupiga simu kazi ya finalizeDeposit (opens in a new tab) na vigezo hivi:

KigezoThamaniMaana
_l1Tokenanwani(0)Thamani maalum ya kuwakilisha ETH (ambayo si tokeni ya ERC-20) kwenye L1
_l2TokenLib_PredeployAddresses.OVM_ETHMkataba wa L2 unaosimamia ETH kwenye Optimism, 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000 (mkataba huu ni kwa matumizi ya ndani ya Optimism pekee)
_kutoka_kutokaAnwani kwenye L1 inayotuma ETH
_kwa_kwaAnwani kwenye L2 inayopokea ETH
kiasimsg.valueKiasi cha wei kilichotumwa (ambacho tayari kimetumwa kwenye daraja)
_data_dataData ya ziada ya kuambatisha kwenye uwekaji
        // Tuma calldata kwenye L2
        // slither-disable-next-line reentrancy-events
        sendCrossDomainMessage(l2TokenBridge, _l2Gas, ujumbe);

Tuma ujumbe kupitia mtumaji ujumbe wa vikoa tofauti.

        // slither-disable-next-line reentrancy-events
        emit ETHDepositInitiated(_from, _to, msg.value, _data);
    }

Toa tukio la kujulisha programu yoyote iliyogatuliwa inayosikiliza kuhusu uhamisho huu.

Kazi hizi mbili ni vifuniko vinavyozunguka _initiateERC20Deposit, kazi inayoshughulikia uwekaji halisi wa ERC-20.

Kazi hii inafanana na _initiateETHDeposit hapo juu, na tofauti chache muhimu. Tofauti ya kwanza ni kwamba kazi hii inapokea anwani za tokeni na kiasi cha kuhamisha kama vigezo. Kwa upande wa ETH simu kwa daraja tayari inajumuisha uhamisho wa rasilimali kwenda akaunti ya daraja (msg.value).

        // Uwekaji unapoanzishwa kwenye L1, Daraja la L1 huhamisha fedha kwake lenyewe kwa ajili ya
        // utoaji wa siku zijazo. safeTransferFrom pia huangalia ikiwa mkataba una msimbo, kwa hivyo hii itashindwa ikiwa
        // _from ni EOA au anwani(0).
        // slither-disable-next-line reentrancy-events, reentrancy-benign
        IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount);

Uhamisho wa tokeni za ERC-20 hufuata mchakato tofauti na ETH:

  1. Mtumiaji (_from) anatoa ruhusa kwa daraja kuhamisha tokeni zinazofaa.
  2. Mtumiaji anapiga simu daraja na anwani ya mkataba wa tokeni, kiasi, n.k.
  3. Daraja linahamisha tokeni (kwake lenyewe) kama sehemu ya mchakato wa uwekaji.

Hatua ya kwanza inaweza kutokea katika muamala tofauti na mbili za mwisho. Hata hivyo, uendeshaji-wa-mbele si tatizo kwa sababu kazi mbili zinazoita _initiateERC20Deposit (depositERC20 na depositERC20To) huita kazi hii tu na msg.sender kama kigezo cha _from.

Ongeza kiasi cha tokeni zilizowekwa kwenye muundo wa data wa deposits. Kunaweza kuwa na anwani nyingi kwenye L2 zinazolingana na tokeni ileile ya L1 ERC-20, kwa hivyo haitoshi kutumia salio la daraja la tokeni ya L1 ERC-20 kufuatilia uwekaji.

Daraja la L2 hutuma ujumbe kwa mtumaji ujumbe wa vikoa tofauti wa L2 ambao husababisha mtumaji ujumbe wa vikoa tofauti wa L1 kupiga simu kazi hii (mara tu muamala unaokamilisha ujumbe (opens in a new tab) utakapowasilishwa kwenye L1, bila shaka).

    ) external onlyFromCrossDomainAccount(l2TokenBridge) {

Hakikisha kuwa huu ni ujumbe halali, unaotoka kwa mtumaji ujumbe wa vikoa tofauti na unaotokana na daraja la tokeni la L2. Kazi hii inatumika kutoa ETH kutoka kwa daraja, kwa hivyo tunapaswa kuhakikisha inaitwa tu na mpigaji simu aliyeidhinishwa.

        // slither-disable-next-line reentrancy-events
        (bool success, ) = _to.call{ value: _amount }(new bytes(0));

Njia ya kuhamisha ETH ni kumpigia simu mpokeaji na kiasi cha wei katika msg.value.

        require(success, "TransferHelper::safeTransferETH: Uhamisho wa ETH umeshindwa");

        // slither-disable-next-line reentrancy-events
        emit ETHWithdrawalFinalized(_from, _to, _amount, _data);

Toa tukio kuhusu utoaji.

Kazi hii inafanana na finalizeETHWithdrawal hapo juu, na mabadiliko muhimu kwa tokeni za ERC-20.

        deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] - _amount;

Sasisha muundo wa data wa deposits.

Kulikuwa na utekelezaji wa awali wa daraja. Tulipohamia kutoka kwa utekelezaji huo hadi huu, tulilazimika kuhamisha rasilimali zote. Tokeni za ERC-20 zinaweza tu kuhamishwa. Hata hivyo, ili kuhamisha ETH kwa mkataba unahitaji idhini ya mkataba huo, ambayo ndiyo donateETH inatupatia.

Tokeni za ERC-20 kwenye L2

Ili tokeni ya ERC-20 iingie kwenye daraja la kawaida, inahitaji kuruhusu daraja la kawaida, na tu daraja la kawaida, kutoa tokeni. Hii ni muhimu kwa sababu madaraja yanahitaji kuhakikisha kuwa idadi ya tokeni zinazozunguka kwenye Optimism ni sawa na idadi ya tokeni zilizofungwa ndani ya mkataba wa daraja la L1. Ikiwa kuna tokeni nyingi sana kwenye L2 baadhi ya watumiaji wasingeweza kuvusha rasilimali zao kwa daraja kurudi L1. Badala ya daraja linaloaminika, kimsingi tungeunda upya benki ya hifadhi ya sehemu (opens in a new tab). Ikiwa kuna tokeni nyingi sana kwenye L1, baadhi ya tokeni hizo zingebaki zimefungwa ndani ya mkataba wa daraja milele kwa sababu hakuna njia ya kuzitoa bila kuchoma tokeni za L2.

IL2StandardERC20

Kila tokeni ya ERC-20 kwenye L2 inayotumia daraja la kawaida inahitaji kutoa kiolesura hiki (opens in a new tab), ambacho kina kazi na matukio ambayo daraja la kawaida linahitaji.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

Kiolesura cha kawaida cha ERC-20 (opens in a new tab) hakijumuishi kazi za mint na burn. Njia hizo hazihitajiki na kiwango cha ERC-20 (opens in a new tab), ambacho kinaacha taratibu za kuunda na kuharibu tokeni bila kubainishwa.

import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

Kiolesura cha ERC-165 (opens in a new tab) kinatumika kubainisha kazi ambazo mkataba unatoa. Unaweza kusoma kiwango hapa (opens in a new tab).

interface IL2StandardERC20 is IERC20, IERC165 {
    function l1Token() external returns (anwani);

Kazi hii inatoa anwani ya tokeni ya L1 ambayo imevushwa kwa daraja kwenda mkataba huu. Kumbuka kuwa hatuna kazi kama hiyo kwa upande mwingine. Tunahitaji kuweza kuvusha kwa daraja tokeni yoyote ya L1, bila kujali kama msaada wa L2 ulipangwa wakati ilitekelezwa au la.


    function mint(anwani _to, uint256 _amount) external;

    function burn(anwani _from, uint256 _amount) external;

    event Mint(anwani iliyoorodheshwa _account, uint256 _amount);
    event Burn(anwani iliyoorodheshwa _account, uint256 _amount);
}

Kazi na matukio ya kutoa (kuunda) na kuchoma (kuharibu) tokeni. Daraja linapaswa kuwa chombo pekee kinachoweza kuendesha kazi hizi ili kuhakikisha idadi ya tokeni ni sahihi (sawa na idadi ya tokeni zilizofungwa kwenye L1).

L2StandardERC20

Huu ndio utekelezaji wetu wa kiolesura cha IL2StandardERC20 (opens in a new tab). Isipokuwa unahitaji aina fulani ya mantiki maalum, unapaswa kutumia hii.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

Mkataba wa OpenZeppelin ERC-20 (opens in a new tab). Optimism haiamini katika kuunda upya gurudumu, hasa wakati gurudumu limekaguliwa vizuri na linahitaji kuwa la kuaminika vya kutosha kushikilia rasilimali.

import "./IL2StandardERC20.sol";

contract L2StandardERC20 is IL2StandardERC20, ERC20 {
    anwani public l1Token;
    anwani public l2Bridge;

Hivi ni vigezo viwili vya ziada vya usanidi ambavyo tunahitaji na ERC-20 kawaida haihitaji.

Kwanza piga simu kijenzi cha mkataba tunachorithi kutoka (ERC20(_name, _symbol)) na kisha weka vigezo vyetu wenyewe.

Hivi ndivyo ERC-165 (opens in a new tab) inavyofanya kazi. Kila kiolesura ni idadi ya kazi zinazoungwa mkono, na kinatambuliwa kama au ya kipekee (opens in a new tab) ya viteuzi vya kazi vya ABI (opens in a new tab) vya kazi hizo.

Daraja la L2 linatumia ERC-165 kama ukaguzi wa usalama kuhakikisha kuwa mkataba wa ERC-20 ambao linatuma rasilimali ni IL2StandardERC20.

Kumbuka: Hakuna kinachozuia mkataba hasidi kutoa majibu ya uwongo kwa supportsInterface, kwa hivyo huu ni utaratibu wa ukaguzi wa usalama, sio utaratibu wa usalama.

Ni daraja la L2 pekee linaloruhusiwa kutoa na kuchoma rasilimali.

_mint na _burn kwa kweli zimefafanuliwa katika mkataba wa OpenZeppelin ERC-20. Mkataba huo hauwaweki wazi nje, kwa sababu masharti ya kutoa na kuchoma tokeni ni tofauti kama idadi ya njia za kutumia ERC-20.

Msimbo wa Daraja la L2

Huu ni msimbo unaoendesha daraja kwenye Optimism. Chanzo cha mkataba huu kiko hapa (opens in a new tab).

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/* Interface Imports */
import { IL1StandardBridge } from "../../L1/messaging/IL1StandardBridge.sol";
import { IL1ERC20Bridge } from "../../L1/messaging/IL1ERC20Bridge.sol";
import { IL2ERC20Bridge } from "./IL2ERC20Bridge.sol";

Kiolesura cha IL2ERC20Bridge (opens in a new tab) kinafanana sana na sawa ya L1 tuliyoona hapo juu. Kuna tofauti mbili muhimu:

  1. Kwenye L1 unaanzisha uwekaji na kukamilisha utoaji. Hapa unaanzisha utoaji na kukamilisha uwekaji.
  2. Kwenye L1 ni muhimu kutofautisha kati ya ETH na tokeni za ERC-20. Kwenye L2 tunaweza kutumia kazi zile zile kwa zote mbili kwa sababu ndani ya Optimism salio za ETH hushughulikiwa kama tokeni ya ERC-20 yenye anwani 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000 (opens in a new tab).

Fuatilia anwani ya daraja la L1. Kumbuka kuwa tofauti na sawa ya L1, hapa tunahitaji kigezo hiki. Anwani ya daraja la L1 haijulikani mapema.

Kazi hizi mbili zinaanzisha utoaji. Kumbuka kuwa hakuna haja ya kubainisha anwani ya tokeni ya L1. Tokeni za L2 zinatarajiwa kutuambia anwani sawa ya L1.

Kumbuka kuwa hatutegemei kigezo cha _from bali msg.sender ambayo ni ngumu zaidi kughushi (haiwezekani, kwa kadiri ninavyojua).


        // Jenga calldata kwa l1TokenBridge.finalizeERC20Withdrawal(_to, _amount)
        // slither-disable-next-line reentrancy-events
        anwani l1Token = IL2StandardERC20(_l2Token).l1Token();
        bytes memory message;

        if (_l2Token == Lib_PredeployAddresses.OVM_ETH) {

Kwenye L1 ni muhimu kutofautisha kati ya ETH na ERC-20.

Kazi hii inaitwa na L1StandardBridge.

    ) external virtual onlyFromCrossDomainAccount(l1TokenBridge) {

Hakikisha chanzo cha ujumbe ni halali. Hii ni muhimu kwa sababu kazi hii huita _mint na inaweza kutumika kutoa tokeni ambazo hazifunikwa na tokeni ambazo daraja linamiliki kwenye L1.

        // Angalia tokeni lengwa inatii na
        // hakikisha tokeni iliyowekwa kwenye L1 inalingana na uwakilishi wa tokeni iliyowekwa ya L2 hapa
        if (
            // slither-disable-next-line reentrancy-events
            ERC165Checker.supportsInterface(_l2Token, 0x1d1d8b63) &&
            _l1Token == IL2StandardERC20(_l2Token).l1Token()

Ukaguzi wa usalama:

  1. Kiolesura sahihi kinaauniwa
  2. Anwani ya L1 ya mkataba wa L2 ERC-20 inalingana na chanzo cha L1 cha tokeni
        ) {
            // Uwekaji unapokamilishwa, tunaweka kiasi sawa cha
            // tokeni kwenye akaunti kwenye L2.
            // slither-disable-next-line reentrancy-events
            IL2StandardERC20(_l2Token).mint(_to, _amount);
            // slither-disable-next-line reentrancy-events
            emit DepositFinalized(_l1Token, _l2Token, _from, _to, _amount, _data);

Ikiwa ukaguzi wa usalama utapita, kamilisha uwekaji:

  1. Toa tokeni
  2. Toa tukio linalofaa

Ikiwa mtumiaji alifanya kosa linaloweza kugunduliwa kwa kutumia anwani isiyo sahihi ya tokeni ya L2, tunataka kughairi uwekaji na kurudisha tokeni kwenye L1. Njia pekee tunayoweza kufanya hivi kutoka L2 ni kutuma ujumbe ambao utalazimika kusubiri kipindi cha changamoto ya makosa, lakini hiyo ni bora zaidi kwa mtumiaji kuliko kupoteza tokeni kabisa.

Hitimisho

Daraja la kawaida ndio utaratibu unaobadilika zaidi kwa uhamisho wa rasilimali. Hata hivyo, kwa sababu ni ya jumla sana si rahisi kila wakati kutumia utaratibu huu. Hasa kwa utoaji, watumiaji wengi wanapendelea kutumia madaraja ya watu wengine (opens in a new tab) ambayo hayasubiri kipindi cha changamoto na hayahitaji uthibitisho wa Merkle kukamilisha utoaji.

Madaraja haya kwa kawaida hufanya kazi kwa kuwa na rasilimali kwenye L1, ambazo hutoa mara moja kwa ada ndogo (mara nyingi chini ya gharama ya gesi kwa utoaji wa daraja la kawaida). Wakati daraja (au watu wanaoliendesha) linapotarajia kupungukiwa na rasilimali za L1 linahamisha rasilimali za kutosha kutoka L2. Kwa kuwa haya ni utoaji mkubwa sana, gharama ya utoaji inapunguzwa kwa kiasi kikubwa na ni asilimia ndogo zaidi.

Tunatumai makala hii imekusaidia kuelewa zaidi kuhusu jinsi safu 2 inavyofanya kazi, na jinsi ya kuandika msimbo wa Solidity ulio wazi na salama.

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

Ukurasa ulisasishwa mwisho: 3 Aprili 2026

Je, mafunzo haya yalikuwa ya msaada?