مرکزی مواد پر جائیں

آپٹیمزم کے معیاری پل کے کنٹریکٹ کا تفصیلی جائزہ

Solidity
پل
لیئر ۲
درمیانی
اوری پومرانٹز
۳۰ مارچ، ۲۰۲۲
42 منٹ کا مطالعہ

آپٹیمزم (opens in a new tab) ایک آپٹمسٹک رول اپ ہے۔ آپٹمسٹک رول اپس ایتھیریم مین نیٹ (جسے لیئر ۱ (l1) بھی کہا جاتا ہے) کی نسبت بہت کم قیمت پر ٹرانزیکشنز پر کارروائی کر سکتے ہیں کیونکہ ٹرانزیکشنز پر نیٹ ورک کے ہر نوڈ کے بجائے صرف چند نوڈز کے ذریعے کارروائی کی جاتی ہے۔ اس کے ساتھ ہی، تمام ڈیٹا لیئر ۱ (l1) پر لکھا جاتا ہے تاکہ مین نیٹ کی سالمیت اور دستیابی کی تمام ضمانتوں کے ساتھ ہر چیز کو ثابت اور دوبارہ تشکیل دیا جا سکے۔

آپٹیمزم (یا کسی دوسری لیئر ۲ (l2)) پر لیئر ۱ (l1) کے اثاثے استعمال کرنے کے لیے، اثاثوں کو پل کرنے کی ضرورت ہوتی ہے۔ اسے حاصل کرنے کا ایک طریقہ یہ ہے کہ صارفین لیئر ۱ (l1) پر اثاثوں (جن میں ETH اور ERC-20 ٹوکنز سب سے عام ہیں) کو مقفل کریں، اور لیئر ۲ (l2) پر استعمال کرنے کے لیے مساوی اثاثے حاصل کریں۔ بالآخر، جس کے پاس بھی یہ اثاثے ہوں گے وہ انہیں واپس لیئر ۱ (l1) پر پل کرنا چاہے گا۔ ایسا کرتے وقت، اثاثوں کو لیئر ۲ (l2) پر جلایا جاتا ہے اور پھر لیئر ۱ (l1) پر صارف کو واپس جاری کر دیا جاتا ہے۔

یہ وہ طریقہ ہے جس سے آپٹیمزم کا معیاری پل (opens in a new tab) کام کرتا ہے۔ اس مضمون میں ہم اس پل کے سورس کوڈ کا جائزہ لیں گے تاکہ یہ دیکھ سکیں کہ یہ کیسے کام کرتا ہے اور اسے اچھی طرح سے لکھے گئے Solidity کوڈ کی ایک مثال کے طور پر پڑھیں گے۔

کنٹرول فلوز

پل کے دو اہم فلوز ہیں:

  • جمع کرنا (لیئر ۱ (l1) سے لیئر ۲ (l2) تک)
  • انخلا (لیئر ۲ (l2) سے لیئر ۱ (l1) تک)

جمع کرنے کا فلو

لیئر ۱

  1. اگر کوئی ERC-20 جمع کر رہا ہے، تو جمع کنندہ پل کو جمع کی جانے والی رقم خرچ کرنے کا الاؤنس دیتا ہے۔
  2. جمع کنندہ لیئر ۱ (l1) پل کو کال کرتا ہے (depositERC20، depositERC20To، depositETH، یا depositETHTo)
  3. لیئر ۱ (l1) پل، پل کیے گئے اثاثے کا قبضہ لے لیتا ہے
    • ETH: اثاثہ جمع کنندہ کی جانب سے کال کے حصے کے طور پر منتقل کیا جاتا ہے
    • ERC-20: اثاثہ پل کی جانب سے جمع کنندہ کے فراہم کردہ الاؤنس کا استعمال کرتے ہوئے خود کو منتقل کیا جاتا ہے
  4. لیئر ۱ (l1) پل کراس ڈومین پیغام کے طریقہ کار کا استعمال کرتے ہوئے لیئر ۲ (l2) پل پر finalizeDeposit کو کال کرتا ہے

