ఆప్టిమిజం స్టాండర్డ్ బ్రిడ్జ్ కాంట్రాక్ట్ విశ్లేషణ
Optimismopens in a new tab అనేది ఒక Optimistic Rollup. ఆప్టిమిస్టిక్ రోలప్లు ఇథిరియం మెయిన్నెట్ (లేయర్ 1 లేదా L1 అని కూడా పిలుస్తారు) కంటే చాలా తక్కువ ధరకు లావాదేవీలను ప్రాసెస్ చేయగలవు, ఎందుకంటే లావాదేవీలు నెట్వర్క్లోని ప్రతి నోడ్ ద్వారా కాకుండా, కొన్ని నోడ్ల ద్వారా మాత్రమే ప్రాసెస్ చేయబడతాయి. అదే సమయంలో, డేటా మొత్తం L1కి వ్రాయబడుతుంది, కాబట్టి మెయిన్నెట్ యొక్క అన్ని సమగ్రత మరియు లభ్యత హామీలతో ప్రతిదీ నిరూపించబడుతుంది మరియు పునర్నిర్మించబడుతుంది.
Optimism (లేదా ఏదైనా ఇతర L2)లో L1 ఆస్తులను ఉపయోగించడానికి, ఆస్తులను బ్రిడ్జ్ చేయాలి. దీనిని సాధించడానికి ఒక మార్గం ఏమిటంటే, వినియోగదారులు L1పై ఆస్తులను (ETH మరియు ERC-20 టోకెన్లు అత్యంత సాధారణమైనవి) లాక్ చేసి, L2లో ఉపయోగించడానికి సమానమైన ఆస్తులను స్వీకరించడం. చివరికి, వాటిని పొందిన ఎవరైనా వాటిని తిరిగి L1కి బ్రిడ్జ్ చేయాలనుకోవచ్చు. ఇలా చేస్తున్నప్పుడు, ఆస్తులు L2లో బర్న్ చేయబడతాయి మరియు ఆ తర్వాత L1లో వినియోగదారునికి తిరిగి విడుదల చేయబడతాయి.
Optimism స్టాండర్డ్ బ్రిడ్జ్opens in a new tab ఈ విధంగా పనిచేస్తుంది. ఈ వ్యాసంలో మేము ఆ బ్రిడ్జ్ ఎలా పనిచేస్తుందో చూడటానికి దాని సోర్స్ కోడ్ను పరిశీలిస్తాము మరియు దానిని బాగా వ్రాసిన Solidity కోడ్కు ఉదాహరణగా అధ్యయనం చేస్తాము.
కంట్రోల్ ఫ్లోస్
బ్రిడ్జ్కు రెండు ప్రధాన ఫ్లోలు ఉన్నాయి:
- డిపాజిట్ (L1 నుండి L2కి)
- విత్డ్రాయల్ (L2 నుండి L1కి)
డిపాజిట్ ఫ్లో
లేయర్ 1
- ERC-20ని డిపాజిట్ చేస్తుంటే, డిపాజిటర్ డిపాజిట్ చేయబడుతున్న మొత్తాన్ని ఖర్చు చేయడానికి బ్రిడ్జ్కు అనుమతి ఇస్తాడు
- డిపాజిటర్ L1 బ్రిడ్జ్ని పిలుస్తాడు (
depositERC20,depositERC20To,depositETH, లేదాdepositETHTo) - L1 బ్రిడ్జ్ బ్రిడ్జ్ చేయబడిన ఆస్తిని స్వాధీనం చేసుకుంటుంది
- ETH: కాల్లో భాగంగా డిపాజిటర్ ద్వారా ఆస్తి బదిలీ చేయబడుతుంది
- ERC-20: డిపాజిటర్ అందించిన అనుమతిని ఉపయోగించి బ్రిడ్జ్ ద్వారా ఆస్తి దానికి బదిలీ చేయబడుతుంది
- L1 బ్రిడ్జ్ క్రాస్-డొమైన్ మెసేజ్ మెకానిజంను ఉపయోగించి L2 బ్రిడ్జ్పై
finalizeDepositని పిలుస్తుంది
లేయర్ 2
- L2 బ్రిడ్జ్
finalizeDepositకి వచ్చిన కాల్ చట్టబద్ధమైనదని ధృవీకరిస్తుంది:- క్రాస్ డొమైన్ మెసేజ్ కాంట్రాక్ట్ నుండి వచ్చింది
- వాస్తవానికి L1లోని బ్రిడ్జ్ నుండి వచ్చింది
- L2 బ్రిడ్జ్ L2లోని ERC-20 టోకెన్ కాంట్రాక్ట్ సరైనదేనా అని తనిఖీ చేస్తుంది:
- L2 కాంట్రాక్ట్ దాని L1 ప్రతిరూపం L1 నుండి టోకెన్లు వచ్చిన దానితో సమానమని నివేదిస్తుంది
- L2 కాంట్రాక్ట్ అది సరైన ఇంటర్ఫేస్కు మద్దతు ఇస్తుందని నివేదిస్తుంది (ERC-165ని ఉపయోగించిopens in a new tab).
- L2 కాంట్రాక్ట్ సరైనది అయితే, తగిన చిరునామాకు తగినన్ని టోకెన్లను ముద్రించడానికి దానిని కాల్ చేయండి. కాకపోతే, L1పై టోకెన్లను క్లెయిమ్ చేయడానికి వినియోగదారుని అనుమతించడానికి విత్డ్రాయల్ ప్రక్రియను ప్రారంభించండి.
విత్డ్రాయల్ ఫ్లో
లేయర్ 2
- విత్డ్రాయర్ L2 బ్రిడ్జ్ని పిలుస్తాడు (
withdrawలేదాwithdrawTo) - L2 బ్రిడ్జ్
msg.senderకి చెందిన తగిన సంఖ్యలో టోకెన్లను బర్న్ చేస్తుంది - L2 బ్రిడ్జ్ క్రాస్-డొమైన్ మెసేజ్ మెకానిజంను ఉపయోగించి L1 బ్రిడ్జ్పై
finalizeETHWithdrawalలేదాfinalizeERC20Withdrawalని పిలుస్తుంది
లేయర్ 1
- L1 బ్రిడ్జ్
finalizeETHWithdrawalలేదాfinalizeERC20Withdrawalకి వచ్చిన కాల్ చట్టబద్ధమైనదని ధృవీకరిస్తుంది:- క్రాస్ డొమైన్ మెసేజ్ మెకానిజం నుండి వచ్చింది
- వాస్తవానికి L2లోని బ్రిడ్జ్ నుండి వచ్చింది
- L1 బ్రిడ్జ్ తగిన ఆస్తిని (ETH లేదా ERC-20) తగిన చిరునామాకు బదిలీ చేస్తుంది
లేయర్ 1 కోడ్
ఇది L1, ఇథిరియం మెయిన్నెట్పై నడిచే కోడ్.
IL1ERC20Bridge
ఈ ఇంటర్ఫేస్ ఇక్కడ నిర్వచించబడిందిopens in a new tab. ఇందులో ERC-20 టోకెన్లను బ్రిడ్జ్ చేయడానికి అవసరమైన ఫంక్షన్లు మరియు నిర్వచనాలు ఉంటాయి.
1// SPDX-License-Identifier: MITఆప్టిమిజం యొక్క చాలా కోడ్ MIT లైసెన్సు క్రింద విడుదల చేయబడిందిopens in a new tab.
1pragma solidity >0.5.0 <0.9.0;ఈ రచన సమయంలో Solidity యొక్క తాజా వెర్షన్ 0.8.12. వెర్షన్ 0.9.0 విడుదలయ్యే వరకు, ఈ కోడ్ దానికి అనుకూలంగా ఉందో లేదో మాకు తెలియదు.
1/**2 * @title IL1ERC20Bridge3 */4interface IL1ERC20Bridge {5 /**********6 * Events *7 **********/89 event ERC20DepositInitiated(అన్నీ చూపించుఆప్టిమిజం బ్రిడ్జ్ పరిభాషలో deposit అంటే L1 నుండి L2కి బదిలీ, మరియు withdrawal అంటే L2 నుండి L1కి బదిలీ.
1 address indexed _l1Token,2 address indexed _l2Token,చాలా సందర్భాలలో L1లోని ERC-20 యొక్క చిరునామా L2లోని సమానమైన ERC-20 చిరునామాతో సమానంగా ఉండదు.
మీరు టోకెన్ చిరునామాల జాబితాను ఇక్కడ చూడవచ్చుopens in a new tab.
chainId 1 ఉన్న చిరునామా L1 (మెయిన్నెట్) పై ఉంది మరియు chainId 10 ఉన్న చిరునామా L2 (ఆప్టిమిజం) పై ఉంది.
మిగిలిన రెండు chainId విలువలు కోవన్ టెస్ట్ నెట్వర్క్ (42) మరియు ఆప్టిమిస్టిక్ కోవన్ టెస్ట్ నెట్వర్క్ (69) కోసం ఉన్నాయి.
1 address indexed _from,2 address _to,3 uint256 _amount,4 bytes _data5 );బదిలీలకు గమనికలను జోడించడం సాధ్యమే, ఈ సందర్భంలో అవి వాటిని నివేదించే ఈవెంట్లకు జోడించబడతాయి.
1 event ERC20WithdrawalFinalized(2 address indexed _l1Token,3 address indexed _l2Token,4 address indexed _from,5 address _to,6 uint256 _amount,7 bytes _data8 );అదే బ్రిడ్జ్ కాంట్రాక్ట్ రెండు దిశలలో బదిలీలను నిర్వహిస్తుంది. L1 బ్రిడ్జ్ విషయంలో, దీని అర్థం డిపాజిట్లను ప్రారంభించడం మరియు విత్డ్రాయల్లను ఖరారు చేయడం.
12 /********************3 * Public Functions *4 ********************/56 /**7 * @dev get the address of the corresponding L2 bridge contract.8 * @return Address of the corresponding L2 bridge contract.9 */10 function l2TokenBridge() external returns (address);అన్నీ చూపించుఈ ఫంక్షన్ నిజంగా అవసరం లేదు, ఎందుకంటే L2లో ఇది ముందుగా డిప్లాయ్ చేయబడిన కాంట్రాక్ట్, కాబట్టి ఇది ఎల్లప్పుడూ 0x4200000000000000000000000000000000000010 చిరునామాలో ఉంటుంది.
ఇది L2 బ్రిడ్జ్తో సౌష్టవం కోసం ఇక్కడ ఉంది, ఎందుకంటే L1 బ్రిడ్జ్ యొక్క చిరునామాను తెలుసుకోవడం చాలా సులభం కాదు.
1 /**2 * @dev deposit an amount of the ERC20 to the caller's balance on L2.3 * @param _l1Token Address of the L1 ERC20 we are depositing4 * @param _l2Token Address of the L1 respective L2 ERC205 * @param _amount Amount of the ERC20 to deposit6 * @param _l2Gas Gas limit required to complete the deposit on L2.7 * @param _data Optional data to forward to L2. This data is provided8 * solely as a convenience for external contracts. Aside from enforcing a maximum9 * length, these contracts provide no guarantees about its content.10 */11 function depositERC20(12 address _l1Token,13 address _l2Token,14 uint256 _amount,15 uint32 _l2Gas,16 bytes calldata _data17 ) external;అన్నీ చూపించు_l2Gas పరామితి లావాదేవీ ఖర్చు చేయడానికి అనుమతించబడిన L2 గ్యాస్ మొత్తం.
ఒక నిర్దిష్ట (అధిక) పరిమితి వరకు, ఇది ఉచితంopens in a new tab, కాబట్టి ERC-20 కాంట్రాక్ట్ మింటింగ్ చేసేటప్పుడు నిజంగా వింతగా ఏమీ చేయకపోతే, అది ఒక సమస్య కాకూడదు.
ఈ ఫంక్షన్ ఒక వినియోగదారు వేరొక బ్లాక్చైన్లోని అదే చిరునామాకు ఆస్తులను బ్రిడ్జ్ చేసే సాధారణ దృశ్యాన్ని చూసుకుంటుంది.
1 /**2 * @dev deposit an amount of ERC20 to a recipient's balance on L2.3 * @param _l1Token Address of the L1 ERC20 we are depositing4 * @param _l2Token Address of the L1 respective L2 ERC205 * @param _to L2 address to credit the withdrawal to.6 * @param _amount Amount of the ERC20 to deposit.7 * @param _l2Gas Gas limit required to complete the deposit on L2.8 * @param _data Optional data to forward to L2. This data is provided9 * solely as a convenience for external contracts. Aside from enforcing a maximum10 * length, these contracts provide no guarantees about its content.11 */12 function depositERC20To(13 address _l1Token,14 address _l2Token,15 address _to,16 uint256 _amount,17 uint32 _l2Gas,18 bytes calldata _data19 ) external;అన్నీ చూపించుఈ ఫంక్షన్ దాదాపుగా depositERC20తో సమానంగా ఉంటుంది, కానీ ఇది వేరొక చిరునామాకు ERC-20ని పంపడానికి మిమ్మల్ని అనుమతిస్తుంది.
1 /*************************2 * Cross-chain Functions *3 *************************/45 /**6 * @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the7 * L1 ERC20 token.8 * This call will fail if the initialized withdrawal from L2 has not been finalized.9 *10 * @param _l1Token Address of L1 token to finalizeWithdrawal for.11 * @param _l2Token Address of L2 token where withdrawal was initiated.12 * @param _from L2 address initiating the transfer.13 * @param _to L1 address to credit the withdrawal to.14 * @param _amount Amount of the ERC20 to deposit.15 * @param _data Data provided by the sender on L2. This data is provided16 * solely as a convenience for external contracts. Aside from enforcing a maximum17 * length, these contracts provide no guarantees about its content.18 */19 function finalizeERC20Withdrawal(20 address _l1Token,21 address _l2Token,22 address _from,23 address _to,24 uint256 _amount,25 bytes calldata _data26 ) external;27}అన్నీ చూపించుఆప్టిమిజంలో విత్డ్రాయల్స్ (మరియు L2 నుండి L1కి ఇతర సందేశాలు) రెండు దశల ప్రక్రియ:
- L2లో ఒక ప్రారంభ లావాదేవీ.
- L1లో ఒక ఖరారు చేసే లేదా క్లెయిమ్ చేసే లావాదేవీ. L2 లావాదేవీ కోసం ఫాల్ట్ ఛాలెంజ్ వ్యవధిopens in a new tab ముగిసిన తర్వాత ఈ లావాదేవీ జరగాలి.
IL1StandardBridge
ఈ ఇంటర్ఫేస్ ఇక్కడ నిర్వచించబడిందిopens in a new tab.
ఈ ఫైల్లో ETH కోసం ఈవెంట్ మరియు ఫంక్షన్ నిర్వచనాలు ఉన్నాయి.
ఈ నిర్వచనాలు పైన ERC-20 కోసం IL1ERC20Bridgeలో నిర్వచించిన వాటికి చాలా పోలి ఉంటాయి.
కొన్ని ERC-20 టోకెన్లకు కస్టమ్ ప్రాసెసింగ్ అవసరం మరియు స్టాండర్డ్ బ్రిడ్జ్ ద్వారా నిర్వహించబడనందున బ్రిడ్జ్ ఇంటర్ఫేస్ రెండు ఫైల్ల మధ్య విభజించబడింది.
ఈ విధంగా అలాంటి టోకెన్ను నిర్వహించే కస్టమ్ బ్రిడ్జ్ IL1ERC20Bridgeని అమలు చేయగలదు మరియు ETHని కూడా బ్రిడ్జ్ చేయవలసిన అవసరం లేదు.
1// SPDX-License-Identifier: MIT2pragma solidity >0.5.0 <0.9.0;34import "./IL1ERC20Bridge.sol";56/**7 * @title IL1StandardBridge8 */9interface IL1StandardBridge is IL1ERC20Bridge {10 /**********11 * Events *12 **********/13 event ETHDepositInitiated(14 address indexed _from,15 address indexed _to,16 uint256 _amount,17 bytes _data18 );అన్నీ చూపించుఈ ఈవెంట్ ERC-20 వెర్షన్ (ERC20DepositInitiated)తో దాదాపు సమానంగా ఉంటుంది, L1 మరియు L2 టోకెన్ చిరునామాలు మినహా.
ఇతర ఈవెంట్లు మరియు ఫంక్షన్లకు కూడా ఇదే వర్తిస్తుంది.
1 event ETHWithdrawalFinalized(2 .3 .4 .5 );67 /********************8 * Public Functions *9 ********************/1011 /**12 * @dev Deposit an amount of the ETH to the caller's balance on L2.13 .14 .15 .16 */17 function depositETH(uint32 _l2Gas, bytes calldata _data) external payable;1819 /**20 * @dev Deposit an amount of ETH to a recipient's balance on L2.21 .22 .23 .24 */25 function depositETHTo(26 address _to,27 uint32 _l2Gas,28 bytes calldata _data29 ) external payable;3031 /*************************32 * Cross-chain Functions *33 *************************/3435 /**36 * @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the37 * L1 ETH token. Since only the xDomainMessenger can call this function, it will never be called38 * before the withdrawal is finalized.39 .40 .41 .42 */43 function finalizeETHWithdrawal(44 address _from,45 address _to,46 uint256 _amount,47 bytes calldata _data48 ) external;49}అన్నీ చూపించుCrossDomainEnabled
ఈ కాంట్రాక్ట్opens in a new tab ఇతర లేయర్కు సందేశాలను పంపడానికి రెండు బ్రిడ్జ్ల (L1 మరియు L2) ద్వారా వారసత్వంగా పొందబడుతుంది.
1// SPDX-License-Identifier: MIT2pragma solidity >0.5.0 <0.9.0;34/* Interface Imports */5import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol";ఈ ఇంటర్ఫేస్opens in a new tab క్రాస్ డొమైన్ మెసెంజర్ను ఉపయోగించి ఇతర లేయర్కు సందేశాలను ఎలా పంపాలో కాంట్రాక్ట్కు చెబుతుంది. ఈ క్రాస్ డొమైన్ మెసెంజర్ పూర్తిగా వేరే సిస్టమ్, మరియు దాని స్వంత వ్యాసం అవసరం, దీనిని నేను భవిష్యత్తులో వ్రాయాలని ఆశిస్తున్నాను.
1/**2 * @title CrossDomainEnabled3 * @dev Helper contract for contracts performing cross-domain communications4 *5 * Compiler used: defined by inheriting contract6 */7contract CrossDomainEnabled {8 /*************9 * Variables *10 *************/1112 // Messenger contract used to send and receive messages from the other domain.13 address public messenger;1415 /***************16 * Constructor *17 ***************/1819 /**20 * @param _messenger Address of the CrossDomainMessenger on the current layer.21 */22 constructor(address _messenger) {23 messenger = _messenger;24 }అన్నీ చూపించుకాంట్రాక్ట్కు తెలియాల్సిన ఒక పరామితి, ఈ లేయర్లోని క్రాస్ డొమైన్ మెసెంజర్ యొక్క చిరునామా. ఈ పరామితి కన్స్ట్రక్టర్లో ఒక్కసారి సెట్ చేయబడుతుంది మరియు ఎప్పటికీ మారదు.
12 /**********************3 * Function Modifiers *4 **********************/56 /**7 * Enforces that the modified function is only callable by a specific cross-domain account.8 * @param _sourceDomainAccount The only account on the originating domain which is9 * authenticated to call this function.10 */11 modifier onlyFromCrossDomainAccount(address _sourceDomainAccount) {అన్నీ చూపించుక్రాస్ డొమైన్ మెసేజింగ్ అది నడుస్తున్న బ్లాక్చైన్లోని (ఇథిరియం మెయిన్నెట్ లేదా ఆప్టిమిజం) ఏ కాంట్రాక్ట్ ద్వారానైనా యాక్సెస్ చేయబడుతుంది. కానీ ప్రతి వైపు ఉన్న బ్రిడ్జ్ ఇతర వైపు ఉన్న బ్రిడ్జ్ నుండి వచ్చినట్లయితే మాత్రమే కొన్ని సందేశాలను విశ్వసించాలి.
1 require(2 msg.sender == address(getCrossDomainMessenger()),3 "OVM_XCHAIN: messenger contract unauthenticated"4 );తగిన క్రాస్ డొమైన్ మెసెంజర్ (messenger, మీరు క్రింద చూసినట్లుగా) నుండి వచ్చిన సందేశాలను మాత్రమే విశ్వసించవచ్చు.
12 require(3 getCrossDomainMessenger().xDomainMessageSender() == _sourceDomainAccount,4 "OVM_XCHAIN: wrong sender of cross-domain message"5 );క్రాస్ డొమైన్ మెసెంజర్ ఇతర లేయర్తో సందేశాన్ని పంపిన చిరునామాను అందించే మార్గం .xDomainMessageSender() ఫంక్షన్opens in a new tab.
సందేశం ద్వారా ప్రారంభించబడిన లావాదేవీలో ఇది పిలవబడినంత కాలం ఇది ఈ సమాచారాన్ని అందించగలదు.
మాకు వచ్చిన సందేశం ఇతర బ్రిడ్జ్ నుండి వచ్చిందని మేము నిర్ధారించుకోవాలి.
12 _;3 }45 /**********************6 * Internal Functions *7 **********************/89 /**10 * Gets the messenger, usually from storage. This function is exposed in case a child contract11 * needs to override.12 * @return The address of the cross-domain messenger contract which should be used.13 */14 function getCrossDomainMessenger() internal virtual returns (ICrossDomainMessenger) {15 return ICrossDomainMessenger(messenger);16 }అన్నీ చూపించుఈ ఫంక్షన్ క్రాస్ డొమైన్ మెసెంజర్ను తిరిగి ఇస్తుంది.
మేము messenger వేరియబుల్కు బదులుగా ఒక ఫంక్షన్ను ఉపయోగిస్తాము, దీని నుండి వారసత్వంగా వచ్చే కాంట్రాక్టులు ఏ క్రాస్ డొమైన్ మెసెంజర్ను ఉపయోగించాలో పేర్కొనడానికి ఒక అల్గారిథమ్ను ఉపయోగించడానికి అనుమతిస్తాయి.
12 /**3 * Sends a message to an account on another domain4 * @param _crossDomainTarget The intended recipient on the destination domain5 * @param _message The data to send to the target (usually calldata to a function with6 * `onlyFromCrossDomainAccount()`)7 * @param _gasLimit The gasLimit for the receipt of the message on the target domain.8 */9 function sendCrossDomainMessage(10 address _crossDomainTarget,11 uint32 _gasLimit,12 bytes memory _messageఅన్నీ చూపించుచివరగా, ఇతర లేయర్కు సందేశాన్ని పంపే ఫంక్షన్.
1 ) internal {2 // slither-disable-next-line reentrancy-events, reentrancy-benignSlitheropens in a new tab అనేది బలహీనతలు మరియు ఇతర సంభావ్య సమస్యల కోసం ప్రతి కాంట్రాక్ట్పై ఆప్టిమిజం నడిపే ఒక స్టాటిక్ అనలైజర్. ఈ సందర్భంలో, కింది లైన్ రెండు బలహీనతలను ప్రేరేపిస్తుంది:
1 getCrossDomainMessenger().sendMessage(_crossDomainTarget, _message, _gasLimit);2 }3}ఈ సందర్భంలో, getCrossDomainMessenger() ఒక విశ్వసనీయ చిరునామాను తిరిగి ఇస్తుందని మాకు తెలిసినప్పటికీ, Slitherకు అది తెలుసుకునే మార్గం లేనప్పటికీ, మేము రీఎంట్రాన్సీ గురించి ఆందోళన చెందము.
L1 బ్రిడ్జ్ కాంట్రాక్ట్
ఈ కాంట్రాక్ట్ కోసం సోర్స్ కోడ్ ఇక్కడ ఉందిopens in a new tab.
1// SPDX-License-Identifier: MIT2pragma solidity ^0.8.9;ఇంటర్ఫేస్లు ఇతర కాంట్రాక్టులలో భాగంగా ఉండవచ్చు, కాబట్టి అవి విస్తృత శ్రేణి Solidity వెర్షన్లకు మద్దతు ఇవ్వాలి. కానీ బ్రిడ్జ్ అనేది మన కాంట్రాక్ట్, మరియు అది ఏ Solidity వెర్షన్ను ఉపయోగిస్తుందనే దానిపై మేము కఠినంగా ఉండవచ్చు.
1/* Interface Imports */2import { IL1StandardBridge } from "./IL1StandardBridge.sol";3import { IL1ERC20Bridge } from "./IL1ERC20Bridge.sol";IL1ERC20Bridge మరియు IL1StandardBridge పైన వివరించబడ్డాయి.
1import { IL2ERC20Bridge } from "../../L2/messaging/IL2ERC20Bridge.sol";ఈ ఇంటర్ఫేస్opens in a new tab L2లోని స్టాండర్డ్ బ్రిడ్జ్ను నియంత్రించడానికి సందేశాలను సృష్టించడానికి మాకు అనుమతిస్తుంది.
1import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";ఈ ఇంటర్ఫేస్opens in a new tab ERC-20 కాంట్రాక్టులను నియంత్రించడానికి మాకు అనుమతిస్తుంది. మీరు దాని గురించి ఇక్కడ మరింత చదవవచ్చు.
1/* Library Imports */2import { CrossDomainEnabled } from "../../libraries/bridge/CrossDomainEnabled.sol";పైన వివరించినట్లు, ఈ కాంట్రాక్ట్ ఇంటర్లేయర్ మెసేజింగ్ కోసం ఉపయోగించబడుతుంది.
1import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployAddresses.sol";Lib_PredeployAddressesopens in a new tab ఎల్లప్పుడూ ఒకే చిరునామాను కలిగి ఉండే L2 కాంట్రాక్టుల కోసం చిరునామాలను కలిగి ఉంటుంది. ఇందులో L2లోని స్టాండర్డ్ బ్రిడ్జ్ కూడా ఉంటుంది.
1import { Address } from "@openzeppelin/contracts/utils/Address.sol";ఓపెన్జెప్పెలిన్ యొక్క అడ్రస్ యుటిలిటీస్opens in a new tab. ఇది కాంట్రాక్ట్ చిరునామాలు మరియు బాహ్యంగా యాజమాన్య ఖాతాలకు (EOA) చెందిన వాటి మధ్య తేడాను గుర్తించడానికి ఉపయోగించబడుతుంది.
గమనించండి, ఇది ఒక పరిపూర్ణ పరిష్కారం కాదు, ఎందుకంటే ప్రత్యక్ష కాల్స్ మరియు కాంట్రాక్ట్ కన్స్ట్రక్టర్ నుండి చేసిన కాల్స్ మధ్య తేడాను గుర్తించడానికి మార్గం లేదు, కానీ కనీసం ఇది కొన్ని సాధారణ వినియోగదారు లోపాలను గుర్తించడానికి మరియు నివారించడానికి మాకు అనుమతిస్తుంది.
1import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";ERC-20 స్టాండర్డ్opens in a new tab వైఫల్యాన్ని నివేదించడానికి ఒక కాంట్రాక్ట్కు రెండు మార్గాలకు మద్దతు ఇస్తుంది:
- తిరిగి వెళ్లు
falseని తిరిగి ఇవ్వండి
రెండు కేసులను నిర్వహించడం మా కోడ్ను మరింత క్లిష్టతరం చేస్తుంది, కాబట్టి బదులుగా మేము ఓపెన్జెప్పెలిన్ యొక్క SafeERC20opens in a new tabను ఉపయోగిస్తాము, ఇది అన్ని వైఫల్యాలు తిరిగి వెళ్ళడానికి దారితీస్తాయనిopens in a new tab నిర్ధారిస్తుంది.
1/**2 * @title L1StandardBridge3 * @dev The L1 ETH and ERC20 Bridge is a contract which stores deposited L1 funds and standard4 * tokens that are in use on L2. It synchronizes a corresponding L2 Bridge, informing it of deposits5 * and listening to it for newly finalized withdrawals.6 *7 */8contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled {9 using SafeERC20 for IERC20;అన్నీ చూపించుమేము IERC20 ఇంటర్ఫేస్ను ఉపయోగించిన ప్రతిసారీ SafeERC20 రేపర్ను ఉపయోగించమని పేర్కొనడానికి ఈ లైన్ మార్గం.
12 /********************************3 * External Contract References *4 ********************************/56 address public l2TokenBridge;L2StandardBridge యొక్క చిరునామా.
12 // Maps L1 token to L2 token to balance of the L1 token deposited3 mapping(address => mapping(address => uint256)) public deposits;ఇటువంటి డబుల్ మ్యాపింగ్opens in a new tab మీరు రెండు-డైమెన్షనల్ స్పార్స్ శ్రేణినిopens in a new tab నిర్వచించే మార్గం.
ఈ డేటా స్ట్రక్చర్లోని విలువలు deposit[L1 టోకెన్ addr][L2 టోకెన్ addr]గా గుర్తించబడతాయి.
డిఫాల్ట్ విలువ సున్నా.
వేరొక విలువకు సెట్ చేయబడిన సెల్స్ మాత్రమే స్టోరేజ్కు వ్రాయబడతాయి.
12 /***************3 * Constructor *4 ***************/56 // This contract lives behind a proxy, so the constructor parameters will go unused.7 constructor() CrossDomainEnabled(address(0)) {}స్టోరేజ్లోని అన్ని వేరియబుల్స్ను కాపీ చేయకుండానే ఈ కాంట్రాక్ట్ను అప్గ్రేడ్ చేయగలగాలి.
అలా చేయడానికి, మేము ప్రాక్సీopens in a new tabని ఉపయోగిస్తాము, ఇది ప్రాక్సీ కాంట్రాక్ట్ ద్వారా నిల్వ చేయబడిన చిరునామా ఉన్న వేరొక కాంట్రాక్ట్కు కాల్స్ను బదిలీ చేయడానికి delegatecallopens in a new tabని ఉపయోగించే ఒక కాంట్రాక్ట్ (మీరు అప్గ్రేడ్ చేసినప్పుడు మీరు ప్రాక్సీకి ఆ చిరునామాను మార్చమని చెబుతారు).
మీరు delegatecallని ఉపయోగించినప్పుడు, స్టోరేజ్ కాలింగ్ కాంట్రాక్ట్ యొక్క స్టోరేజ్గానే ఉంటుంది, కాబట్టి అన్ని కాంట్రాక్ట్ స్టేట్ వేరియబుల్స్ యొక్క విలువలు ప్రభావితం కావు.
ఈ నమూనా యొక్క ఒక ప్రభావం ఏమిటంటే, delegatecall యొక్క కాల్డ్ అయిన కాంట్రాక్ట్ యొక్క స్టోరేజ్ ఉపయోగించబడదు మరియు అందువల్ల దానికి పంపిన కన్స్ట్రక్టర్ విలువలు పట్టింపు లేదు.
CrossDomainEnabled కన్స్ట్రక్టర్కు మేము ఒక అర్థరహిత విలువను అందించడానికి ఇదే కారణం.
కింది ఇనిషియలైజేషన్ కన్స్ట్రక్టర్ నుండి వేరుగా ఉండటానికి కూడా ఇదే కారణం.
1 /******************2 * Initialization *3 ******************/45 /**6 * @param _l1messenger L1 Messenger address being used for cross-chain communications.7 * @param _l2TokenBridge L2 standard bridge address.8 */9 // slither-disable-next-line external-functionఅన్నీ చూపించుఈ Slither టెస్ట్opens in a new tab కాంట్రాక్ట్ కోడ్ నుండి పిలవబడని మరియు అందువల్ల publicకు బదులుగా externalగా ప్రకటించగల ఫంక్షన్లను గుర్తిస్తుంది.
external ఫంక్షన్ల యొక్క గ్యాస్ ఖర్చు తక్కువగా ఉండవచ్చు, ఎందుకంటే వాటికి కాల్డేటాలో పరామితులు అందించబడవచ్చు.
publicగా ప్రకటించబడిన ఫంక్షన్లు కాంట్రాక్ట్ లోపల నుండి యాక్సెస్ చేయగలవుగా ఉండాలి.
కాంట్రాక్టులు తమ సొంత కాల్డేటాను మార్చలేవు, కాబట్టి పరామితులు మెమరీలో ఉండాలి.
అటువంటి ఫంక్షన్ను బాహ్యంగా పిలిచినప్పుడు, కాల్డేటాను మెమరీకి కాపీ చేయడం అవసరం, దీనికి గ్యాస్ ఖర్చవుతుంది.
ఈ సందర్భంలో, ఫంక్షన్ ఒక్కసారి మాత్రమే పిలవబడుతుంది, కాబట్టి అసమర్థత మాకు పట్టింపు లేదు.
1 function initialize(address _l1messenger, address _l2TokenBridge) public {2 require(messenger == address(0), "Contract has already been initialized.");initialize ఫంక్షన్ ఒక్కసారి మాత్రమే పిలవబడాలి.
L1 క్రాస్ డొమైన్ మెసెంజర్ లేదా L2 టోకెన్ బ్రిడ్జ్ యొక్క చిరునామా మారితే, మేము కొత్త ప్రాక్సీని మరియు దానిని పిలిచే కొత్త బ్రిడ్జ్ను సృష్టిస్తాము.
మొత్తం సిస్టమ్ అప్గ్రేడ్ చేయబడినప్పుడు తప్ప ఇది జరగడం చాలా అరుదు.
గమనించండి, ఈ ఫంక్షన్లో ఎవరు దీనిని పిలవగలరో పరిమితం చేసే ఏ యంత్రాంగం లేదు.
దీని అర్థం, సిద్ధాంతపరంగా ఒక దాడి చేసేవాడు మేము ప్రాక్సీని మరియు బ్రిడ్జ్ యొక్క మొదటి వెర్షన్ను డిప్లాయ్ చేసే వరకు వేచి ఉండి, ఆ తర్వాత చట్టబద్ధమైన వినియోగదారు చేసే ముందు initialize ఫంక్షన్ను చేరుకోవడానికి ఫ్రంట్-రన్opens in a new tab చేయగలడు. కానీ దీనిని నివారించడానికి రెండు పద్ధతులు ఉన్నాయి:
- కాంట్రాక్టులు నేరుగా EOA ద్వారా కాకుండా వాటిని సృష్టించడానికి మరొక కాంట్రాక్ట్ ఉన్న లావాదేవీలోopens in a new tab డిప్లాయ్ చేయబడితే, మొత్తం ప్రక్రియ పరమాణువుగా ఉంటుంది, మరియు ఏ ఇతర లావాదేవీ అమలు చేయబడకముందే పూర్తి అవుతుంది.
initializeకు చట్టబద్ధమైన కాల్ విఫలమైతే, కొత్తగా సృష్టించబడిన ప్రాక్సీ మరియు బ్రిడ్జ్ను విస్మరించి, కొత్త వాటిని సృష్టించడం ఎల్లప్పుడూ సాధ్యమే.
1 messenger = _l1messenger;2 l2TokenBridge = _l2TokenBridge;3 }బ్రిడ్జ్కు తెలియాల్సిన రెండు పరామితులు ఇవి.
12 /**************3 * Depositing *4 **************/56 /** @dev Modifier requiring sender to be EOA. This check could be bypassed by a malicious7 * contract via initcode, but it takes care of the user error we want to avoid.8 */9 modifier onlyEOA() {10 // Used to stop deposits from contracts (avoid accidentally lost tokens)11 require(!Address.isContract(msg.sender), "Account not EOA");12 _;13 }అన్నీ చూపించుఓపెన్జెప్పెలిన్ యొక్క Address యుటిలిటీస్ మాకు అవసరం కావడానికి ఇదే కారణం.
1 /**2 * @dev This function can be called with no data3 * to deposit an amount of ETH to the caller's balance on L2.4 * Since the receive function doesn't take data, a conservative5 * default amount is forwarded to L2.6 */7 receive() external payable onlyEOA {8 _initiateETHDeposit(msg.sender, msg.sender, 200_000, bytes(""));9 }అన్నీ చూపించుఈ ఫంక్షన్ పరీక్ష ప్రయోజనాల కోసం ఉంది. గమనించండి, ఇది ఇంటర్ఫేస్ నిర్వచనాలలో కనిపించదు - ఇది సాధారణ ఉపయోగం కోసం కాదు.
1 /**2 * @inheritdoc IL1StandardBridge3 */4 function depositETH(uint32 _l2Gas, bytes calldata _data) external payable onlyEOA {5 _initiateETHDeposit(msg.sender, msg.sender, _l2Gas, _data);6 }78 /**9 * @inheritdoc IL1StandardBridge10 */11 function depositETHTo(12 address _to,13 uint32 _l2Gas,14 bytes calldata _data15 ) external payable {16 _initiateETHDeposit(msg.sender, _to, _l2Gas, _data);17 }అన్నీ చూపించుఈ రెండు ఫంక్షన్లు _initiateETHDeposit చుట్టూ రేపర్స్, ఇది వాస్తవ ETH డిపాజిట్ను నిర్వహించే ఫంక్షన్.
1 /**2 * @dev Performs the logic for deposits by storing the ETH and informing the L2 ETH Gateway of3 * the deposit.4 * @param _from Account to pull the deposit from on L1.5 * @param _to Account to give the deposit to on L2.6 * @param _l2Gas Gas limit required to complete the deposit on L2.7 * @param _data Optional data to forward to L2. This data is provided8 * solely as a convenience for external contracts. Aside from enforcing a maximum9 * length, these contracts provide no guarantees about its content.10 */11 function _initiateETHDeposit(12 address _from,13 address _to,14 uint32 _l2Gas,15 bytes memory _data16 ) internal {17 // Construct calldata for finalizeDeposit call18 bytes memory message = abi.encodeWithSelector(అన్నీ చూపించుక్రాస్ డొమైన్ సందేశాలు పనిచేసే విధానం ఏమిటంటే, గమ్యస్థాన కాంట్రాక్ట్ దాని కాల్డేటాగా సందేశంతో పిలవబడుతుంది.
Solidity కాంట్రాక్టులు ఎల్లప్పుడూ తమ కాల్డేటాను
ABI స్పెసిఫికేషన్లకుopens in a new tab అనుగుణంగా వివరిస్తాయి.
Solidity ఫంక్షన్ abi.encodeWithSelectoropens in a new tab ఆ కాల్డేటాను సృష్టిస్తుంది.
1 IL2ERC20Bridge.finalizeDeposit.selector,2 address(0),3 Lib_PredeployAddresses.OVM_ETH,4 _from,5 _to,6 msg.value,7 _data8 );ఇక్కడి సందేశం ఈ పరామితులతో finalizeDeposit ఫంక్షన్నుopens in a new tab పిలవడం:
| పరామితి | విలువ | అర్థం |
|---|---|---|
| _l1Token | చిరునామా(0) | L1లో ETH (ఇది ఒక ERC-20 టోకెన్ కాదు) కోసం ప్రత్యేక విలువ |
| _l2Token | Lib_PredeployAddresses.OVM_ETH | ఆప్టిమిజంలో ETHని నిర్వహించే L2 కాంట్రాక్ట్, 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000 (ఈ కాంట్రాక్ట్ అంతర్గత ఆప్టిమిజం ఉపయోగం కోసం మాత్రమే) |
| _from | _from | ETHని పంపే L1లోని చిరునామా |
| _to | _to | ETHని స్వీకరించే L2లోని చిరునామా |
| మొత్తం | msg.value | పంపిన wei మొత్తం (ఇది ఇప్పటికే బ్రిడ్జ్కు పంపబడింది) |
| _data | _data | డిపాజిట్కు జోడించడానికి అదనపు డేటా |
1 // Send calldata into L22 // slither-disable-next-line reentrancy-events3 sendCrossDomainMessage(l2TokenBridge, _l2Gas, message);సందేశాన్ని క్రాస్ డొమైన్ మెసెంజర్ ద్వారా పంపండి.
1 // slither-disable-next-line reentrancy-events2 emit ETHDepositInitiated(_from, _to, msg.value, _data);3 }ఈ బదిలీ గురించి వినే ఏ వికేంద్రీకృత అప్లికేషన్కు తెలియజేయడానికి ఒక ఈవెంట్ను విడుదల చేయండి.
1 /**2 * @inheritdoc IL1ERC20Bridge3 */4 function depositERC20(5 .6 .7 .8 ) external virtual onlyEOA {9 _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, msg.sender, _amount, _l2Gas, _data);10 }1112 /**13 * @inheritdoc IL1ERC20Bridge14 */15 function depositERC20To(16 .17 .18 .19 ) external virtual {20 _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, _to, _amount, _l2Gas, _data);21 }అన్నీ చూపించుఈ రెండు ఫంక్షన్లు _initiateERC20Deposit చుట్టూ రేపర్స్, ఇది వాస్తవ ERC-20 డిపాజిట్ను నిర్వహించే ఫంక్షన్.
1 /**2 * @dev Performs the logic for deposits by informing the L2 Deposited Token3 * contract of the deposit and calling a handler to lock the L1 funds. (e.g., transferFrom)4 *5 * @param _l1Token Address of the L1 ERC20 we are depositing6 * @param _l2Token Address of the L1 respective L2 ERC207 * @param _from Account to pull the deposit from on L18 * @param _to Account to give the deposit to on L29 * @param _amount Amount of the ERC20 to deposit.10 * @param _l2Gas Gas limit required to complete the deposit on L2.11 * @param _data Optional data to forward to L2. This data is provided12 * solely as a convenience for external contracts. Aside from enforcing a maximum13 * length, these contracts provide no guarantees about its content.14 */15 function _initiateERC20Deposit(16 address _l1Token,17 address _l2Token,18 address _from,19 address _to,20 uint256 _amount,21 uint32 _l2Gas,22 bytes calldata _data23 ) internal {అన్నీ చూపించుఈ ఫంక్షన్ పైన ఉన్న _initiateETHDepositతో సమానంగా ఉంటుంది, కొన్ని ముఖ్యమైన తేడాలతో.
మొదటి తేడా ఏమిటంటే, ఈ ఫంక్షన్ టోకెన్ చిరునామాలను మరియు బదిలీ చేయాల్సిన మొత్తాన్ని పరామితులుగా స్వీకరిస్తుంది.
ETH విషయంలో, బ్రిడ్జ్కు కాల్ ఇప్పటికే బ్రిడ్జ్ ఖాతాకు ఆస్తి బదిలీని కలిగి ఉంటుంది (msg.value).
1 // When a deposit is initiated on L1, the L1 Bridge transfers the funds to itself for future2 // withdrawals. safeTransferFrom also checks if the contract has code, so this will fail if3 // _from is an EOA or address(0).4 // slither-disable-next-line reentrancy-events, reentrancy-benign5 IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount);ERC-20 టోకెన్ బదిలీలు ETH నుండి భిన్నమైన ప్రక్రియను అనుసరిస్తాయి:
- వినియోగదారు (
_from) తగిన టోకెన్లను బదిలీ చేయడానికి బ్రిడ్జ్కు అనుమతి ఇస్తాడు. - వినియోగదారు టోకెన్ కాంట్రాక్ట్ చిరునామా, మొత్తం మొదలైన వాటితో బ్రిడ్జ్ను పిలుస్తాడు.
- బ్రిడ్జ్ డిపాజిట్ ప్రక్రియలో భాగంగా టోకెన్లను (దానికి) బదిలీ చేస్తుంది.
మొదటి దశ చివరి రెండు నుండి వేరొక లావాదేవీలో జరగవచ్చు.
అయినప్పటికీ, ఫ్రంట్-రన్నింగ్ ఒక సమస్య కాదు ఎందుకంటే _initiateERC20Deposit (depositERC20 మరియు depositERC20To) అని పిలిచే రెండు ఫంక్షన్లు ఈ ఫంక్షన్ను msg.senderని _from పరామితిగా మాత్రమే పిలుస్తాయి.
1 // Construct calldata for _l2Token.finalizeDeposit(_to, _amount)2 bytes memory message = abi.encodeWithSelector(3 IL2ERC20Bridge.finalizeDeposit.selector,4 _l1Token,5 _l2Token,6 _from,7 _to,8 _amount,9 _data10 );1112 // Send calldata into L213 // slither-disable-next-line reentrancy-events, reentrancy-benign14 sendCrossDomainMessage(l2TokenBridge, _l2Gas, message);1516 // slither-disable-next-line reentrancy-benign17 deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] + _amount;అన్నీ చూపించుడిపాజిట్ చేయబడిన టోకెన్ల మొత్తాన్ని deposits డేటా స్ట్రక్చర్కు జోడించండి.
అదే L1 ERC-20 టోకెన్కు సంబంధించిన అనేక చిరునామాలు L2లో ఉండవచ్చు, కాబట్టి డిపాజిట్లను ట్రాక్ చేయడానికి బ్రిడ్జ్ యొక్క L1 ERC-20 టోకెన్ బ్యాలెన్స్ను ఉపయోగించడం సరిపోదు.
12 // slither-disable-next-line reentrancy-events3 emit ERC20DepositInitiated(_l1Token, _l2Token, _from, _to, _amount, _data);4 }56 /*************************7 * Cross-chain Functions *8 *************************/910 /**11 * @inheritdoc IL1StandardBridge12 */13 function finalizeETHWithdrawal(14 address _from,15 address _to,16 uint256 _amount,17 bytes calldata _dataఅన్నీ చూపించుL2 బ్రిడ్జ్ L2 క్రాస్ డొమైన్ మెసెంజర్కు సందేశాన్ని పంపుతుంది, ఇది L1 క్రాస్ డొమైన్ మెసెంజర్ ఈ ఫంక్షన్ను పిలవడానికి కారణమవుతుంది (వాస్తవానికి, సందేశాన్ని ఖరారు చేసే లావాదేవీopens in a new tab L1లో సమర్పించబడిన తర్వాత).
1 ) external onlyFromCrossDomainAccount(l2TokenBridge) {ఇది క్రాస్ డొమైన్ మెసెంజర్ నుండి మరియు L2 టోకెన్ బ్రిడ్జ్ నుండి వచ్చిన ఒక చట్టబద్ధమైన సందేశం అని నిర్ధారించుకోండి. ఈ ఫంక్షన్ బ్రిడ్జ్ నుండి ETHని ఉపసంహరించుకోవడానికి ఉపయోగించబడుతుంది, కాబట్టి ఇది అధీకృత కాలర్ ద్వారా మాత్రమే పిలవబడుతుందని మేము నిర్ధారించుకోవాలి.
1 // slither-disable-next-line reentrancy-events2 (bool success, ) = _to.call{ value: _amount }(new bytes(0));ETHని బదిలీ చేయడానికి మార్గం msg.valueలో wei మొత్తంతో గ్రహీతను పిలవడం.
1 require(success, "TransferHelper::safeTransferETH: ETH transfer failed");23 // slither-disable-next-line reentrancy-events4 emit ETHWithdrawalFinalized(_from, _to, _amount, _data);విత్డ్రాయల్ గురించి ఒక ఈవెంట్ను విడుదల చేయండి.
1 }23 /**4 * @inheritdoc IL1ERC20Bridge5 */6 function finalizeERC20Withdrawal(7 address _l1Token,8 address _l2Token,9 address _from,10 address _to,11 uint256 _amount,12 bytes calldata _data13 ) external onlyFromCrossDomainAccount(l2TokenBridge) {అన్నీ చూపించుఈ ఫంక్షన్ పైన ఉన్న finalizeETHWithdrawalతో సమానంగా ఉంటుంది, ERC-20 టోకెన్ల కోసం అవసరమైన మార్పులతో.
1 deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] - _amount;deposits డేటా స్ట్రక్చర్ను అప్డేట్ చేయండి.
12 // When a withdrawal is finalized on L1, the L1 Bridge transfers the funds to the withdrawer3 // slither-disable-next-line reentrancy-events4 IERC20(_l1Token).safeTransfer(_to, _amount);56 // slither-disable-next-line reentrancy-events7 emit ERC20WithdrawalFinalized(_l1Token, _l2Token, _from, _to, _amount, _data);8 }91011 /*****************************12 * Temporary - Migrating ETH *13 *****************************/1415 /**16 * @dev Adds ETH balance to the account. This is meant to allow for ETH17 * to be migrated from an old gateway to a new gateway.18 * NOTE: This is left for one upgrade only so we are able to receive the migrated ETH from the19 * old contract20 */21 function donateETH() external payable {}22}అన్నీ చూపించుబ్రిడ్జ్ యొక్క మునుపటి అమలు ఒకటి ఉండేది.
మేము ఆ అమలు నుండి దీనికి మారినప్పుడు, మేము అన్ని ఆస్తులను తరలించవలసి వచ్చింది.
ERC-20 టోకెన్లను కేవలం తరలించవచ్చు.
అయితే, ఒక కాంట్రాక్ట్కు ETHని బదిలీ చేయడానికి మీకు ఆ కాంట్రాక్ట్ ఆమోదం అవసరం, అదే donateETH మాకు అందిస్తుంది.
L2లో ERC-20 టోకెన్లు
ఒక ERC-20 టోకెన్ స్టాండర్డ్ బ్రిడ్జ్కు సరిపోవాలంటే, అది స్టాండర్డ్ బ్రిడ్జ్కు, మరియు మాత్రమే స్టాండర్డ్ బ్రిడ్జ్కు టోకెన్లను ముద్రించడానికి అనుమతించాలి. ఆప్టిమిజంలో చలామణిలో ఉన్న టోకెన్ల సంఖ్య L1 బ్రిడ్జ్ కాంట్రాక్ట్లో లాక్ చేయబడిన టోకెన్ల సంఖ్యకు సమానంగా ఉందని బ్రిడ్జ్లు నిర్ధారించుకోవాలి కాబట్టి ఇది అవసరం. L2లో ఎక్కువ టోకెన్లు ఉంటే, కొంతమంది వినియోగదారులు తమ ఆస్తులను తిరిగి L1కి బ్రిడ్జ్ చేయలేరు. విశ్వసనీయమైన బ్రిడ్జ్కు బదులుగా, మేము తప్పనిసరిగా ఫ్రాక్షనల్ రిజర్వ్ బ్యాంకింగ్నుopens in a new tab పునఃసృష్టిస్తాము. L1లో ఎక్కువ టోకెన్లు ఉంటే, ఆ టోకెన్లలో కొన్ని బ్రిడ్జ్ కాంట్రాక్ట్లో శాశ్వతంగా లాక్ చేయబడి ఉంటాయి, ఎందుకంటే L2 టోకెన్లను బర్న్ చేయకుండా వాటిని విడుదల చేయడానికి మార్గం లేదు.
IL2StandardERC20
L2లో స్టాండర్డ్ బ్రిడ్జ్ను ఉపయోగించే ప్రతి ERC-20 టోకెన్ ఈ ఇంటర్ఫేస్నుopens in a new tab అందించాలి, ఇందులో స్టాండర్డ్ బ్రిడ్జ్కు అవసరమైన ఫంక్షన్లు మరియు ఈవెంట్లు ఉంటాయి.
1// SPDX-License-Identifier: MIT2pragma solidity ^0.8.9;34import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";స్టాండర్డ్ ERC-20 ఇంటర్ఫేస్opens in a new tabలో mint మరియు burn ఫంక్షన్లు ఉండవు.
ఆ పద్ధతులు ERC-20 స్టాండర్డ్opens in a new tabచే అవసరం లేదు, ఇది టోకెన్లను సృష్టించడానికి మరియు నాశనం చేయడానికి యంత్రాంగాలను పేర్కొనలేదు.
1import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";ERC-165 ఇంటర్ఫేస్opens in a new tab ఒక కాంట్రాక్ట్ ఏ ఫంక్షన్లను అందిస్తుందో పేర్కొనడానికి ఉపయోగించబడుతుంది. మీరు ఇక్కడ స్టాండర్డ్ చదవవచ్చుopens in a new tab.
1interface IL2StandardERC20 is IERC20, IERC165 {2 function l1Token() external returns (address);ఈ ఫంక్షన్ ఈ కాంట్రాక్ట్కు బ్రిడ్జ్ చేయబడిన L1 టోకెన్ చిరునామాను అందిస్తుంది. గమనించండి, వ్యతిరేక దిశలో మనకు సమానమైన ఫంక్షన్ లేదు. L2 మద్దతు అది అమలు చేయబడినప్పుడు ప్రణాళిక చేయబడిందా లేదా అనే దానితో సంబంధం లేకుండా మేము ఏ L1 టోకెన్ను అయినా బ్రిడ్జ్ చేయగలగాలి.
12 function mint(address _to, uint256 _amount) external;34 function burn(address _from, uint256 _amount) external;56 event Mint(address indexed _account, uint256 _amount);7 event Burn(address indexed _account, uint256 _amount);8}టోకెన్లను ముద్రించడానికి (సృష్టించడానికి) మరియు బర్న్ చేయడానికి (నాశనం చేయడానికి) ఫంక్షన్లు మరియు ఈవెంట్లు. టోకెన్ల సంఖ్య సరైనదని (L1లో లాక్ చేయబడిన టోకెన్ల సంఖ్యకు సమానం) నిర్ధారించడానికి బ్రిడ్జ్ మాత్రమే ఈ ఫంక్షన్లను అమలు చేయగల ఏకైక సంస్థగా ఉండాలి.
L2StandardERC20
ఇది IL2StandardERC20 ఇంటర్ఫేస్ యొక్క మా అమలుopens in a new tab.
మీకు ఏ రకమైన కస్టమ్ లాజిక్ అవసరం లేకపోతే, మీరు దీనిని ఉపయోగించాలి.
1// SPDX-License-Identifier: MIT2pragma solidity ^0.8.9;34import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";ఓపెన్జెప్పెలిన్ ERC-20 కాంట్రాక్ట్opens in a new tab. ఆప్టిమిజం చక్రాన్ని తిరిగి ఆవిష్కరించడంలో నమ్మకం లేదు, ప్రత్యేకించి ఆ చక్రం బాగా ఆడిట్ చేయబడి, ఆస్తులను కలిగి ఉండేంత విశ్వసనీయంగా ఉండాల్సినప్పుడు.
1import "./IL2StandardERC20.sol";23contract L2StandardERC20 is IL2StandardERC20, ERC20 {4 address public l1Token;5 address public l2Bridge;మాకు అవసరమైన మరియు సాధారణంగా ERC-20కి అవసరం లేని రెండు అదనపు కాన్ఫిగరేషన్ పరామితులు ఇవి.
12 /**3 * @param _l2Bridge Address of the L2 standard bridge.4 * @param _l1Token Address of the corresponding L1 token.5 * @param _name ERC20 name.6 * @param _symbol ERC20 symbol.7 */8 constructor(9 address _l2Bridge,10 address _l1Token,11 string memory _name,12 string memory _symbol13 ) ERC20(_name, _symbol) {14 l1Token = _l1Token;15 l2Bridge = _l2Bridge;16 }అన్నీ చూపించుమొదట మనం వారసత్వంగా వచ్చే కాంట్రాక్ట్ కోసం కన్స్ట్రక్టర్ను పిలిచి (ERC20(_name, _symbol)) ఆ తర్వాత మన సొంత వేరియబుల్స్ను సెట్ చేయాలి.
12 modifier onlyL2Bridge() {3 require(msg.sender == l2Bridge, "Only L2 Bridge can mint and burn");4 _;5 }678 // slither-disable-next-line external-function9 function supportsInterface(bytes4 _interfaceId) public pure returns (bool) {10 bytes4 firstSupportedInterface = bytes4(keccak256("supportsInterface(bytes4)")); // ERC16511 bytes4 secondSupportedInterface = IL2StandardERC20.l1Token.selector ^12 IL2StandardERC20.mint.selector ^13 IL2StandardERC20.burn.selector;14 return _interfaceId == firstSupportedInterface || _interfaceId == secondSupportedInterface;15 }అన్నీ చూపించుఇది ERC-165opens in a new tab పనిచేసే విధానం. ప్రతి ఇంటర్ఫేస్ అనేక మద్దతు ఉన్న ఫంక్షన్లను కలిగి ఉంటుంది, మరియు ఆ ఫంక్షన్ల యొక్క ABI ఫంక్షన్ సెలెక్టర్లopens in a new tab యొక్క ఎక్స్క్లూజివ్ లేదాopens in a new tabగా గుర్తించబడుతుంది.
L2 బ్రిడ్జ్ అది ఆస్తులను పంపే ERC-20 కాంట్రాక్ట్ ఒక IL2StandardERC20 అని నిర్ధారించుకోవడానికి ERC-165ని ఒక శానిటీ చెక్గా ఉపయోగిస్తుంది.
గమనిక: supportsInterfaceకు తప్పుడు సమాధానాలు ఇవ్వకుండా రోగ్ కాంట్రాక్ట్లను నిరోధించడానికి ఏమీ లేదు, కాబట్టి ఇది ఒక శానిటీ చెక్ మెకానిజం, కాదు ఒక భద్రతా మెకానిజం.
1 // slither-disable-next-line external-function2 function mint(address _to, uint256 _amount) public virtual onlyL2Bridge {3 _mint(_to, _amount);45 emit Mint(_to, _amount);6 }78 // slither-disable-next-line external-function9 function burn(address _from, uint256 _amount) public virtual onlyL2Bridge {10 _burn(_from, _amount);1112 emit Burn(_from, _amount);13 }14}అన్నీ చూపించుL2 బ్రిడ్జ్ మాత్రమే ఆస్తులను ముద్రించడానికి మరియు బర్న్ చేయడానికి అనుమతించబడుతుంది.
_mint మరియు _burn వాస్తవానికి ఓపెన్జెప్పెలిన్ ERC-20 కాంట్రాక్ట్లో నిర్వచించబడ్డాయి.
ఆ కాంట్రాక్ట్ వాటిని బాహ్యంగా బహిర్గతం చేయదు, ఎందుకంటే టోకెన్లను ముద్రించడానికి మరియు బర్న్ చేయడానికి పరిస్థితులు ERC-20ని ఉపయోగించే మార్గాల సంఖ్యంత వైవిధ్యంగా ఉంటాయి.
L2 బ్రిడ్జ్ కోడ్
ఇది ఆప్టిమిజంలో బ్రిడ్జ్ను నడిపే కోడ్. ఈ కాంట్రాక్ట్ కోసం సోర్స్ ఇక్కడ ఉందిopens in a new tab.
1// SPDX-License-Identifier: MIT2pragma solidity ^0.8.9;34/* Interface Imports */5import { IL1StandardBridge } from "../../L1/messaging/IL1StandardBridge.sol";6import { IL1ERC20Bridge } from "../../L1/messaging/IL1ERC20Bridge.sol";7import { IL2ERC20Bridge } from "./IL2ERC20Bridge.sol";IL2ERC20Bridgeopens in a new tab ఇంటర్ఫేస్ పైన మనం చూసిన L1 సమానమైనదానికి చాలా పోలి ఉంటుంది. రెండు ముఖ్యమైన తేడాలు ఉన్నాయి:
- L1లో మీరు డిపాజిట్లను ప్రారంభిస్తారు మరియు విత్డ్రాయల్స్ను ఖరారు చేస్తారు. ఇక్కడ మీరు విత్డ్రాయల్స్ను ప్రారంభిస్తారు మరియు డిపాజిట్లను ఖరారు చేస్తారు.
- L1లో ETH మరియు ERC-20 టోకెన్ల మధ్య తేడాను గుర్తించడం అవసరం. L2లో మేము రెండింటికీ ఒకే ఫంక్షన్లను ఉపయోగించవచ్చు, ఎందుకంటే ఆప్టిమిజంలో అంతర్గతంగా ETH బ్యాలెన్స్లు 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000opens in a new tab చిరునామాతో ERC-20 టోకెన్గా నిర్వహించబడతాయి.
1/* Library Imports */2import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";3import { CrossDomainEnabled } from "../../libraries/bridge/CrossDomainEnabled.sol";4import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployAddresses.sol";56/* Contract Imports */7import { IL2StandardERC20 } from "../../standards/IL2StandardERC20.sol";89/**10 * @title L2StandardBridge11 * @dev The L2 Standard bridge is a contract which works together with the L1 Standard bridge to12 * enable ETH and ERC20 transitions between L1 and L2.13 * This contract acts as a minter for new tokens when it hears about deposits into the L1 Standard14 * bridge.15 * This contract also acts as a burner of the tokens intended for withdrawal, informing the L116 * bridge to release L1 funds.17 */18contract L2StandardBridge is IL2ERC20Bridge, CrossDomainEnabled {19 /********************************20 * External Contract References *21 ********************************/2223 address public l1TokenBridge;అన్నీ చూపించుL1 బ్రిడ్జ్ చిరునామాను ట్రాక్ చేయండి. L1 సమానమైన దానికి విరుద్ధంగా, ఇక్కడ మాకు ఈ వేరియబుల్ అవసరం అని గమనించండి. L1 బ్రిడ్జ్ చిరునామా ముందుగానే తెలియదు.
12 /***************3 * Constructor *4 ***************/56 /**7 * @param _l2CrossDomainMessenger Cross-domain messenger used by this contract.8 * @param _l1TokenBridge Address of the L1 bridge deployed to the main chain.9 */10 constructor(address _l2CrossDomainMessenger, address _l1TokenBridge)11 CrossDomainEnabled(_l2CrossDomainMessenger)12 {13 l1TokenBridge = _l1TokenBridge;14 }1516 /***************17 * Withdrawing *18 ***************/1920 /**21 * @inheritdoc IL2ERC20Bridge22 */23 function withdraw(24 address _l2Token,25 uint256 _amount,26 uint32 _l1Gas,27 bytes calldata _data28 ) external virtual {29 _initiateWithdrawal(_l2Token, msg.sender, msg.sender, _amount, _l1Gas, _data);30 }3132 /**33 * @inheritdoc IL2ERC20Bridge34 */35 function withdrawTo(36 address _l2Token,37 address _to,38 uint256 _amount,39 uint32 _l1Gas,40 bytes calldata _data41 ) external virtual {42 _initiateWithdrawal(_l2Token, msg.sender, _to, _amount, _l1Gas, _data);43 }అన్నీ చూపించుఈ రెండు ఫంక్షన్లు విత్డ్రాయల్స్ను ప్రారంభిస్తాయి. L1 టోకెన్ చిరునామాను పేర్కొనవలసిన అవసరం లేదని గమనించండి. L2 టోకెన్లు మాకు L1 సమానమైన చిరునామాను చెబుతాయని ఆశించబడుతుంది.
12 /**3 * @dev Performs the logic for withdrawals by burning the token and informing4 * the L1 token Gateway of the withdrawal.5 * @param _l2Token Address of L2 token where withdrawal is initiated.6 * @param _from Account to pull the withdrawal from on L2.7 * @param _to Account to give the withdrawal to on L1.8 * @param _amount Amount of the token to withdraw.9 * @param _l1Gas Unused, but included for potential forward compatibility considerations.10 * @param _data Optional data to forward to L1. This data is provided11 * solely as a convenience for external contracts. Aside from enforcing a maximum12 * length, these contracts provide no guarantees about its content.13 */14 function _initiateWithdrawal(15 address _l2Token,16 address _from,17 address _to,18 uint256 _amount,19 uint32 _l1Gas,20 bytes calldata _data21 ) internal {22 // When a withdrawal is initiated, we burn the withdrawer's funds to prevent subsequent L223 // usage24 // slither-disable-next-line reentrancy-events25 IL2StandardERC20(_l2Token).burn(msg.sender, _amount);అన్నీ చూపించుగమనించండి, మేము _from పరామితిపై ఆధారపడటం లేదు కానీ msg.senderపై ఆధారపడుతున్నాము, ఇది నకిలీ చేయడం చాలా కష్టం (నాకు తెలిసినంతవరకు అసాధ్యం).
12 // Construct calldata for l1TokenBridge.finalizeERC20Withdrawal(_to, _amount)3 // slither-disable-next-line reentrancy-events4 address l1Token = IL2StandardERC20(_l2Token).l1Token();5 bytes memory message;67 if (_l2Token == Lib_PredeployAddresses.OVM_ETH) {L1లో ETH మరియు ERC-20 మధ్య తేడాను గుర్తించడం అవసరం.
1 message = abi.encodeWithSelector(2 IL1StandardBridge.finalizeETHWithdrawal.selector,3 _from,4 _to,5 _amount,6 _data7 );8 } else {9 message = abi.encodeWithSelector(10 IL1ERC20Bridge.finalizeERC20Withdrawal.selector,11 l1Token,12 _l2Token,13 _from,14 _to,15 _amount,16 _data17 );18 }1920 // Send message up to L1 bridge21 // slither-disable-next-line reentrancy-events22 sendCrossDomainMessage(l1TokenBridge, _l1Gas, message);2324 // slither-disable-next-line reentrancy-events25 emit WithdrawalInitiated(l1Token, _l2Token, msg.sender, _to, _amount, _data);26 }2728 /************************************29 * Cross-chain Function: Depositing *30 ************************************/3132 /**33 * @inheritdoc IL2ERC20Bridge34 */35 function finalizeDeposit(36 address _l1Token,37 address _l2Token,38 address _from,39 address _to,40 uint256 _amount,41 bytes calldata _dataఅన్నీ చూపించుఈ ఫంక్షన్ L1StandardBridge ద్వారా పిలవబడుతుంది.
1 ) external virtual onlyFromCrossDomainAccount(l1TokenBridge) {సందేశం యొక్క మూలం చట్టబద్ధమైనదని నిర్ధారించుకోండి.
ఇది ముఖ్యం ఎందుకంటే ఈ ఫంక్షన్ _mintను పిలుస్తుంది మరియు L1లో బ్రిడ్జ్ యాజమాన్యంలోని టోకెన్ల ద్వారా కవర్ చేయబడని టోకెన్లను ఇవ్వడానికి ఉపయోగించబడుతుంది.
1 // Check the target token is compliant and2 // verify the deposited token on L1 matches the L2 deposited token representation here3 if (4 // slither-disable-next-line reentrancy-events5 ERC165Checker.supportsInterface(_l2Token, 0x1d1d8b63) &&6 _l1Token == IL2StandardERC20(_l2Token).l1Token()శానిటీ చెక్స్:
- సరైన ఇంటర్ఫేస్కు మద్దతు ఉంది
- L2 ERC-20 కాంట్రాక్ట్ యొక్క L1 చిరునామా టోకెన్ల L1 మూలంతో సరిపోలుతుంది
1 ) {2 // When a deposit is finalized, we credit the account on L2 with the same amount of3 // tokens.4 // slither-disable-next-line reentrancy-events5 IL2StandardERC20(_l2Token).mint(_to, _amount);6 // slither-disable-next-line reentrancy-events7 emit DepositFinalized(_l1Token, _l2Token, _from, _to, _amount, _data);శానిటీ చెక్స్ పాస్ అయితే, డిపాజిట్ను ఖరారు చేయండి:
- టోకెన్లను ముద్రించండి
- తగిన ఈవెంట్ను విడుదల చేయండి
1 } else {2 // Either the L2 token which is being deposited-into disagrees about the correct address3 // of its L1 token, or does not support the correct interface.4 // This should only happen if there is a malicious L2 token, or if a user somehow5 // specified the wrong L2 token address to deposit into.6 // In either case, we stop the process here and construct a withdrawal7 // message so that users can get their funds out in some cases.8 // There is no way to prevent malicious token contracts altogether, but this does limit9 // user error and mitigate some forms of malicious contract behavior.అన్నీ చూపించుఒక వినియోగదారు తప్పు L2 టోకెన్ చిరునామాను ఉపయోగించడం ద్వారా గుర్తించగల లోపాన్ని చేస్తే, మేము డిపాజిట్ను రద్దు చేసి, L1లో టోకెన్లను తిరిగి ఇవ్వాలనుకుంటున్నాము. L2 నుండి మేము దీనిని చేయగల ఏకైక మార్గం ఫాల్ట్ ఛాలెంజ్ వ్యవధి కోసం వేచి ఉండవలసిన సందేశాన్ని పంపడం, కానీ అది వినియోగదారుకు టోకెన్లను శాశ్వతంగా కోల్పోవడం కంటే చాలా మంచిది.
1 bytes memory message = abi.encodeWithSelector(2 IL1ERC20Bridge.finalizeERC20Withdrawal.selector,3 _l1Token,4 _l2Token,5 _to, // switched the _to and _from here to bounce back the deposit to the sender6 _from,7 _amount,8 _data9 );1011 // Send message up to L1 bridge12 // slither-disable-next-line reentrancy-events13 sendCrossDomainMessage(l1TokenBridge, 0, message);14 // slither-disable-next-line reentrancy-events15 emit DepositFailed(_l1Token, _l2Token, _from, _to, _amount, _data);16 }17 }18}అన్నీ చూపించుముగింపు
ఆస్తి బదిలీల కోసం స్టాండర్డ్ బ్రిడ్జ్ అత్యంత అనువైన యంత్రాంగం. అయితే, ఇది చాలా సాధారణమైనది కాబట్టి, ఇది ఎల్లప్పుడూ ఉపయోగించడానికి సులభమైన యంత్రాంగం కాదు. ముఖ్యంగా విత్డ్రాయల్స్ కోసం, చాలా మంది వినియోగదారులు ఛాలెంజ్ వ్యవధి కోసం వేచి ఉండని మరియు విత్డ్రాయల్ను ఖరారు చేయడానికి మెర్కిల్ ప్రూఫ్ అవసరం లేని థర్డ్ పార్టీ బ్రిడ్జ్లనుopens in a new tab ఉపయోగించడానికి ఇష్టపడతారు.
ఈ బ్రిడ్జ్లు సాధారణంగా L1లో ఆస్తులను కలిగి ఉండటం ద్వారా పనిచేస్తాయి, అవి తక్షణమే చిన్న రుసుముకు అందిస్తాయి (తరచుగా స్టాండర్డ్ బ్రిడ్జ్ విత్డ్రాయల్ కోసం గ్యాస్ ఖర్చు కంటే తక్కువ). బ్రిడ్జ్ (లేదా దానిని నడుపుతున్న వ్యక్తులు) L1 ఆస్తుల కొరతను ఊహించినప్పుడు, అది L2 నుండి తగినన్ని ఆస్తులను బదిలీ చేస్తుంది. ఇవి చాలా పెద్ద విత్డ్రాయల్స్ కాబట్టి, విత్డ్రాయల్ ఖర్చు పెద్ద మొత్తంపై రుణ విమోచన చేయబడుతుంది మరియు చాలా తక్కువ శాతంగా ఉంటుంది.
ఈ వ్యాసం లేయర్ 2 ఎలా పనిచేస్తుందో, మరియు స్పష్టంగా మరియు సురక్షితంగా ఉండే Solidity కోడ్ను ఎలా వ్రాయాలో మరింత అర్థం చేసుకోవడానికి మీకు సహాయపడిందని ఆశిస్తున్నాము.
నా మరిన్ని పనుల కోసం ఇక్కడ చూడండిopens in a new tab.
పేజీ చివరి అప్డేట్: 22 అక్టోబర్, 2025