মূল কন্টেন্টে যান

অপ্টিমিজম স্ট্যান্ডার্ড ব্রিজ কন্ট্রাক্ট ওয়াকথ্রু

Solidity
ব্রিজ
লেয়ার ২
ইন্টারমিডিয়েট
ওরি পোমেরান্টজ
30 মার্চ, 2022
31 মিনিট পড়া

Optimism (opens in a new tab) হলো একটি অপ্টিমেস্টিক রোলআপ। অপ্টিমেস্টিক রোলআপ ইথিরিয়াম মেইননেট (যা লেয়ার 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

  1. যদি কোনো ERC-20 ডিপোজিট করা হয়, তবে ডিপোজিটর ব্রিজকে ডিপোজিট করা পরিমাণ খরচ করার অনুমতি দেয়
  2. ডিপোজিটর L1 ব্রিজকে কল করে (depositERC20, depositERC20To, depositETH, বা depositETHTo)
  3. L1 ব্রিজ ব্রিজ করা অ্যাসেটের দখল নেয়
    • ETH: কলের অংশ হিসেবে ডিপোজিটর দ্বারা অ্যাসেট ট্রান্সফার করা হয়
    • ERC-20: ডিপোজিটরের দেওয়া অনুমতি ব্যবহার করে ব্রিজ নিজেই অ্যাসেট ট্রান্সফার করে নেয়
  4. L1 ব্রিজ ক্রস-ডোমেইন মেসেজ মেকানিজম ব্যবহার করে L2 ব্রিজে finalizeDeposit কল করে

লেয়ার 2

  1. L2 ব্রিজ যাচাই করে যে finalizeDeposit-এর কলটি বৈধ কি না:
    • ক্রস ডোমেইন মেসেজ কন্ট্রাক্ট থেকে এসেছে
    • মূলত L1-এর ব্রিজ থেকে এসেছে
  2. L2 ব্রিজ চেক করে যে L2-তে ERC-20 টোকেন কন্ট্রাক্টটি সঠিক কি না:
    • L2 কন্ট্রাক্ট রিপোর্ট করে যে এর L1 কাউন্টারপার্টটি ঠিক সেই কন্ট্রাক্ট যেখান থেকে L1-এ টোকেনগুলো এসেছে
    • L2 কন্ট্রাক্ট রিপোর্ট করে যে এটি সঠিক ইন্টারফেস সাপোর্ট করে (ERC-165 ব্যবহার করে (opens in a new tab))।
  3. যদি L2 কন্ট্রাক্টটি সঠিক হয়, তবে সঠিক এডড্রেস-এ উপযুক্ত সংখ্যক টোকেন মিন্ট করতে এটিকে কল করুন। যদি না হয়, তবে ব্যবহারকারীকে L1-এ টোকেনগুলো ক্লেইম করার অনুমতি দিতে একটি উত্তোলন প্রক্রিয়া শুরু করুন।

উত্তোলন ফ্লো

লেয়ার 2

  1. উত্তোলনকারী L2 ব্রিজকে কল করে (withdraw বা withdrawTo)
  2. L2 ব্রিজ msg.sender-এর অন্তর্গত উপযুক্ত সংখ্যক টোকেন বার্ন করে
  3. L2 ব্রিজ ক্রস-ডোমেইন মেসেজ মেকানিজম ব্যবহার করে L1 ব্রিজে finalizeETHWithdrawal বা finalizeERC20Withdrawal কল করে

লেয়ার 1

  1. L1 ব্রিজ যাচাই করে যে finalizeETHWithdrawal বা finalizeERC20Withdrawal-এর কলটি বৈধ কি না:
    • ক্রস ডোমেইন মেসেজ মেকানিজম থেকে এসেছে
    • মূলত L2-এর ব্রিজ থেকে এসেছে
  2. L1 ব্রিজ উপযুক্ত অ্যাসেট (ETH বা ERC-20) সঠিক এডড্রেস-এ ট্রান্সফার করে

লেয়ার 1 কোড

এটি সেই কোড যা L1, ইথিরিয়াম মেইননেট-এ রান করে।

IL1ERC20Bridge

এই ইন্টারফেসটি এখানে সংজ্ঞায়িত করা হয়েছে (opens in a new tab)। এতে ERC-20 টোকেন ব্রিজ করার জন্য প্রয়োজনীয় ফাংশন এবং সংজ্ঞা অন্তর্ভুক্ত রয়েছে।

// SPDX-License-Identifier: MIT

Optimism-এর বেশিরভাগ কোড MIT লাইসেন্সের অধীনে রিলিজ করা হয়েছে (opens in a new tab)

pragma solidity >0.5.0 <0.9.0;

লেখার সময় Solidity-এর সর্বশেষ ভার্সন হলো 0.8.12। 0.9.0 ভার্সন রিলিজ না হওয়া পর্যন্ত, আমরা জানি না এই কোডটি এর সাথে সামঞ্জস্যপূর্ণ হবে কি না।

/* *
 * @title IL1ERC20Bridge */
interface IL1ERC20Bridge {
    /* *********
     * ইভেন্টস *
     ********* */

    event ERC20DepositInitiated(

Optimism ব্রিজের পরিভাষায় ডিপোজিট মানে 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 (Optimism)-তে রয়েছে। অন্য দুটি chainId ভ্যালু হলো Kovan টেস্ট নেটওয়ার্ক (42) এবং Optimistic Kovan টেস্ট নেটওয়ার্ক (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 পাঠাতে দেয়।

Optimism-এ উত্তোলন (এবং 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

অন্য লেয়ার-এ মেসেজ পাঠানোর জন্য উভয় ব্রিজ (L1 এবং L2) দ্বারা এই কন্ট্রাক্টটি (opens in a new tab) ইনহেরিট করা হয়।

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

/* ইন্টারফেস ইমপোর্টস */
import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol";

এই ইন্টারফেসটি (opens in a new tab) কন্ট্রাক্টকে বলে যে কীভাবে ক্রস ডোমেইন মেসেঞ্জার ব্যবহার করে অন্য লেয়ার-এ মেসেজ পাঠাতে হয়। এই ক্রস ডোমেইন মেসেঞ্জারটি সম্পূর্ণ অন্য একটি সিস্টেম, এবং এর নিজস্ব একটি আর্টিকেল প্রাপ্য, যা আমি ভবিষ্যতে লেখার আশা করি।

কন্ট্রাক্টটির যে একটি প্যারামিটার জানা প্রয়োজন, তা হলো এই লেয়ার-এ ক্রস ডোমেইন মেসেঞ্জারের এডড্রেস। এই প্যারামিটারটি কনস্ট্রাক্টরে একবার সেট করা হয় এবং কখনো পরিবর্তন হয় না।

ক্রস ডোমেইন মেসেজিং ব্লকচেইন-এ চলমান যেকোনো কন্ট্রাক্ট দ্বারা অ্যাক্সেসযোগ্য (হয় ইথিরিয়াম মেইননেট বা Optimism)। কিন্তু আমাদের প্রতিটি দিকের ব্রিজের প্রয়োজন শুধুমাত্র নির্দিষ্ট মেসেজগুলোকে বিশ্বাস করা যদি সেগুলো অন্য দিকের ব্রিজ থেকে আসে।

        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 {
        // slither-disable-next-line reentrancy-events, reentrancy-benign

Slither (opens in a new tab) হলো একটি স্ট্যাটিক অ্যানালাইজার যা Optimism প্রতিটি কন্ট্রাক্টে দুর্বলতা এবং অন্যান্য সম্ভাব্য সমস্যা খুঁজতে রান করে। এই ক্ষেত্রে, নিচের লাইনটি দুটি দুর্বলতা ট্রিগার করে:

  1. রিএন্ট্রান্সি ইভেন্ট (opens in a new tab)
  2. বিনাইন রিএন্ট্রান্সি (opens in a new tab)
        getCrossDomainMessenger().sendMessage(_crossDomainTarget, _message, _gasLimit);
    }
}

এই ক্ষেত্রে আমরা রিএন্ট্রান্সি নিয়ে চিন্তিত নই, আমরা জানি getCrossDomainMessenger() একটি বিশ্বস্ত এডড্রেস রিটার্ন করে, যদিও Slither-এর তা জানার কোনো উপায় নেই।

The L1 bridge contract

এই কন্ট্রাক্টের সোর্স কোড এখানে রয়েছে (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";

OpenZeppelin-এর Address ইউটিলিটিজ (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 রিটার্ন করা

উভয় ক্ষেত্র পরিচালনা করা আমাদের কোডকে আরও জটিল করে তুলবে, তাই এর পরিবর্তে আমরা OpenZeppelin-এর SafeERC20 (opens in a new tab) ব্যবহার করি, যা নিশ্চিত করে যে সমস্ত ব্যর্থতার ফলে একটি রিভার্ট হয় (opens in a new tab)

/* *
 * @title L1StandardBridge
 * @dev L1 ETH এবং ERC20 ব্রিজ হলো এমন একটি কন্ট্রাক্ট যা ডিপোজিট করা L1 ফান্ড এবং L2-তে ব্যবহৃত স্ট্যান্ডার্ড টোকেনগুলো স্টোর করে। এটি একটি সংশ্লিষ্ট L2 ব্রিজ সিঙ্ক্রোনাইজ করে, এটিকে ডিপোজিট সম্পর্কে অবহিত করে এবং নতুন চূড়ান্ত হওয়া উত্তোলনের জন্য এটি লিসেন করে।
 * */
contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled {
    using SafeERC20 for IERC20;

এই লাইনটি হলো যেভাবে আমরা প্রতিবার 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 কনস্ট্রাক্টরে একটি অর্থহীন ভ্যালু প্রদান করতে পারি। এটিও একটি কারণ যে নিচের ইনিশিয়ালাইজেশনটি কনস্ট্রাক্টর থেকে আলাদা।

    /* *****************
     * ইনিশিয়ালাইজেশন *
     ***************** */

    /* *
     * @param _l1messenger ক্রস-চেইন কমিউনিকেশনের জন্য ব্যবহৃত L1 মেসেঞ্জারের ঠিকানা।
     * @param _l2TokenBridge L2 স্ট্যান্ডার্ড ব্রিজ ঠিকানা। */
    // slither-disable-next-line external-function

এই Slither টেস্ট (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;
    }

এই দুটি প্যারামিটার ব্রিজের জানা প্রয়োজন।

এই কারণেই আমাদের OpenZeppelin-এর Address ইউটিলিটিজ প্রয়োজন ছিল।

    /* *
     * @dev L2-তে কলারের ব্যালেন্সে নির্দিষ্ট পরিমাণ ETH ডিপোজিট করতে
     * কোনো ডেটা ছাড়াই এই ফাংশনটি কল করা যেতে পারে।
     * যেহেতু রিসিভ ফাংশন ডেটা নেয় না, তাই একটি রক্ষণশীল
     * ডিফল্ট পরিমাণ L2-তে ফরোয়ার্ড করা হয়। */
    receive() external payable onlyEOA {
        _initiateETHDeposit(msg.sender, msg.sender, 200_000, bytes(""));
    }

এই ফাংশনটি টেস্টিংয়ের উদ্দেশ্যে বিদ্যমান। লক্ষ্য করুন যে এটি ইন্টারফেস সংজ্ঞায় উপস্থিত নেই - এটি সাধারণ ব্যবহারের জন্য নয়।

এই দুটি ফাংশন হলো _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_ETHL2 কন্ট্রাক্ট যা Optimism-এ ETH পরিচালনা করে, 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000 (এই কন্ট্রাক্টটি শুধুমাত্র অভ্যন্তরীণ Optimism ব্যবহারের জন্য)
_from_fromL1-এর এডড্রেস যা ETH পাঠায়
_to_toL2-এর এডড্রেস যা ETH গ্রহণ করে
amountmsg.valueপাঠানো wei-এর পরিমাণ (যা ইতিমধ্যে ব্রিজে পাঠানো হয়েছে)
_data_dataডিপোজিটের সাথে যুক্ত করার জন্য অতিরিক্ত ডেটা
        // L2-তে কলডেটা পাঠান
        // slither-disable-next-line reentrancy-events
        sendCrossDomainMessage(l2TokenBridge, _l2Gas, message);

ক্রস ডোমেইন মেসেঞ্জারের মাধ্যমে মেসেজটি পাঠান।

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

এই ট্রান্সফার সম্পর্কে শোনে এমন যেকোনো ডিসেন্ট্রালাইজড এপ্লিকেশন-কে জানাতে একটি ইভেন্ট এমিট করুন।

এই দুটি ফাংশন হলো _initiateERC20Deposit-এর চারপাশের র‍্যাপার, যে ফাংশনটি প্রকৃত ERC-20 ডিপোজিট পরিচালনা করে।

এই ফাংশনটি উপরের _initiateETHDeposit-এর মতোই, তবে কিছু গুরুত্বপূর্ণ পার্থক্য রয়েছে। প্রথম পার্থক্য হলো এই ফাংশনটি টোকেন এডড্রেস এবং ট্রান্সফার করার পরিমাণ প্যারামিটার হিসেবে গ্রহণ করে। ETH-এর ক্ষেত্রে ব্রিজে কলে ইতিমধ্যে ব্রিজ একাউন্ট-এ অ্যাসেট ট্রান্সফার অন্তর্ভুক্ত থাকে (msg.value)।

        // যখন L1-এ কোনো ডিপোজিট শুরু হয়, তখন L1 ব্রিজ ভবিষ্যতের জন্য ফান্ডগুলো নিজের কাছে ট্রান্সফার করে
        // উত্তোলনের জন্য। safeTransferFrom কন্ট্রাক্টে কোড আছে কিনা তাও চেক করে, তাই এটি ব্যর্থ হবে যদি
        // _from একটি EOA বা address(0) হয়।
        // slither-disable-next-line reentrancy-events, reentrancy-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 উত্তোলন করতে ব্যবহৃত হয়, তাই আমাদের নিশ্চিত করতে হবে যে এটি শুধুমাত্র অনুমোদিত কলার দ্বারা কল করা হয়েছে।

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

ETH ট্রান্সফার করার উপায় হলো msg.value-তে wei-এর পরিমাণ দিয়ে প্রাপককে কল করা।

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

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

উত্তোলন সম্পর্কে একটি ইভেন্ট এমিট করুন।

এই ফাংশনটি উপরের finalizeETHWithdrawal-এর মতোই, ERC-20 টোকেনের জন্য প্রয়োজনীয় পরিবর্তনসহ।

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

deposits ডেটা স্ট্রাকচার আপডেট করুন।

ব্রিজের একটি পূর্ববর্তী ইমপ্লিমেন্টেশন ছিল। যখন আমরা সেই ইমপ্লিমেন্টেশন থেকে এটিতে চলে আসি, তখন আমাদের সমস্ত অ্যাসেট সরাতে হয়েছিল। ERC-20 টোকেনগুলো সহজেই সরানো যেতে পারে। তবে, একটি কন্ট্রাক্টে ETH ট্রান্সফার করতে আপনার সেই কন্ট্রাক্টের অনুমোদন প্রয়োজন, যা donateETH আমাদের প্রদান করে।

L2-তে ERC-20 টোকেন

একটি ERC-20 টোকেনকে স্ট্যান্ডার্ড ব্রিজে ফিট করার জন্য, এটিকে স্ট্যান্ডার্ড ব্রিজকে এবং শুধুমাত্র স্ট্যান্ডার্ড ব্রিজকে টোকেন মিন্ট করার অনুমতি দিতে হবে। এটি প্রয়োজনীয় কারণ ব্রিজগুলোকে নিশ্চিত করতে হবে যে Optimism-এ সার্কুলেট হওয়া টোকেনের সংখ্যা 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";

OpenZeppelin ERC-20 কন্ট্রাক্ট (opens in a new tab)। Optimism চাকা পুনরায় আবিষ্কার করায় বিশ্বাস করে না, বিশেষ করে যখন চাকাটি ভালোভাবে অডিট করা হয় এবং অ্যাসেট ধরে রাখার জন্য যথেষ্ট বিশ্বস্ত হওয়া প্রয়োজন।

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)-এর এক্সক্লুসিভ অর (opens in a new tab) হিসেবে চিহ্নিত করা হয়।

L2 ব্রিজ ERC-165-কে একটি স্যানিটি চেক হিসেবে ব্যবহার করে যাতে নিশ্চিত করা যায় যে এটি যে ERC-20 কন্ট্রাক্টে অ্যাসেট পাঠায় তা একটি IL2StandardERC20

নোট: supportsInterface-এ মিথ্যা উত্তর প্রদান করা থেকে কোনো দুর্বৃত্ত কন্ট্রাক্টকে বাধা দেওয়ার কিছু নেই, তাই এটি একটি স্যানিটি চেক মেকানিজম, কোনো সিকিউরিটি মেকানিজম নয়

শুধুমাত্র L2 ব্রিজকে অ্যাসেট মিন্ট এবং বার্ন করার অনুমতি দেওয়া হয়েছে।

_mint এবং _burn আসলে OpenZeppelin ERC-20 কন্ট্রাক্ট-এ সংজ্ঞায়িত করা হয়েছে। সেই কন্ট্রাক্টটি কেবল সেগুলোকে বাহ্যিকভাবে প্রকাশ করে না, কারণ টোকেন মিন্ট এবং বার্ন করার শর্তগুলো ERC-20 ব্যবহার করার উপায়গুলোর সংখ্যার মতোই বৈচিত্র্যময়।

L2 ব্রিজ কোড

এটি সেই কোড যা Optimism-এ ব্রিজ রান করে। এই কন্ট্রাক্টের সোর্স এখানে রয়েছে (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-তে আমরা উভয়ের জন্য একই ফাংশন ব্যবহার করতে পারি কারণ অভ্যন্তরীণভাবে Optimism-এ ETH ব্যালেন্সগুলো 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000 (opens in a new tab) এডড্রেস সহ একটি ERC-20 টোকেন হিসেবে পরিচালিত হয়।

L1 ব্রিজের এডড্রেস-এর ট্র্যাক রাখুন। মনে রাখবেন যে L1 সমতুল্যের বিপরীতে, এখানে আমাদের এই ভেরিয়েবলটি প্রয়োজন। L1 ব্রিজের এডড্রেস আগে থেকে জানা যায় না।

এই দুটি ফাংশন উত্তোলন শুরু করে। মনে রাখবেন যে L1 টোকেন এডড্রেস নির্দিষ্ট করার কোনো প্রয়োজন নেই। L2 টোকেনগুলো আমাদের L1 সমতুল্যের এডড্রেস বলবে বলে আশা করা হয়।

লক্ষ্য করুন যে আমরা _from প্যারামিটারের ওপর নির্ভর করছি না বরং msg.sender-এর ওপর নির্ভর করছি যা নকল করা অনেক কঠিন (আমার জানামতে অসম্ভব)।


        // l1TokenBridge.finalizeERC20Withdrawal(_to, _amount)-এর জন্য কলডেটা তৈরি করুন
        // slither-disable-next-line reentrancy-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 (
            // slither-disable-next-line reentrancy-events
            ERC165Checker.supportsInterface(_l2Token, 0x1d1d8b63) &&
            _l1Token == IL2StandardERC20(_l2Token).l1Token()

স্যানিটি চেক:

  1. সঠিক ইন্টারফেস সাপোর্ট করে
  2. L2 ERC-20 কন্ট্রাক্টের L1 এডড্রেস টোকেনগুলোর L1 উৎসের সাথে মেলে
        ) {
            // যখন কোনো ডিপোজিট চূড়ান্ত হয়, তখন আমরা 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);

যদি স্যানিটি চেক পাস হয়, তবে ডিপোজিট চূড়ান্ত করুন:

  1. টোকেনগুলো মিন্ট করুন
  2. উপযুক্ত ইভেন্ট এমিট করুন

যদি কোনো ব্যবহারকারী ভুল L2 টোকেন এডড্রেস ব্যবহার করে কোনো শনাক্তযোগ্য ত্রুটি করে, তবে আমরা ডিপোজিট বাতিল করতে এবং L1-এ টোকেনগুলো ফেরত দিতে চাই। L2 থেকে আমরা এটি করার একমাত্র উপায় হলো একটি মেসেজ পাঠানো যা ফল্ট চ্যালেঞ্জ পিরিয়ড পর্যন্ত অপেক্ষা করতে হবে, তবে এটি ব্যবহারকারীর জন্য টোকেনগুলো স্থায়ীভাবে হারানোর চেয়ে অনেক ভালো।

উপসংহার

স্ট্যান্ডার্ড ব্রিজ হলো অ্যাসেট ট্রান্সফারের জন্য সবচেয়ে নমনীয় মেকানিজম। তবে, এটি এত জেনেরিক হওয়ার কারণে এটি সর্বদা ব্যবহার করার সবচেয়ে সহজ মেকানিজম নয়। বিশেষ করে উত্তোলনের জন্য, বেশিরভাগ ব্যবহারকারী থার্ড পার্টি ব্রিজ (opens in a new tab) ব্যবহার করতে পছন্দ করেন যা চ্যালেঞ্জ পিরিয়ডের জন্য অপেক্ষা করে না এবং উত্তোলন চূড়ান্ত করার জন্য কোনো Merkle প্রুফ-এর প্রয়োজন হয় না।

এই ব্রিজগুলো সাধারণত L1-এ অ্যাসেট রাখার মাধ্যমে কাজ করে, যা তারা একটি ছোট ফি-এর বিনিময়ে অবিলম্বে প্রদান করে (প্রায়শই একটি স্ট্যান্ডার্ড ব্রিজ উত্তোলনের জন্য গ্যাস খরচের চেয়ে কম)। যখন ব্রিজ (বা এটি পরিচালনাকারী লোকেরা) L1 অ্যাসেট কম হওয়ার আশঙ্কা করে তখন এটি L2 থেকে পর্যাপ্ত অ্যাসেট ট্রান্সফার করে। যেহেতু এগুলো অনেক বড় উত্তোলন, তাই উত্তোলন খরচ একটি বড় পরিমাণের ওপর অ্যামর্টাইজ করা হয় এবং এটি অনেক ছোট শতাংশ হয়।

আশা করি এই আর্টিকেলটি আপনাকে লেয়ার ২ কীভাবে কাজ করে এবং কীভাবে পরিষ্কার ও সুরক্ষিত Solidity কোড লিখতে হয় সে সম্পর্কে আরও বুঝতে সাহায্য করেছে।

আমার আরও কাজের জন্য এখানে দেখুন (opens in a new tab)

পেজ সর্বশেষ আপডেট করা হয়েছে: 3 এপ্রিল, 2026

এই টিউটোরিয়ালটি কি সহায়ক ছিল?