لیئر ۲

  1. لیئر ۲ (l2) پل تصدیق کرتا ہے کہ finalizeDeposit کی کال جائز ہے:
    • کراس ڈومین پیغام کے کنٹریکٹ سے آئی ہے
    • اصل میں لیئر ۱ (l1) پر موجود پل سے تھی
  2. لیئر ۲ (l2) پل چیک کرتا ہے کہ آیا لیئر ۲ (l2) پر ERC-20 ٹوکن کنٹریکٹ درست ہے:
    • لیئر ۲ (l2) کنٹریکٹ رپورٹ کرتا ہے کہ اس کا لیئر ۱ (l1) ہم منصب وہی ہے جہاں سے لیئر ۱ (l1) پر ٹوکن آئے تھے
    • لیئر ۲ (l2) کنٹریکٹ رپورٹ کرتا ہے کہ یہ درست انٹرفیس کو سپورٹ کرتا ہے (ERC-165 کا استعمال کرتے ہوئے (opens in a new tab)
  3. اگر لیئر ۲ (l2) کنٹریکٹ درست ہے، تو اسے مناسب پتے پر مناسب تعداد میں ٹوکنز ڈھالنے کے لیے کال کریں۔ اگر نہیں، تو صارف کو لیئر ۱ (l1) پر ٹوکنز کا دعویٰ کرنے کی اجازت دینے کے لیے انخلا کا عمل شروع کریں۔

انخلا کا فلو

لیئر ۲

  1. انخلا کرنے والا لیئر ۲ (l2) پل کو کال کرتا ہے (withdraw یا withdrawTo)
  2. لیئر ۲ (l2) پل msg.sender سے تعلق رکھنے والے مناسب تعداد میں ٹوکنز کو جلاتا ہے
  3. لیئر ۲ (l2) پل کراس ڈومین پیغام کے طریقہ کار کا استعمال کرتے ہوئے لیئر ۱ (l1) پل پر finalizeETHWithdrawal یا finalizeERC20Withdrawal کو کال کرتا ہے

لیئر ۱

  1. لیئر ۱ (l1) پل تصدیق کرتا ہے کہ finalizeETHWithdrawal یا finalizeERC20Withdrawal کی کال جائز ہے:
    • کراس ڈومین پیغام کے طریقہ کار سے آئی ہے
    • اصل میں لیئر ۲ (l2) پر موجود پل سے تھی
  2. لیئر ۱ (l1) پل مناسب اثاثہ (ETH یا ERC-20) کو مناسب پتے پر منتقل کرتا ہے

لیئر ۱ کا کوڈ

یہ وہ کوڈ ہے جو لیئر ۱ (l1)، یعنی ایتھیریم مین نیٹ پر چلتا ہے۔

IL1ERC20Bridge

اس انٹرفیس کی تعریف یہاں کی گئی ہے (opens in a new tab)۔ اس میں ERC-20 ٹوکنز کو پل کرنے کے لیے درکار فنکشنز اور تعریفیں شامل ہیں۔

// SPDX-License-Identifier: MIT

آپٹیمزم کا زیادہ تر کوڈ MIT لائسنس کے تحت جاری کیا گیا ہے (opens in a new tab)۔

pragma solidity >0.5.0 <0.9.0;

لکھتے وقت Solidity کا تازہ ترین ورژن 0.8.12 ہے۔ جب تک ورژن 0.9.0 جاری نہیں ہوتا، ہم نہیں جانتے کہ یہ کوڈ اس کے ساتھ مطابقت رکھتا ہے یا نہیں۔

آپٹیمزم پل کی اصطلاح میں جمع کرنے کا مطلب لیئر ۱ (l1) سے لیئر ۲ (l2) میں منتقلی ہے، اور انخلا کا مطلب لیئر ۲ (l2) سے لیئر ۱ (l1) میں منتقلی ہے۔

        address indexed _l1Token,
        address indexed _l2Token,

زیادہ تر معاملات میں لیئر ۱ (l1) پر ERC-20 کا پتہ لیئر ۲ (l2) پر مساوی ERC-20 کے پتے جیسا نہیں ہوتا ہے۔ آپ ٹوکن کے پتوں کی فہرست یہاں دیکھ سکتے ہیں (opens in a new tab)۔ chainId 1 والا پتہ لیئر ۱ (l1) (مین نیٹ) پر ہے اور chainId 10 والا پتہ لیئر ۲ (l2) (آپٹیمزم) پر ہے۔ دیگر دو chainId ویلیوز کوون ٹیسٹ نیٹ ورک (42) اور آپٹمسٹک کوون ٹیسٹ نیٹ ورک (69) کے لیے ہیں۔

        address indexed _from,
        address _to,
        uint256 _amount,
        bytes _data
    );

منتقلیوں میں نوٹس شامل کرنا ممکن ہے، اس صورت میں انہیں ان ایونٹس میں شامل کیا جاتا ہے جو ان کی رپورٹ کرتے ہیں۔

    event ERC20WithdrawalFinalized(
        address indexed _l1Token,
        address indexed _l2Token,
        address indexed _from,
        address _to,
        uint256 _amount,
        bytes _data
    );

یہی پل کا کنٹریکٹ دونوں سمتوں میں منتقلیوں کو سنبھالتا ہے۔ لیئر ۱ (l1) پل کے معاملے میں، اس کا مطلب جمع کرنے کا آغاز اور انخلا کی تکمیل ہے۔

اس فنکشن کی واقعی ضرورت نہیں ہے، کیونکہ لیئر ۲ (l2) پر یہ پہلے سے تعینات کردہ کنٹریکٹ ہے، اس لیے یہ ہمیشہ 0x4200000000000000000000000000000000000010 پتے پر ہوتا ہے۔ یہ یہاں لیئر ۲ (l2) پل کے ساتھ ہم آہنگی کے لیے ہے، کیونکہ لیئر ۱ (l1) پل کا پتہ جاننا معمولی بات نہیں ہے۔

_l2Gas پیرامیٹر لیئر ۲ (l2) گیس کی وہ مقدار ہے جو ٹرانزیکشن کو خرچ کرنے کی اجازت ہے۔ ایک مخصوص (اعلیٰ) حد تک، یہ مفت ہے (opens in a new tab)، لہذا جب تک کہ ERC-20 کنٹریکٹ ڈھالتے وقت کچھ واقعی عجیب نہ کرے، یہ کوئی مسئلہ نہیں ہونا چاہیے۔ یہ فنکشن اس عام صورتحال کو سنبھالتا ہے، جہاں ایک صارف مختلف بلاک چین پر اسی پتے پر اثاثوں کو پل کرتا ہے۔

یہ فنکشن تقریباً depositERC20 جیسا ہی ہے، لیکن یہ آپ کو ERC-20 کو کسی مختلف پتے پر بھیجنے کی اجازت دیتا ہے۔

آپٹیمزم میں انخلا (اور لیئر ۲ (l2) سے لیئر ۱ (l1) تک کے دیگر پیغامات) دو مراحل پر مشتمل عمل ہیں:

  1. لیئر ۲ (l2) پر ایک ابتدائی ٹرانزیکشن۔
  2. لیئر ۱ (l1) پر ایک حتمی یا دعویٰ کرنے والی ٹرانزیکشن۔ یہ ٹرانزیکشن لیئر ۲ (l2) ٹرانزیکشن کے لیے فالٹ چیلنج کی مدت (opens in a new tab) ختم ہونے کے بعد ہونی چاہیے۔

IL1StandardBridge

اس انٹرفیس کی تعریف یہاں کی گئی ہے (opens in a new tab)۔ اس فائل میں ETH کے لیے ایونٹ اور فنکشن کی تعریفیں شامل ہیں۔ یہ تعریفیں اوپر ERC-20 کے لیے IL1ERC20Bridge میں بیان کردہ تعریفوں سے بہت ملتی جلتی ہیں۔

پل کا انٹرفیس دو فائلوں کے درمیان تقسیم کیا گیا ہے کیونکہ کچھ ERC-20 ٹوکنز کو کسٹم پروسیسنگ کی ضرورت ہوتی ہے اور انہیں معیاری پل کے ذریعے نہیں سنبھالا جا سکتا۔ اس طرح وہ کسٹم پل جو ایسے ٹوکن کو سنبھالتا ہے وہ IL1ERC20Bridge کو نافذ کر سکتا ہے اور اسے ETH کو بھی پل کرنے کی ضرورت نہیں ہوتی۔

یہ ایونٹ تقریباً ERC-20 ورژن (ERC20DepositInitiated) جیسا ہی ہے، سوائے اس کے کہ اس میں لیئر ۱ (l1) اور لیئر ۲ (l2) ٹوکن کے پتے نہیں ہیں۔ یہی بات دیگر ایونٹس اور فنکشنز کے لیے بھی درست ہے۔

CrossDomainEnabled

یہ کنٹریکٹ (opens in a new tab) دونوں پلوں (لیئر ۱ (l1) اور لیئر ۲ (l2)) کو وراثت میں ملا ہے تاکہ دوسری لیئر کو پیغامات بھیجے جا سکیں۔

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

/* انٹرفیس امپورٹس */
import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol";

یہ انٹرفیس (opens in a new tab) کنٹریکٹ کو بتاتا ہے کہ کراس ڈومین میسنجر کا استعمال کرتے ہوئے دوسری لیئر کو پیغامات کیسے بھیجنے ہیں۔ یہ کراس ڈومین میسنجر ایک بالکل الگ سسٹم ہے، اور اس پر ایک الگ مضمون بنتا ہے، جو مجھے امید ہے کہ میں مستقبل میں لکھوں گا۔

وہ واحد پیرامیٹر جو کنٹریکٹ کو جاننے کی ضرورت ہے، وہ اس لیئر پر کراس ڈومین میسنجر کا پتہ ہے۔ یہ پیرامیٹر کنسٹرکٹر میں ایک بار سیٹ کیا جاتا ہے، اور کبھی تبدیل نہیں ہوتا۔

کراس ڈومین میسجنگ اس بلاک چین پر موجود کسی بھی کنٹریکٹ کے ذریعے قابل رسائی ہے جہاں یہ چل رہا ہے (چاہے وہ ایتھیریم مین نیٹ ہو یا آپٹیمزم)۔ لیکن ہمیں ہر طرف کے پل کی ضرورت ہے کہ وہ صرف مخصوص پیغامات پر بھروسہ کرے اگر وہ دوسری طرف کے پل سے آتے ہیں۔

        require(
            msg.sender == address(getCrossDomainMessenger()),
            "OVM_XCHAIN: messenger contract unauthenticated"
        );

صرف مناسب کراس ڈومین میسنجر (messenger، جیسا کہ آپ نیچے دیکھ سکتے ہیں) کے پیغامات پر بھروسہ کیا جا سکتا ہے۔


        require(
            getCrossDomainMessenger().xDomainMessageSender() == _sourceDomainAccount,
            "OVM_XCHAIN: wrong sender of cross-domain message"
        );

جس طریقے سے کراس ڈومین میسنجر وہ پتہ فراہم کرتا ہے جس نے دوسری لیئر کے ساتھ پیغام بھیجا تھا وہ .xDomainMessageSender() فنکشن (opens in a new tab) ہے۔ جب تک اسے اس ٹرانزیکشن میں کال کیا جاتا ہے جو پیغام کے ذریعے شروع کی گئی تھی، یہ یہ معلومات فراہم کر سکتا ہے۔

ہمیں یہ یقینی بنانے کی ضرورت ہے کہ ہمیں موصول ہونے والا پیغام دوسرے پل سے آیا ہے۔

یہ فنکشن کراس ڈومین میسنجر واپس کرتا ہے۔ ہم متغیر messenger کے بجائے ایک فنکشن استعمال کرتے ہیں تاکہ اس سے وراثت پانے والے کنٹریکٹس کو یہ بتانے کے لیے ایک الگورتھم استعمال کرنے کی اجازت دی جا سکے کہ کون سا کراس ڈومین میسنجر استعمال کرنا ہے۔

آخر میں، وہ فنکشن جو دوسری لیئر کو پیغام بھیجتا ہے۔

    ) internal {
        // سلدر-disable-next-line مکرر داخلہ-events, مکرر داخلہ-benign

سلدر (opens in a new tab) ایک جامد تجزیہ کار ہے جسے آپٹیمزم ہر کنٹریکٹ پر چلاتا ہے تاکہ کمزوریوں اور دیگر ممکنہ مسائل کو تلاش کیا جا سکے۔ اس صورت میں، درج ذیل لائن دو کمزوریوں کو متحرک کرتی ہے:

  1. مکرر داخلہ کے ایونٹس (opens in a new tab)
  2. بے ضرر مکرر داخلہ (opens in a new tab)
        getCrossDomainMessenger().sendMessage(_crossDomainTarget, _message, _gasLimit);
    }
}

اس صورت میں ہم مکرر داخلہ کے بارے میں فکر مند نہیں ہیں، ہم جانتے ہیں کہ getCrossDomainMessenger() ایک قابل اعتماد پتہ واپس کرتا ہے، یہاں تک کہ اگر سلدر کے پاس یہ جاننے کا کوئی طریقہ نہیں ہے۔

لیئر ۱ کا پل کنٹریکٹ

اس کنٹریکٹ کا سورس کوڈ یہاں ہے (opens in a new tab)۔

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

انٹرفیسز دیگر کنٹریکٹس کا حصہ ہو سکتے ہیں، اس لیے انہیں Solidity ورژنز کی ایک وسیع رینج کو سپورٹ کرنا ہوتا ہے۔ لیکن پل بذات خود ہمارا کنٹریکٹ ہے، اور ہم اس بارے میں سخت ہو سکتے ہیں کہ یہ کون سا Solidity ورژن استعمال کرتا ہے۔

/* انٹرفیس امپورٹس */
import { IL1StandardBridge } from "./IL1StandardBridge.sol";
import { IL1ERC20Bridge } from "./IL1ERC20Bridge.sol";

IL1ERC20Bridge اور IL1StandardBridge کی وضاحت اوپر کی گئی ہے۔

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

یہ انٹرفیس (opens in a new tab) ہمیں لیئر ۲ (l2) پر معیاری پل کو کنٹرول کرنے کے لیے پیغامات بنانے کی اجازت دیتا ہے۔

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

یہ انٹرفیس (opens in a new tab) ہمیں ERC-20 کنٹریکٹس کو کنٹرول کرنے کی اجازت دیتا ہے۔ آپ اس کے بارے میں مزید یہاں پڑھ سکتے ہیں۔

/* لائبریری امپورٹس */
import { CrossDomainEnabled } from "../../libraries/bridge/CrossDomainEnabled.sol";

جیسا کہ اوپر وضاحت کی گئی ہے، یہ کنٹریکٹ انٹرلیئر میسجنگ کے لیے استعمال ہوتا ہے۔

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

Lib_PredeployAddresses (opens in a new tab) میں لیئر ۲ (l2) کنٹریکٹس کے پتے ہیں جن کا پتہ ہمیشہ ایک ہی ہوتا ہے۔ اس میں لیئر ۲ (l2) پر معیاری پل شامل ہے۔

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

اوپن زیپلن کی ایڈریس یوٹیلیٹیز (opens in a new tab)۔ یہ کنٹریکٹ کے پتوں اور بیرونی ملکیت والے اکاؤنٹس (EOA) سے تعلق رکھنے والے پتوں کے درمیان فرق کرنے کے لیے استعمال ہوتا ہے۔

نوٹ کریں کہ یہ کوئی مکمل حل نہیں ہے، کیونکہ براہ راست کالز اور کنٹریکٹ کے کنسٹرکٹر سے کی گئی کالز کے درمیان فرق کرنے کا کوئی طریقہ نہیں ہے، لیکن کم از کم یہ ہمیں صارف کی کچھ عام غلطیوں کی نشاندہی کرنے اور انہیں روکنے کی اجازت دیتا ہے۔

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

ERC-20 معیار (opens in a new tab) کنٹریکٹ کے لیے ناکامی کی اطلاع دینے کے دو طریقوں کو سپورٹ کرتا ہے:

  1. ریورٹ
  2. false واپس کریں

دونوں صورتوں کو سنبھالنے سے ہمارا کوڈ مزید پیچیدہ ہو جائے گا، اس لیے اس کے بجائے ہم اوپن زیپلن کا SafeERC20 (opens in a new tab) استعمال کرتے ہیں، جو اس بات کو یقینی بناتا ہے کہ تمام ناکامیوں کا نتیجہ ریورٹ کی صورت میں نکلے (opens in a new tab)۔

یہ لائن بتاتی ہے کہ ہم ہر بار IERC20 انٹرفیس استعمال کرتے وقت SafeERC20 ریپر استعمال کرنے کی وضاحت کیسے کرتے ہیں۔


    /********************************
     * ایکسٹرنل کنٹریکٹ ریفرنسز *
     ********************************/

    address public l2TokenBridge;

L2StandardBridge کا پتہ۔


    // جمع شدہ لیئر ۱ (l1) ٹوکن کے بیلنس کے لیے لیئر ۱ (l1) ٹوکن کو لیئر ۲ (l2) ٹوکن سے میپ کرتا ہے
    mapping(address => mapping(address => uint256)) public deposits;

اس طرح کی ڈبل میپنگ (opens in a new tab) وہ طریقہ ہے جس سے آپ دو جہتی اسپارس ایرے (opens in a new tab) کی وضاحت کرتے ہیں۔ اس ڈیٹا اسٹرکچر میں ویلیوز کی شناخت deposit[L1 token addr][L2 token addr] کے طور پر کی جاتی ہے۔ پہلے سے طے شدہ ویلیو صفر ہے۔ صرف وہ سیلز جو مختلف ویلیو پر سیٹ ہوتے ہیں انہیں اسٹوریج میں لکھا جاتا ہے۔


    /***************
     * کنسٹرکٹر *
     ***************/

    // یہ کنٹریکٹ پراکسی کے پیچھے رہتا ہے، اس لیے کنسٹرکٹر کے پیرامیٹرز غیر استعمال شدہ رہیں گے۔
    constructor() CrossDomainEnabled(address(0)) {}

اسٹوریج میں موجود تمام متغیرات کو کاپی کیے بغیر اس کنٹریکٹ کو اپ گریڈ کرنے کے قابل ہونا۔ ایسا کرنے کے لیے ہم ایک Proxy (opens in a new tab) استعمال کرتے ہیں، ایک ایسا کنٹریکٹ جو کالز کو ایک الگ کنٹریکٹ میں منتقل کرنے کے لیے delegatecall (opens in a new tab) کا استعمال کرتا ہے جس کا پتہ پراکسی کنٹریکٹ کے ذریعے اسٹور کیا جاتا ہے (جب آپ اپ گریڈ کرتے ہیں تو آپ پراکسی کو وہ پتہ تبدیل کرنے کا کہتے ہیں)۔ جب آپ delegatecall استعمال کرتے ہیں تو اسٹوریج کال کرنے والے کنٹریکٹ کی اسٹوریج ہی رہتی ہے، اس لیے کنٹریکٹ کی حالت کے تمام متغیرات کی ویلیوز متاثر نہیں ہوتیں۔

اس پیٹرن کا ایک اثر یہ ہے کہ اس کنٹریکٹ کی اسٹوریج جو delegatecall کا کال کردہ ہے استعمال نہیں ہوتی اور اس لیے اسے پاس کی گئی کنسٹرکٹر ویلیوز کوئی اہمیت نہیں رکھتیں۔ یہی وجہ ہے کہ ہم CrossDomainEnabled کنسٹرکٹر کو ایک بے معنی ویلیو فراہم کر سکتے ہیں۔ یہ اس بات کی بھی وجہ ہے کہ ذیل میں دی گئی ابتدا کنسٹرکٹر سے الگ ہے۔

یہ سلدر ٹیسٹ (opens in a new tab) ان فنکشنز کی نشاندہی کرتا ہے جنہیں کنٹریکٹ کوڈ سے کال نہیں کیا جاتا اور اس لیے انہیں public کے بجائے external قرار دیا جا سکتا ہے۔ external فنکشنز کی گیس کی قیمت کم ہو سکتی ہے، کیونکہ انہیں کال ڈیٹا میں پیرامیٹرز فراہم کیے جا سکتے ہیں۔ public قرار دیے گئے فنکشنز کو کنٹریکٹ کے اندر سے قابل رسائی ہونا چاہیے۔ کنٹریکٹس اپنے کال ڈیٹا میں ترمیم نہیں کر سکتے، اس لیے پیرامیٹرز کو میموری میں ہونا چاہیے۔ جب ایسے فنکشن کو بیرونی طور پر کال کیا جاتا ہے، تو کال ڈیٹا کو میموری میں کاپی کرنا ضروری ہوتا ہے، جس پر گیس خرچ ہوتی ہے۔ اس صورت میں فنکشن کو صرف ایک بار کال کیا جاتا ہے، اس لیے یہ غیر موثر ہونا ہمارے لیے کوئی معنی نہیں رکھتا۔

    function initialize(address _l1messenger, address _l2TokenBridge) public {
        require(messenger == address(0), "Contract has already been initialized.");

initialize فنکشن کو صرف ایک بار کال کیا جانا چاہیے۔ اگر لیئر ۱ (l1) کراس ڈومین میسنجر یا لیئر ۲ (l2) ٹوکن پل کا پتہ تبدیل ہوتا ہے، تو ہم ایک نیا پراکسی اور ایک نیا پل بناتے ہیں جو اسے کال کرتا ہے۔ ایسا ہونے کا امکان نہیں ہے سوائے اس کے کہ جب پورے سسٹم کو اپ گریڈ کیا جائے، جو کہ بہت کم ہوتا ہے۔

نوٹ کریں کہ اس فنکشن میں ایسا کوئی طریقہ کار نہیں ہے جو اس بات کو محدود کرے کہ اسے کون کال کر سکتا ہے۔ اس کا مطلب یہ ہے کہ نظریاتی طور پر ایک حملہ آور اس وقت تک انتظار کر سکتا ہے جب تک کہ ہم پراکسی اور پل کا پہلا ورژن تعینات نہ کر دیں اور پھر جائز صارف سے پہلے initialize فنکشن تک پہنچنے کے لیے فرنٹ رننگ (opens in a new tab) کرے۔ لیکن اسے روکنے کے دو طریقے ہیں:

  1. اگر کنٹریکٹس براہ راست کسی EOA کے ذریعے نہیں بلکہ ایک ایسی ٹرانزیکشن میں تعینات کیے جاتے ہیں جس میں کوئی دوسرا کنٹریکٹ انہیں بناتا ہے (opens in a new tab) تو پورا عمل ایٹمی ہو سکتا ہے، اور کسی بھی دوسری ٹرانزیکشن کے عمل میں آنے سے پہلے ختم ہو سکتا ہے۔
  2. اگر initialize کی جائز کال ناکام ہو جاتی ہے تو نئے بنائے گئے پراکسی اور پل کو نظر انداز کرنا اور نئے بنانا ہمیشہ ممکن ہوتا ہے۔
        messenger = _l1messenger;
        l2TokenBridge = _l2TokenBridge;
    }

یہ وہ دو پیرامیٹرز ہیں جو پل کو جاننے کی ضرورت ہے۔

یہی وجہ ہے کہ ہمیں اوپن زیپلن کی Address یوٹیلیٹیز کی ضرورت تھی۔

یہ فنکشن ٹیسٹنگ کے مقاصد کے لیے موجود ہے۔ غور کریں کہ یہ انٹرفیس کی تعریفوں میں ظاہر نہیں ہوتا - یہ عام استعمال کے لیے نہیں ہے۔

یہ دونوں فنکشنز _initiateETHDeposit کے گرد ریپرز ہیں، وہ فنکشن جو اصل ETH جمع کرنے کو سنبھالتا ہے۔

کراس ڈومین پیغامات کے کام کرنے کا طریقہ یہ ہے کہ منزل کے کنٹریکٹ کو پیغام کے ساتھ اس کے کال ڈیٹا کے طور پر کال کیا جاتا ہے۔ Solidity کنٹریکٹس ہمیشہ اپنے کال ڈیٹا کی تشریح ABI کی خصوصیات (opens in a new tab) کے مطابق کرتے ہیں۔ Solidity فنکشن abi.encodeWithSelector (opens in a new tab) وہ کال ڈیٹا بناتا ہے۔

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

یہاں پیغام ان پیرامیٹرز کے ساتھ finalizeDeposit فنکشن (opens in a new tab) کو کال کرنا ہے:

پیرامیٹرویلیومطلب
_l1Tokenaddress(0)لیئر ۱ (l1) پر ETH (جو کہ ERC-20 ٹوکن نہیں ہے) کی نمائندگی کرنے کے لیے خصوصی ویلیو
_l2TokenLib_PredeployAddresses.OVM_ETHلیئر ۲ (l2) کنٹریکٹ جو آپٹیمزم پر ETH کا انتظام کرتا ہے، 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000 (یہ کنٹریکٹ صرف اندرونی آپٹیمزم کے استعمال کے لیے ہے)
_from_fromلیئر ۱ (l1) پر وہ پتہ جو ETH بھیجتا ہے
_to_toلیئر ۲ (l2) پر وہ پتہ جو ETH وصول کرتا ہے
amountmsg.valueبھیجے گئے Wei کی مقدار (جو پہلے ہی پل کو بھیجی جا چکی ہے)
_data_dataجمع کرنے کے ساتھ منسلک کرنے کے لیے اضافی ڈیٹا
        // لیئر ۲ (l2) میں کال ڈیٹا بھیجیں
        // سلدر-disable-next-line مکرر داخلہ-events
        sendCrossDomainMessage(l2TokenBridge, _l2Gas, message);

کراس ڈومین میسنجر کے ذریعے پیغام بھیجیں۔

        // سلدر-disable-next-line مکرر داخلہ-events
        emit ETHDepositInitiated(_from, _to, msg.value, _data);
    }

کسی بھی غیر مرکزی ایپلی کیشن (dapp) کو مطلع کرنے کے لیے ایک ایونٹ خارج کریں جو اس منتقلی کو سنتی ہے۔

یہ دونوں فنکشنز _initiateERC20Deposit کے گرد ریپرز ہیں، وہ فنکشن جو اصل ERC-20 جمع کرنے کو سنبھالتا ہے۔

یہ فنکشن اوپر دیے گئے _initiateETHDeposit سے ملتا جلتا ہے، جس میں چند اہم فرق ہیں۔ پہلا فرق یہ ہے کہ یہ فنکشن ٹوکن کے پتے اور منتقل کی جانے والی رقم کو پیرامیٹرز کے طور پر وصول کرتا ہے۔ ETH کے معاملے میں پل کی کال میں پہلے ہی پل کے اکاؤنٹ (msg.value) میں اثاثے کی منتقلی شامل ہوتی ہے۔

        // جب لیئر ۱ (l1) پر جمع شروع کیا جاتا ہے، تو لیئر ۱ (l1) پل مستقبل کے لیے فنڈز اپنے پاس منتقل کر لیتا ہے
        // انخلا۔ safeTransferFrom یہ بھی چیک کرتا ہے کہ آیا کنٹریکٹ میں کوڈ ہے، لہذا یہ ناکام ہو جائے گا اگر
        // _from ایک EOA یا پتہ(0) ہے۔
        // سلدر-disable-next-line مکرر داخلہ-events, مکرر داخلہ-benign
        IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount);

ERC-20 ٹوکن کی منتقلی ETH سے مختلف عمل کی پیروی کرتی ہے:

  1. صارف (_from) پل کو مناسب ٹوکن منتقل کرنے کا الاؤنس دیتا ہے۔
  2. صارف ٹوکن کنٹریکٹ کے پتے، رقم وغیرہ کے ساتھ پل کو کال کرتا ہے۔
  3. پل جمع کرنے کے عمل کے حصے کے طور پر ٹوکنز کو (خود کو) منتقل کرتا ہے۔

پہلا قدم آخری دو سے الگ ٹرانزیکشن میں ہو سکتا ہے۔ تاہم، فرنٹ رننگ کوئی مسئلہ نہیں ہے کیونکہ وہ دو فنکشنز جو _initiateERC20Deposit کو کال کرتے ہیں (depositERC20 اور depositERC20To) وہ صرف اس فنکشن کو msg.sender کے ساتھ _from پیرامیٹر کے طور پر کال کرتے ہیں۔

جمع کی گئی ٹوکنز کی رقم کو deposits ڈیٹا اسٹرکچر میں شامل کریں۔ لیئر ۲ (l2) پر ایک سے زیادہ پتے ہو سکتے ہیں جو ایک ہی لیئر ۱ (l1) ERC-20 ٹوکن سے مطابقت رکھتے ہوں، اس لیے جمع کی گئی رقوم کا ٹریک رکھنے کے لیے لیئر ۱ (l1) ERC-20 ٹوکن کے پل کے بیلنس کا استعمال کرنا کافی نہیں ہے۔

لیئر ۲ (l2) پل لیئر ۲ (l2) کراس ڈومین میسنجر کو ایک پیغام بھیجتا ہے جس کی وجہ سے لیئر ۱ (l1) کراس ڈومین میسنجر اس فنکشن کو کال کرتا ہے (یقیناً، ایک بار جب پیغام کو حتمی شکل دینے والی ٹرانزیکشن (opens in a new tab) لیئر ۱ (l1) پر جمع ہو جاتی ہے)۔

    ) external onlyFromCrossDomainAccount(l2TokenBridge) {

یقینی بنائیں کہ یہ ایک جائز پیغام ہے، جو کراس ڈومین میسنجر سے آ رہا ہے اور لیئر ۲ (l2) ٹوکن پل سے شروع ہو رہا ہے۔ یہ فنکشن پل سے ETH نکالنے کے لیے استعمال ہوتا ہے، اس لیے ہمیں یہ یقینی بنانا ہوگا کہ اسے صرف مجاز کالر کے ذریعے ہی کال کیا جائے۔

        // سلدر-disable-next-line مکرر داخلہ-events
        (bool success, ) = _to.call{ value: _amount }(new bytes(0));

ETH منتقل کرنے کا طریقہ یہ ہے کہ وصول کنندہ کو msg.value میں Wei کی مقدار کے ساتھ کال کی جائے۔

        require(success, "TransferHelper::safeTransferETH: ETH transfer failed");

        // سلدر-disable-next-line مکرر داخلہ-events
        emit ETHWithdrawalFinalized(_from, _to, _amount, _data);

انخلا کے بارے میں ایک ایونٹ خارج کریں۔

یہ فنکشن اوپر دیے گئے finalizeETHWithdrawal سے ملتا جلتا ہے، جس میں ERC-20 ٹوکنز کے لیے ضروری تبدیلیاں کی گئی ہیں۔

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

deposits ڈیٹا اسٹرکچر کو اپ ڈیٹ کریں۔

پل کا ایک ابتدائی نفاذ تھا۔ جب ہم اس نفاذ سے اس کی طرف منتقل ہوئے، تو ہمیں تمام اثاثوں کو منتقل کرنا پڑا۔ ERC-20 ٹوکنز کو صرف منتقل کیا جا سکتا ہے۔ تاہم، کسی کنٹریکٹ میں ETH منتقل کرنے کے لیے آپ کو اس کنٹریکٹ کی منظوری کی ضرورت ہوتی ہے، جو کہ donateETH ہمیں فراہم کرتا ہے۔

لیئر ۲ پر ERC-20 ٹوکنز

کسی ERC-20 ٹوکن کو معیاری پل میں فٹ ہونے کے لیے، اسے معیاری پل، اور صرف معیاری پل کو ٹوکن ڈھالنے کی اجازت دینے کی ضرورت ہوتی ہے۔ یہ ضروری ہے کیونکہ پلوں کو یہ یقینی بنانے کی ضرورت ہوتی ہے کہ آپٹیمزم پر گردش کرنے والے ٹوکنز کی تعداد لیئر ۱ (l1) پل کنٹریکٹ کے اندر مقفل ٹوکنز کی تعداد کے برابر ہو۔ اگر لیئر ۲ (l2) پر بہت زیادہ ٹوکنز ہوں تو کچھ صارفین اپنے اثاثوں کو واپس لیئر ۱ (l1) پر پل کرنے سے قاصر ہوں گے۔ ایک قابل اعتماد پل کے بجائے، ہم بنیادی طور پر فریکشنل ریزرو بینکنگ (opens in a new tab) کو دوبارہ بنائیں گے۔ اگر لیئر ۱ (l1) پر بہت زیادہ ٹوکنز ہوں، تو ان میں سے کچھ ٹوکنز ہمیشہ کے لیے پل کنٹریکٹ کے اندر مقفل رہیں گے کیونکہ لیئر ۲ (l2) ٹوکنز کو جلائے بغیر انہیں جاری کرنے کا کوئی طریقہ نہیں ہے۔

IL2StandardERC20

لیئر ۲ (l2) پر ہر ERC-20 ٹوکن جو معیاری پل کا استعمال کرتا ہے اسے یہ انٹرفیس (opens in a new tab) فراہم کرنے کی ضرورت ہوتی ہے، جس میں وہ فنکشنز اور ایونٹس ہوتے ہیں جن کی معیاری پل کو ضرورت ہوتی ہے۔

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

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

معیاری ERC-20 انٹرفیس (opens in a new tab) میں mint اور burn فنکشنز شامل نہیں ہیں۔ ان طریقوں کی ERC-20 معیار (opens in a new tab) کو ضرورت نہیں ہے، جو ٹوکن بنانے اور تباہ کرنے کے طریقہ کار کو غیر متعین چھوڑ دیتا ہے۔

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

ERC-165 انٹرفیس (opens in a new tab) یہ بتانے کے لیے استعمال ہوتا ہے کہ کنٹریکٹ کون سے فنکشنز فراہم کرتا ہے۔ آپ معیار کو یہاں پڑھ سکتے ہیں (opens in a new tab)۔

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

یہ فنکشن لیئر ۱ (l1) ٹوکن کا پتہ فراہم کرتا ہے جو اس کنٹریکٹ سے پل کیا گیا ہے۔ نوٹ کریں کہ ہمارے پاس مخالف سمت میں ایسا کوئی فنکشن نہیں ہے۔ ہمیں کسی بھی لیئر ۱ (l1) ٹوکن کو پل کرنے کے قابل ہونے کی ضرورت ہے، قطع نظر اس کے کہ جب اسے نافذ کیا گیا تھا تو لیئر ۲ (l2) سپورٹ کی منصوبہ بندی کی گئی تھی یا نہیں۔


    function mint(address _to, uint256 _amount) external;

    function burn(address _from, uint256 _amount) external;

    event Mint(address indexed _account, uint256 _amount);
    event Burn(address indexed _account, uint256 _amount);
}

ٹوکنز کو ڈھالنے (بنانے) اور جلانے (تباہ کرنے) کے فنکشنز اور ایونٹس۔ پل واحد ہستی ہونی چاہیے جو ان فنکشنز کو چلا سکے تاکہ یہ یقینی بنایا جا سکے کہ ٹوکنز کی تعداد درست ہے (لیئر ۱ (l1) پر مقفل ٹوکنز کی تعداد کے برابر)۔

L2StandardERC20

یہ IL2StandardERC20 انٹرفیس کا ہمارا نفاذ ہے (opens in a new tab)۔ جب تک کہ آپ کو کسی قسم کی کسٹم لاجک کی ضرورت نہ ہو، آپ کو اسے استعمال کرنا چاہیے۔

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

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

اوپن زیپلن کا ERC-20 کنٹریکٹ (opens in a new tab)۔ آپٹیمزم پہیے کو دوبارہ ایجاد کرنے پر یقین نہیں رکھتا، خاص طور پر جب پہیے کا اچھی طرح سے آڈٹ کیا گیا ہو اور اسے اثاثے رکھنے کے لیے کافی قابل اعتماد ہونے کی ضرورت ہو۔

import "./IL2StandardERC20.sol";

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

یہ وہ دو اضافی کنفیگریشن پیرامیٹرز ہیں جن کی ہمیں ضرورت ہوتی ہے اور ERC-20 کو عام طور پر نہیں ہوتی۔

پہلے اس کنٹریکٹ کے لیے کنسٹرکٹر کو کال کریں جس سے ہم وراثت پاتے ہیں (ERC20(_name, _symbol)) اور پھر اپنے متغیرات سیٹ کریں۔

یہ وہ طریقہ ہے جس سے ERC-165 (opens in a new tab) کام کرتا ہے۔ ہر انٹرفیس متعدد سپورٹڈ فنکشنز پر مشتمل ہوتا ہے، اور اس کی شناخت ان فنکشنز کے ABI فنکشن سلیکٹرز (opens in a new tab) کے ایکسکلوسیو اور (XOR) (opens in a new tab) کے طور پر کی جاتی ہے۔

لیئر ۲ (l2) پل ERC-165 کو ایک سینیٹی چیک کے طور پر استعمال کرتا ہے تاکہ یہ یقینی بنایا جا سکے کہ وہ ERC-20 کنٹریکٹ جس پر وہ اثاثے بھیجتا ہے وہ ایک IL2StandardERC20 ہے۔

نوٹ: بدمعاش کنٹریکٹ کو supportsInterface کے غلط جوابات فراہم کرنے سے روکنے کے لیے کچھ نہیں ہے، اس لیے یہ ایک سینیٹی چیک کا طریقہ کار ہے، حفاظتی طریقہ کار نہیں ہے۔

صرف لیئر ۲ (l2) پل کو اثاثے ڈھالنے اور جلانے کی اجازت ہے۔

_mint اور _burn دراصل اوپن زیپلن کے ERC-20 کنٹریکٹ میں بیان کیے گئے ہیں۔ وہ کنٹریکٹ انہیں بیرونی طور پر ظاہر نہیں کرتا، کیونکہ ٹوکنز کو ڈھالنے اور جلانے کی شرائط اتنی ہی مختلف ہیں جتنے ERC-20 کو استعمال کرنے کے طریقے۔

لیئر ۲ کے پل کا کوڈ

یہ وہ کوڈ ہے جو آپٹیمزم پر پل چلاتا ہے۔ اس کنٹریکٹ کا سورس یہاں ہے (opens in a new tab)۔

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

/* انٹرفیس امپورٹس */
import { IL1StandardBridge } from "../../L1/messaging/IL1StandardBridge.sol";
import { IL1ERC20Bridge } from "../../L1/messaging/IL1ERC20Bridge.sol";
import { IL2ERC20Bridge } from "./IL2ERC20Bridge.sol";

IL2ERC20Bridge (opens in a new tab) انٹرفیس اس لیئر ۱ (l1) کے مساوی سے بہت ملتا جلتا ہے جو ہم نے اوپر دیکھا تھا۔ اس میں دو اہم فرق ہیں:

  1. لیئر ۱ (l1) پر آپ جمع کرنے کا آغاز کرتے ہیں اور انخلا کو حتمی شکل دیتے ہیں۔ یہاں آپ انخلا کا آغاز کرتے ہیں اور جمع کرنے کو حتمی شکل دیتے ہیں۔
  2. لیئر ۱ (l1) پر ETH اور ERC-20 ٹوکنز کے درمیان فرق کرنا ضروری ہے۔ لیئر ۲ (l2) پر ہم دونوں کے لیے ایک ہی فنکشنز استعمال کر سکتے ہیں کیونکہ اندرونی طور پر آپٹیمزم پر ETH بیلنس کو 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000 (opens in a new tab) پتے کے ساتھ ایک ERC-20 ٹوکن کے طور پر سنبھالا جاتا ہے۔

لیئر ۱ (l1) پل کے پتے کا ٹریک رکھیں۔ نوٹ کریں کہ لیئر ۱ (l1) کے مساوی کے برعکس، یہاں ہمیں اس متغیر کی ضرورت ہے۔ لیئر ۱ (l1) پل کا پتہ پہلے سے معلوم نہیں ہوتا۔

یہ دونوں فنکشنز انخلا کا آغاز کرتے ہیں۔ نوٹ کریں کہ لیئر ۱ (l1) ٹوکن کا پتہ بتانے کی کوئی ضرورت نہیں ہے۔ توقع کی جاتی ہے کہ لیئر ۲ (l2) ٹوکنز ہمیں لیئر ۱ (l1) کے مساوی کا پتہ بتائیں گے۔

غور کریں کہ ہم _from پیرامیٹر پر انحصار نہیں کر رہے ہیں بلکہ msg.sender پر کر رہے ہیں جسے جعلی بنانا بہت مشکل ہے (جہاں تک میں جانتا ہوں، ناممکن ہے)۔


        // l1TokenBridge.finalizeERC20Withdrawal(_to, _amount) کے لیے کال ڈیٹا بنائیں
        // سلدر-disable-next-line مکرر داخلہ-events
        address l1Token = IL2StandardERC20(_l2Token).l1Token();
        bytes memory message;

        if (_l2Token == Lib_PredeployAddresses.OVM_ETH) {

لیئر ۱ (l1) پر ETH اور ERC-20 کے درمیان فرق کرنا ضروری ہے۔

اس فنکشن کو L1StandardBridge کے ذریعے کال کیا جاتا ہے۔

    ) external virtual onlyFromCrossDomainAccount(l1TokenBridge) {

یقینی بنائیں کہ پیغام کا ذریعہ جائز ہے۔ یہ اہم ہے کیونکہ یہ فنکشن _mint کو کال کرتا ہے اور اسے ایسے ٹوکنز دینے کے لیے استعمال کیا جا سکتا ہے جو ان ٹوکنز کے ذریعے کور نہیں ہوتے جو پل کی لیئر ۱ (l1) پر ملکیت میں ہیں۔

        // چیک کریں کہ ہدف ٹوکن مطابقت رکھتا ہے اور
        // تصدیق کریں کہ لیئر ۱ (l1) پر جمع شدہ ٹوکن یہاں لیئر ۲ (l2) کے جمع شدہ ٹوکن کی نمائندگی سے میل کھاتا ہے
        if (
            // سلدر-disable-next-line مکرر داخلہ-events
            ERC165Checker.supportsInterface(_l2Token, 0x1d1d8b63) &&
            _l1Token == IL2StandardERC20(_l2Token).l1Token()

سینیٹی چیکس:

  1. درست انٹرفیس سپورٹڈ ہے
  2. لیئر ۲ (l2) ERC-20 کنٹریکٹ کا لیئر ۱ (l1) پتہ ٹوکنز کے لیئر ۱ (l1) ذریعہ سے میل کھاتا ہے
        ) {
            // جب جمع کو حتمی شکل دی جاتی ہے، تو ہم لیئر ۲ (l2) پر اکاؤنٹ میں اتنی ہی رقم کے
            // ٹوکنز کریڈٹ کرتے ہیں۔
            // سلدر-disable-next-line مکرر داخلہ-events
            IL2StandardERC20(_l2Token).mint(_to, _amount);
            // سلدر-disable-next-line مکرر داخلہ-events
            emit DepositFinalized(_l1Token, _l2Token, _from, _to, _amount, _data);

اگر سینیٹی چیکس پاس ہو جاتے ہیں، تو جمع کرنے کو حتمی شکل دیں:

  1. ٹوکنز ڈھالیں
  2. مناسب ایونٹ خارج کریں

اگر کسی صارف نے غلط لیئر ۲ (l2) ٹوکن کا پتہ استعمال کر کے کوئی قابل شناخت غلطی کی ہے، تو ہم جمع کرنے کو منسوخ کرنا اور لیئر ۱ (l1) پر ٹوکنز واپس کرنا چاہتے ہیں۔ لیئر ۲ (l2) سے ہم ایسا کرنے کا واحد طریقہ یہ ہے کہ ایک پیغام بھیجیں جسے فالٹ چیلنج کی مدت کا انتظار کرنا پڑے گا، لیکن یہ صارف کے لیے ٹوکنز کو مستقل طور پر کھونے سے کہیں بہتر ہے۔

نتیجہ

معیاری پل اثاثوں کی منتقلی کے لیے سب سے لچکدار طریقہ کار ہے۔ تاہم، چونکہ یہ بہت عام ہے اس لیے اسے استعمال کرنا ہمیشہ سب سے آسان طریقہ کار نہیں ہوتا۔ خاص طور پر انخلا کے لیے، زیادہ تر صارفین تھرڈ پارٹی پلوں (opens in a new tab) کا استعمال کرنے کو ترجیح دیتے ہیں جو چیلنج کی مدت کا انتظار نہیں کرتے اور انخلا کو حتمی شکل دینے کے لیے مرکل ثبوت کی ضرورت نہیں ہوتی۔

یہ پل عام طور پر لیئر ۱ (l1) پر اثاثے رکھ کر کام کرتے ہیں، جو وہ فوری طور پر ایک چھوٹی سی فیس کے عوض فراہم کرتے ہیں (اکثر معیاری پل کے انخلا کے لیے گیس کی قیمت سے بھی کم)۔ جب پل (یا اسے چلانے والے لوگ) لیئر ۱ (l1) کے اثاثوں کی کمی کی توقع کرتے ہیں تو یہ لیئر ۲ (l2) سے کافی اثاثے منتقل کرتا ہے۔ چونکہ یہ بہت بڑے انخلا ہوتے ہیں، اس لیے انخلا کی لاگت ایک بڑی رقم پر تقسیم ہو جاتی ہے اور یہ بہت کم فیصد ہوتی ہے۔

امید ہے کہ اس مضمون نے آپ کو یہ سمجھنے میں مدد کی ہوگی کہ لیئر ۲ (l2) کیسے کام کرتی ہے، اور Solidity کوڈ کیسے لکھا جائے جو واضح اور محفوظ ہو۔

میرے مزید کام کے لیے یہاں دیکھیں (opens in a new tab)۔