From ac8b8962b05884b1e9bdec4b6a5ed7c9e7678a2d Mon Sep 17 00:00:00 2001 From: Joshua <62268199+minimalsm@users.noreply.github.com> Date: Sat, 14 Feb 2026 00:07:01 +0000 Subject: [PATCH] i18n(bn): translation import part 07 of 13 (23 files) --- .../index.md | 300 ++++ .../tutorials/all-you-can-cache/index.md | 867 +++++++++ .../developers/tutorials/app-plasma/index.md | 1255 ++++++++++++++ .../index.md | 131 ++ .../index.md | 585 +++++++ .../index.md | 101 ++ .../index.md | 372 ++++ .../index.md | 144 ++ .../index.md | 107 ++ .../erc-721-vyper-annotated-code/index.md | 640 +++++++ .../tutorials/erc20-annotated-code/index.md | 814 +++++++++ .../erc20-with-safety-rails/index.md | 217 +++ .../tutorials/ethereum-for-web2-auth/index.md | 886 ++++++++++ .../index.md | 156 ++ .../index.md | 102 ++ .../index.md | 1542 +++++++++++++++++ .../hello-world-smart-contract/index.md | 366 ++++ .../index.md | 145 ++ .../tutorials/how-to-mint-an-nft/index.md | 335 ++++ .../index.md | 102 ++ .../index.md | 708 ++++++++ .../index.md | 526 ++++++ .../index.md | 239 +++ 23 files changed, 10640 insertions(+) create mode 100644 public/content/translations/bn/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md create mode 100644 public/content/translations/bn/developers/tutorials/all-you-can-cache/index.md create mode 100644 public/content/translations/bn/developers/tutorials/app-plasma/index.md create mode 100644 public/content/translations/bn/developers/tutorials/calling-a-smart-contract-from-javascript/index.md create mode 100644 public/content/translations/bn/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md create mode 100644 public/content/translations/bn/developers/tutorials/deploying-your-first-smart-contract/index.md create mode 100644 public/content/translations/bn/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md create mode 100644 public/content/translations/bn/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md create mode 100644 public/content/translations/bn/developers/tutorials/eip-1271-smart-contract-signatures/index.md create mode 100644 public/content/translations/bn/developers/tutorials/erc-721-vyper-annotated-code/index.md create mode 100644 public/content/translations/bn/developers/tutorials/erc20-annotated-code/index.md create mode 100644 public/content/translations/bn/developers/tutorials/erc20-with-safety-rails/index.md create mode 100644 public/content/translations/bn/developers/tutorials/ethereum-for-web2-auth/index.md create mode 100644 public/content/translations/bn/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md create mode 100644 public/content/translations/bn/developers/tutorials/guide-to-smart-contract-security-tools/index.md create mode 100644 public/content/translations/bn/developers/tutorials/hello-world-smart-contract-fullstack/index.md create mode 100644 public/content/translations/bn/developers/tutorials/hello-world-smart-contract/index.md create mode 100644 public/content/translations/bn/developers/tutorials/how-to-implement-an-erc721-market/index.md create mode 100644 public/content/translations/bn/developers/tutorials/how-to-mint-an-nft/index.md create mode 100644 public/content/translations/bn/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md create mode 100644 public/content/translations/bn/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md create mode 100644 public/content/translations/bn/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md create mode 100644 public/content/translations/bn/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md diff --git a/public/content/translations/bn/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md b/public/content/translations/bn/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md new file mode 100644 index 00000000000..ceed418309b --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md @@ -0,0 +1,300 @@ +--- +title: "একজন পাইথন ডেভেলপারের জন্য ইথেরিয়ামের ভূমিকা, পার্ট ১" +description: "ইথেরিয়াম ডেভেলপমেন্টের একটি ভূমিকা, বিশেষ করে তাদের জন্য উপযোগী যাদের পাইথন প্রোগ্রামিং ভাষা সম্পর্কে জ্ঞান আছে" +author: Marc Garreau +lang: bn +tags: [ "python", "web3.py" ] +skill: beginner +published: 2020-09-08 +source: Snake charmers +sourceUrl: https://snakecharmers.ethereum.org/a-developers-guide-to-ethereum-pt-1/ +--- + +তাহলে, আপনি এই ইথেরিয়াম জিনিসটা সম্পর্কে শুনেছেন এবং এই রহস্যময় জগতে প্রবেশ করতে প্রস্তুত? এই পোস্টে দ্রুত কিছু ব্লকচেইন বেসিক কভার করা হবে, তারপর আপনাকে একটি সিমুলেটেড ইথেরিয়াম নোডের সাথে ইন্টারঅ্যাক্ট করানো হবে – ব্লক ডেটা পড়া, অ্যাকাউন্টের ব্যালেন্স চেক করা, এবং ট্রানজ্যাকশন পাঠানো। এর পাশাপাশি, আমরা অ্যাপ তৈরির প্রচলিত উপায় এবং এই নতুন ডিসেন্ট্রালাইজড দৃষ্টান্তের মধ্যে পার্থক্য তুলে ধরব। + +## (নমনীয়) পূর্বশর্ত {#soft-prerequisites} + +এই পোস্টটি বিস্তৃত পরিসরের ডেভেলপারদের কাছে সহজলভ্য হওয়ার আশা রাখে। [পাইথন টুলস](/developers/docs/programming-languages/python/) জড়িত থাকবে, কিন্তু সেগুলি ধারণাগুলির জন্য কেবল একটি বাহন – আপনি যদি পাইথন ডেভেলপার না হন তবে কোনো সমস্যা নেই। তবে, আপনি ইতিমধ্যে যা জানেন সে সম্পর্কে আমি কয়েকটি অনুমান করব, যাতে আমরা দ্রুত ইথেরিয়াম-নির্দিষ্ট অংশে যেতে পারি। + +অনুমান: + +- আপনি টার্মিনালে কাজ করতে পারেন, +- আপনি পাইথনের কয়েকটি লাইন কোড লিখেছেন, +- আপনার মেশিনে পাইথন সংস্করণ 3.6 বা তার বেশি ইনস্টল করা আছে (একটি [ভার্চুয়াল পরিবেশ](https://realpython.com/effective-python-environment/#virtual-environments) ব্যবহার করার জন্য দৃঢ়ভাবে উৎসাহিত করা হচ্ছে), এবং +- আপনি `pip`, পাইথনের প্যাকেজ ইনস্টলার ব্যবহার করেছেন। + আবারও, যদি এগুলোর কোনোটি অসত্য হয়, অথবা আপনি যদি এই নিবন্ধের কোডটি পুনরুৎপাদন করার পরিকল্পনা না করেন, তাহলে আপনি সম্ভবত ঠিকঠাকভাবেই অনুসরণ করতে পারবেন। + +## ব্লকচেইন, সংক্ষেপে {#blockchains-briefly} + +ইথেরিয়ামকে বর্ণনা করার অনেক উপায় আছে, কিন্তু এর হৃদয়ে রয়েছে একটি ব্লকচেইন। ব্লকচেইনগুলো ব্লকের একটি সিরিজ দিয়ে তৈরি, তাই চলুন সেখান থেকেই শুরু করা যাক। সবচেয়ে সহজ ভাষায়, ইথেরিয়াম ব্লকচেইনের প্রতিটি ব্লক হল কিছু মেটাডেটা এবং ট্রানজ্যাকশনের একটি তালিকা। JSON ফরম্যাটে, এটি দেখতে অনেকটা এইরকম: + +```json +{ + "number": 1234567, + "hash": "0xabc123...", + "parentHash": "0xdef456...", + ..., + "transactions": [...] +} +``` + +প্রতিটি [ব্লকের](/developers/docs/blocks/) তার আগের ব্লকের একটি রেফারেন্স থাকে; `parentHash` হল কেবল আগের ব্লকের হ্যাস। + +দ্রষ্টব্য: ইথেরিয়াম নিয়মিতভাবে হ্যাস ফাংশন ব্যবহার করে নির্দিষ্ট আকারের মান (“হ্যাস”) তৈরি করতে। ইথেরিয়ামে হ্যাস একটি গুরুত্বপূর্ণ ভূমিকা পালন করে, কিন্তু আপাতত আপনি সেগুলোকে নিরাপদে অনন্য আইডি হিসেবে ভাবতে পারেন। + +![প্রতিটি ব্লকের ভিতরের ডেটা সহ একটি ব্লকচেইন চিত্রিত একটি ডায়াগ্রাম](./blockchain-diagram.png) + +_একটি ব্লকচেইন মূলত একটি লিঙ্কড তালিকা; প্রতিটি ব্লকের আগের ব্লকের একটি রেফারেন্স থাকে।_ + +এই ডেটা স্ট্রাকচারটি নতুন কিছু নয়, কিন্তু নেটওয়ার্ক পরিচালনাকারী নিয়মগুলি (যেমন, পিয়ার-টু-পিয়ার প্রোটোকল) নতুন। কোনো কেন্দ্রীয় কর্তৃপক্ষ নেই; নেটওয়ার্ক টিকিয়ে রাখতে পিয়ারদের নেটওয়ার্ককে সহযোগিতা করতে হবে, এবং পরবর্তী ব্লকে কোন ট্রানজ্যাকশনগুলো অন্তর্ভুক্ত করা হবে তা নির্ধারণ করতে প্রতিযোগিতা করতে হবে। সুতরাং, যখন আপনি কোনো বন্ধুকে কিছু টাকা পাঠাতে চান, আপনাকে সেই ট্রানজ্যাকশনটি নেটওয়ার্কে ব্রডকাস্ট করতে হবে, তারপর এটি একটি আসন্ন ব্লকে অন্তর্ভুক্ত হওয়ার জন্য অপেক্ষা করতে হবে। + +এক ব্যবহারকারী থেকে অন্য ব্যবহারকারীর কাছে টাকা সত্যিই পাঠানো হয়েছে কিনা তা যাচাই করার জন্য ব্লকচেইনের একমাত্র উপায় হলো সেই ব্লকচেইনের একটি স্থানীয় মুদ্রা (অর্থাৎ, সেই ব্লকচেইন দ্বারা তৈরি এবং পরিচালিত) ব্যবহার করা। ইথেরিয়ামে, এই মুদ্রাকে বলা হয় ইথার, এবং ইথেরিয়াম ব্লকচেইনে অ্যাকাউন্টের ব্যালেন্সের একমাত্র অফিসিয়াল রেকর্ড থাকে। + +## একটি নতুন দৃষ্টান্ত {#a-new-paradigm} + +এই নতুন ডিসেন্ট্রালাইজড টেক স্ট্যাক নতুন ডেভেলপার টুলস তৈরি করেছে। এই ধরনের টুলস অনেক প্রোগ্রামিং ভাষায় বিদ্যমান, কিন্তু আমরা পাইথনের দৃষ্টিকোণ থেকে দেখব। পুনরায় বলতে গেলে: যদিও পাইথন আপনার পছন্দের ভাষা না হয়, তবুও এটি অনুসরণ করতে খুব বেশি অসুবিধা হওয়া উচিত নয়। + +যে পাইথন ডেভেলপাররা ইথেরিয়ামের সাথে ইন্টারঅ্যাক্ট করতে চান তারা সম্ভবত [Web3.py](https://web3py.readthedocs.io/) ব্যবহার করবেন। Web3.py হল একটি লাইব্রেরি যা একটি ইথেরিয়াম নোডের সাথে সংযোগ স্থাপন এবং তারপর সেখান থেকে ডেটা পাঠানো এবং গ্রহণ করার উপায়কে ব্যাপকভাবে সরল করে। + +দ্রষ্টব্য: “ইথেরিয়াম নোড” এবং “ইথেরিয়াম ক্লায়েন্ট” একে অপরের পরিবর্তে ব্যবহৃত হয়। উভয় ক্ষেত্রেই, এটি সেই সফটওয়্যারকে বোঝায় যা ইথেরিয়াম নেটওয়ার্কের একজন অংশগ্রহণকারী চালায়। এই সফটওয়্যারটি ব্লক ডেটা পড়তে পারে, চেইনে নতুন ব্লক যুক্ত হলে আপডেট পেতে পারে, নতুন ট্রানজ্যাকশন ব্রডকাস্ট করতে পারে এবং আরও অনেক কিছু করতে পারে। টেকনিক্যালি, ক্লায়েন্ট হল সফটওয়্যার, নোড হল সেই কম্পিউটার যা সফটওয়্যারটি চালায়। + +[ইথেরিয়াম ক্লায়েন্টগুলো](/developers/docs/nodes-and-clients/) [IPC](https://wikipedia.org/wiki/Inter-process_communication), HTTP, বা Websockets দ্বারা পৌঁছানোর জন্য কনফিগার করা যেতে পারে, তাই Web3.py-কে এই কনফিগারেশনটি প্রতিফলিত করতে হবে। Web3.py এই সংযোগ বিকল্পগুলিকে **প্রোভাইডার** হিসাবে উল্লেখ করে। আপনার নোডের সাথে Web3.py ইনস্ট্যান্স লিঙ্ক করার জন্য আপনাকে তিনটি প্রোভাইডারের মধ্যে একটি বেছে নিতে হবে। + +![একটি ডায়াগ্রাম যা দেখাচ্ছে কিভাবে web3.py আপনার এপ্লিকেশনকে একটি ইথেরিয়াম নোডের সাথে সংযোগ করতে IPC ব্যবহার করে](./web3py-and-nodes.png) + +_ইথেরিয়াম নোড এবং Web3.py-কে একই প্রোটোকলের মাধ্যমে যোগাযোগ করার জন্য কনফিগার করুন, যেমন, এই ডায়াগ্রামে IPC।_ + +একবার Web3.py সঠিকভাবে কনফিগার হয়ে গেলে, আপনি ব্লকচেইনের সাথে ইন্টারঅ্যাক্ট করা শুরু করতে পারেন। এখানে Web3.py ব্যবহারের কয়েকটি উদাহরণ দেওয়া হল যা ভবিষ্যতে যা আসবে তার একটি প্রিভিউ হিসাবে: + +```python +# ব্লক ডেটা পড়ুন: +w3.eth.get_block('latest') + +# একটি ট্রানজ্যাকশন পাঠান: +w3.eth.send_transaction({'from': ..., 'to': ..., 'value': ...}) +``` + +## ইনস্টলেশন {#installation} + +এই ওয়াকথ্রুতে, আমরা শুধু একটি পাইথন ইন্টারপ্রেটারের মধ্যে কাজ করব। আমরা কোনো ডিরেক্টরি, ফাইল, ক্লাস বা ফাংশন তৈরি করব না। + +দ্রষ্টব্য: নীচের উদাহরণগুলিতে, `$` দিয়ে শুরু হওয়া কমান্ডগুলি টার্মিনালে চালানোর জন্য উদ্দিষ্ট। ( `$` টাইপ করবেন না, এটি কেবল লাইনের শুরু নির্দেশ করে।) + +প্রথমে, অন্বেষণের জন্য একটি ব্যবহারকারী-বান্ধব পরিবেশের জন্য [IPython](https://ipython.org/) ইনস্টল করুন। IPython অন্যান্য বৈশিষ্ট্যের মধ্যে ট্যাব কমপ্লিশন অফার করে, যা Web3.py-এর মধ্যে কী সম্ভব তা দেখতে অনেক সহজ করে তোলে। + +```bash +pip install ipython +``` + +Web3.py `web3` নামে প্রকাশিত হয়েছে। এটি এভাবে ইনস্টল করুন: + +```bash +pip install web3 +``` + +আরও একটি জিনিস – আমরা পরে একটি ব্লকচেইন সিমুলেট করতে যাচ্ছি, যার জন্য আরও কয়েকটি নির্ভরতা প্রয়োজন। আপনি সেগুলি এর মাধ্যমে ইনস্টল করতে পারেন: + +```bash +pip install 'web3[tester]' +``` + +আপনি যাওয়ার জন্য পুরোপুরি প্রস্তুত! + +দ্রষ্টব্য: `web3[tester]` প্যাকেজটি পাইথন 3.10.xx পর্যন্ত কাজ করে + +## একটি স্যান্ডবক্স চালু করুন {#spin-up-a-sandbox} + +আপনার টার্মিনালে `ipython` চালিয়ে একটি নতুন পাইথন পরিবেশ খুলুন। এটি `python` চালানোর সমতুল্য, কিন্তু আরও অনেক ফিচার সহ আসে। + +```bash +ipython +``` + +এটি আপনার চালানো পাইথন এবং IPython এর সংস্করণ সম্পর্কে কিছু তথ্য প্রিন্ট করবে, তারপর আপনি ইনপুটের জন্য অপেক্ষারত একটি প্রম্পট দেখতে পাবেন: + +```python +In [1]: +``` + +আপনি এখন একটি ইন্টারেক্টিভ পাইথন শেল দেখছেন। মূলত, এটি খেলার জন্য একটি স্যান্ডবক্স। আপনি যদি এতদূর এসে থাকেন, তাহলে Web3.py ইম্পোর্ট করার সময় হয়েছে: + +```python +In [1]: from web3 import Web3 +``` + +## Web3 মডিউলের পরিচিতি {#introducing-the-web3-module} + +ইথেরিয়ামের একটি গেটওয়ে হওয়ার পাশাপাশি, [Web3](https://web3py.readthedocs.io/en/stable/overview.html#base-api) মডিউলটি কয়েকটি সুবিধাজনক ফাংশন অফার করে। চলুন কয়েকটি অন্বেষণ করা যাক। + +একটি ইথেরিয়াম এপ্লিকেশনে, আপনাকে সাধারণত মুদ্রার ডিনোমিনেশন রূপান্তর করতে হবে। Web3 মডিউলটি শুধুমাত্র এর জন্য কয়েকটি হেল্পার মেথড প্রদান করে: [from_wei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.from_wei) এবং [to_wei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.to_wei)। + + +দ্রষ্টব্য: কম্পিউটার দশমিক গণিত পরিচালনায় কুখ্যাতভাবে খারাপ। এটি এড়াতে, ডেভেলপাররা প্রায়শই ডলারের পরিমাণ সেন্টে সংরক্ষণ করেন। উদাহরণস্বরূপ, $5.99 মূল্যের একটি আইটেম ডেটাবেসে 599 হিসাবে সংরক্ষণ করা হতে পারে। + +ইথার-এ ট্রানজ্যাকশন পরিচালনার সময় একটি অনুরূপ প্যাটার্ন ব্যবহার করা হয়। তবে, দুটি দশমিক বিন্দুর পরিবর্তে, ইথারের 18টি আছে! ইথারের সবচেয়ে ছোট ডিনোমিনেশনকে বলা হয় wei, তাই ট্রানজ্যাকশন পাঠানোর সময় সেই মানটি নির্দিষ্ট করা হয়। + +১ ইথার = ১000000000000000000 wei + +১ wei = 0.000000000000000001 ইথার + + + +কিছু মান wei-তে এবং wei থেকে রূপান্তর করার চেষ্টা করুন। মনে রাখবেন যে ইথার এবং wei-এর মধ্যে [অনেক ডিনোমিনেশনের নাম রয়েছে](https://web3py.readthedocs.io/en/stable/troubleshooting.html#how-do-i-convert-currency-denominations)। তাদের মধ্যে সবচেয়ে পরিচিত একটি হল **gwei**, কারণ প্রায়শই ট্রানজ্যাকশন ফি এভাবেই দেখানো হয়। + +```python +In [2]: Web3.to_wei(1, 'ether') +Out[2]: 1000000000000000000 + +In [3]: Web3.from_wei(500000000, 'gwei') +Out[3]: Decimal('0.5') +``` + +Web3 মডিউলের অন্যান্য ইউটিলিটি পদ্ধতির মধ্যে রয়েছে ডেটা ফর্ম্যাট কনভার্টার (যেমন, [`toHex`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.toHex)), অ্যাড্রেস হেল্পার (যেমন, [`isAddress`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.isAddress)), এবং হ্যাস ফাংশন (যেমন, [`keccak`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.keccak))। এই সিরিজের পরবর্তী অংশে এর মধ্যে অনেকগুলি কভার করা হবে। সমস্ত উপলব্ধ মেথড এবং প্রোপার্টি দেখতে, `Web3` টাইপ করে IPython-এর অটো-কমপ্লিট ব্যবহার করুন। এবং পিরিয়ডের পরে দুবার ট্যাব কী টিপুন। + +## চেইনের সাথে কথা বলুন {#talk-to-the-chain} + +সুবিধাজনক মেথডগুলো চমৎকার, কিন্তু চলুন ব্লকচেইনের দিকে এগিয়ে যাওয়া যাক। পরবর্তী ধাপ হল একটি ইথেরিয়াম নোডের সাথে যোগাযোগের জন্য Web3.py কনফিগার করা। এখানে আমাদের কাছে IPC, HTTP, বা Websocket প্রোভাইডার ব্যবহার করার বিকল্প আছে। + +আমরা এই পথে যাব না, কিন্তু HTTP প্রোভাইডার ব্যবহার করে একটি সম্পূর্ণ ওয়ার্কফ্লোর একটি উদাহরণ এইরকম দেখতে হতে পারে: + +- একটি ইথেরিয়াম নোড ডাউনলোড করুন, যেমন, [Geth](https://geth.ethereum.org/)। +- একটি টার্মিনাল উইন্ডোতে Geth শুরু করুন এবং এটি নেটওয়ার্ক সিঙ্ক করার জন্য অপেক্ষা করুন। ডিফল্ট HTTP পোর্ট হল `8545`, কিন্তু এটি কনফিগারযোগ্য। +- Web3.py-কে HTTP এর মাধ্যমে, `localhost:8545`-এ নোডের সাথে সংযোগ করতে বলুন। + `w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))` +- `w3` ইনস্ট্যান্সটি নোডের সাথে ইন্টারঅ্যাক্ট করতে ব্যবহার করুন। + +যদিও এটি করার একটি “বাস্তব” উপায়, সিঙ্কিং প্রক্রিয়াটি কয়েক ঘন্টা সময় নেয় এবং আপনি যদি কেবল একটি ডেভেলপমেন্ট পরিবেশ চান তবে এটি অপ্রয়োজনীয়। Web3.py এই উদ্দেশ্যে একটি চতুর্থ প্রোভাইডার প্রকাশ করে, **EthereumTesterProvider**। এই টেস্টার প্রোভাইডারটি একটি সিমুলেটেড ইথেরিয়াম নোডের সাথে লিঙ্ক করে যেখানে খেলার জন্য শিথিল অনুমতি এবং জাল মুদ্রা রয়েছে। + +![একটি ডায়াগ্রাম যা EthereumTesterProvider-কে আপনার web3.py এপ্লিকেশনটিকে একটি সিমুলেটেড ইথেরিয়াম নোডের সাথে লিঙ্ক করতে দেখাচ্ছে](./ethereumtesterprovider.png) + +_EthereumTesterProvider একটি সিমুলেটেড নোডের সাথে সংযোগ স্থাপন করে এবং দ্রুত ডেভেলপমেন্ট পরিবেশের জন্য সুবিধাজনক।_ + +সেই সিমুলেটেড নোডটিকে [eth-tester](https://github.com/ethereum/eth-tester) বলা হয় এবং আমরা এটিকে `pip install web3[tester]` কমান্ডের অংশ হিসাবে ইনস্টল করেছি। এই টেস্টার প্রোভাইডারটি ব্যবহার করার জন্য Web3.py কনফিগার করা ঠিক ততটাই সহজ: + +```python +In [4]: w3 = Web3(Web3.EthereumTesterProvider()) +``` + +এখন আপনি চেইন সার্ফ করার জন্য প্রস্তুত! এটা এমন কিছু নয় যা মানুষ বলে। আমি এটা এইমাত্র বানালাম। চলুন একটি দ্রুত ট্যুর করা যাক। + +## দ্রুত ট্যুর {#the-quick-tour} + +প্রথমেই, একটি স্যানিটি চেক: + +```python +In [5]: w3.is_connected() +Out[5]: True +``` + +যেহেতু আমরা টেস্টার প্রোভাইডার ব্যবহার করছি, এটি খুব মূল্যবান পরীক্ষা নয়, কিন্তু যদি এটি ব্যর্থ হয়, তবে সম্ভবত আপনি `w3` ভ্যারিয়েবলটি ইনস্ট্যানশিয়েট করার সময় কিছু ভুল টাইপ করেছেন। ডাবল-চেক করুন যে আপনি ভিতরের বন্ধনী অন্তর্ভুক্ত করেছেন, অর্থাৎ, `Web3.EthereumTesterProvider()`। + +## ট্যুর স্টপ #১: [অ্যাকাউন্ট](/developers/docs/accounts/) {#tour-stop-1-accounts} + +সুবিধার জন্য, টেস্টার প্রোভাইডার কিছু অ্যাকাউন্ট তৈরি করেছে এবং সেগুলিকে টেস্ট ইথার দিয়ে প্রি-লোড করেছে। + +প্রথমে, আসুন সেই অ্যাকাউন্টগুলির একটি তালিকা দেখি: + +```python +In [6]: w3.eth.accounts +Out[6]: ['0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', + '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF', + '0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69', ...] +``` + +আপনি যদি এই কমান্ডটি চালান, আপনার `0x` দিয়ে শুরু হওয়া দশটি স্ট্রিংয়ের একটি তালিকা দেখতে হবে। প্রতিটি একটি **পাবলিক অ্যাড্রেস** এবং কিছু দিক থেকে, একটি চেকিং অ্যাকাউন্টের অ্যাকাউন্ট নম্বরের অনুরূপ। আপনাকে যে ইথার পাঠাতে চায় তাকে আপনি এই অ্যাড্রেসটি প্রদান করবেন। + +যেমন উল্লেখ করা হয়েছে, টেস্টার প্রোভাইডার এই প্রতিটি অ্যাকাউন্টে কিছু টেস্ট ইথার প্রি-লোড করেছে। চলুন জেনে নেওয়া যাক প্রথম অ্যাকাউন্টে কত আছে: + +```python +In [7]: w3.eth.get_balance(w3.eth.accounts[0]) +Out[7]: 1000000000000000000000000 +``` + +অনেকগুলো শূন্য! জাল ব্যাঙ্কের দিকে হাসতে হাসতে যাওয়ার আগে, আগের মুদ্রা ডিনোমিনেশন সম্পর্কিত সেই পাঠটি মনে করুন। ইথার মানগুলি সবচেয়ে ছোট ডিনোমিনেশন, wei-তে উপস্থাপিত হয়। সেটিকে ইথারে রূপান্তর করুন: + +```python +In [8]: w3.from_wei(1000000000000000000000000, 'ether') +Out[8]: Decimal('1000000') +``` + +এক মিলিয়ন টেস্ট ইথার - এখনও খুব খারাপ নয়। + +## ট্যুর স্টপ #২: ব্লক ডেটা {#tour-stop-2-block-data} + +চলুন এই সিমুলেটেড ব্লকচেইনের অবস্থা এক নজরে দেখে নেওয়া যাক: + +```python +In [9]: w3.eth.get_block('latest') +Out[9]: AttributeDict({ + 'number': 0, + 'hash': HexBytes('0x9469878...'), + 'parentHash': HexBytes('0x0000000...'), + ... + 'transactions': [] +}) +``` + +একটি ব্লক সম্পর্কে অনেক তথ্য ফেরত আসে, কিন্তু এখানে কয়েকটি বিষয় উল্লেখ করার আছে: + +- ব্লক নম্বর শূন্য - আপনি যত আগেই টেস্টার প্রোভাইডার কনফিগার করুন না কেন। বাস্তব ইথেরিয়াম নেটওয়ার্কের মতো নয়, যা প্রতি 12 সেকেন্ডে একটি নতুন ব্লক যোগ করে, এই সিমুলেশনটি আপনি এটিকে কিছু কাজ না দেওয়া পর্যন্ত অপেক্ষা করবে। +- `transactions` একটি খালি তালিকা, একই কারণে: আমরা এখনও কিছু করিনি। এই প্রথম ব্লকটি একটি **খালি ব্লক**, কেবল চেইন শুরু করার জন্য। +- লক্ষ্য করুন যে `parentHash` কেবল কয়েকটি খালি বাইট। এটি নির্দেশ করে যে এটি চেইনের প্রথম ব্লক, যা **জেনেসিস ব্লক** নামেও পরিচিত। + +## ট্যুর স্টপ #৩: [ট্রানজ্যাকশন](/developers/docs/transactions/) {#tour-stop-3-transactions} + +একটি পেন্ডিং ট্রানজ্যাকশন না হওয়া পর্যন্ত আমরা শূন্য ব্লকে আটকে আছি, তাই আসুন একটি দেওয়া যাক। এক অ্যাকাউন্ট থেকে অন্য অ্যাকাউন্টে কয়েকটি টেস্ট ইথার পাঠান: + +```python +In [10]: tx_hash = w3.eth.send_transaction({ + 'from': w3.eth.accounts[0], + 'to': w3.eth.accounts[1], + 'value': w3.to_wei(3, 'ether'), + 'gas': 21000 +}) +``` + +এটি সাধারণত সেই পয়েন্ট যেখানে আপনি আপনার ট্রানজ্যাকশনটি একটি নতুন ব্লকে অন্তর্ভুক্ত হওয়ার জন্য কয়েক সেকেন্ড অপেক্ষা করবেন। সম্পূর্ণ প্রক্রিয়াটি অনেকটা এইরকম: + +1. একটি ট্রানজ্যাকশন জমা দিন এবং ট্রানজ্যাকশন হ্যাসটি ধরে রাখুন। ট্রানজ্যাকশন ধারণকারী ব্লক তৈরি এবং ব্রডকাস্ট না হওয়া পর্যন্ত, ট্রানজ্যাকশনটি “পেন্ডিং” থাকে। + `tx_hash = w3.eth.send_transaction({ … })` +2. ট্রানজ্যাকশনটি একটি ব্লকে অন্তর্ভুক্ত হওয়ার জন্য অপেক্ষা করুন: + `w3.eth.wait_for_transaction_receipt(tx_hash)` +3. এপ্লিকেশন লজিক চালিয়ে যান। সফল ট্রানজ্যাকশন দেখতে: + `w3.eth.get_transaction(tx_hash)` + +আমাদের সিমুলেটেড পরিবেশটি সঙ্গে সঙ্গে একটি নতুন ব্লকে ট্রানজ্যাকশনটি যোগ করবে, তাই আমরা অবিলম্বে ট্রানজ্যাকশনটি দেখতে পারি: + +```python +In [11]: w3.eth.get_transaction(tx_hash) +Out[11]: AttributeDict({ + 'hash': HexBytes('0x15e9fb95dc39...'), + 'blockNumber': 1, + 'transactionIndex': 0, + 'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', + 'to': '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF', + 'value': 3000000000000000000, + ... +}) +``` + +আপনি এখানে কিছু পরিচিত বিবরণ দেখতে পাবেন: `from`, `to`, এবং `value` ফিল্ডগুলি আমাদের `send_transaction` কলের ইনপুটগুলির সাথে মিলতে হবে। অন্য আশ্বস্তকারী বিষয় হল যে এই ট্রানজ্যাকশনটি ব্লক নম্বর ১-এর মধ্যে প্রথম ট্রানজ্যাকশন (`'transactionIndex': 0`) হিসাবে অন্তর্ভুক্ত করা হয়েছিল। + +আমরা জড়িত দুটি অ্যাকাউন্টের ব্যালেন্স চেক করে এই ট্রানজ্যাকশনের সাফল্য সহজেই যাচাই করতে পারি। তিনটি ইথার একটি থেকে অন্যটিতে চলে যাওয়া উচিত। + +```python +In [12]: w3.eth.get_balance(w3.eth.accounts[0]) +Out[12]: 999996999979000000000000 + +In [13]: w3.eth.get_balance(w3.eth.accounts[1]) +Out[13]: 1000003000000000000000000 +``` + +পরেরটি ভালো দেখাচ্ছে! ব্যালেন্স ১,০০০,০০০ থেকে ১,০০০,০০৩ ইথারে চলে গেছে। কিন্তু প্রথম অ্যাকাউন্টের কী হল? মনে হচ্ছে এটি তিন ইথারের চেয়ে সামান্য বেশি হারিয়েছে। হায়, জীবনে কিছুই বিনামূল্যে নয়, এবং ইথেরিয়াম পাবলিক নেটওয়ার্ক ব্যবহার করার জন্য আপনাকে আপনার পিয়ারদের তাদের সহায়ক ভূমিকার জন্য ক্ষতিপূরণ দিতে হবে। ট্রানজ্যাকশন জমা দেওয়া অ্যাকাউন্ট থেকে একটি ছোট ট্রানজ্যাকশন ফি কেটে নেওয়া হয়েছিল - এই ফি হল পোড়ানো গ্যাসের পরিমাণ (একটি ETH ট্রান্সফারের জন্য ২১০০০ ইউনিট গ্যাস) যা নেটওয়ার্ক কার্যকলাপ অনুযায়ী পরিবর্তিত একটি বেস ফি দ্বারা গুণিত হয় এবং একটি টিপ যা সেই ভ্যালিডেটরের কাছে যায় যে ট্রানজ্যাকশনটি একটি ব্লকে অন্তর্ভুক্ত করে। + +[গ্যাস](/developers/docs/gas/#post-london) সম্পর্কে আরও + +দ্রষ্টব্য: পাবলিক নেটওয়ার্কে, ট্রানজ্যাকশন ফি নেটওয়ার্কের চাহিদা এবং আপনি কত দ্রুত একটি ট্রানজ্যাকশন প্রসেস করতে চান তার উপর ভিত্তি করে পরিবর্তনশীল। ফি কীভাবে গণনা করা হয় তার একটি ব্রেকডাউনে যদি আপনি আগ্রহী হন, তাহলে কীভাবে ট্রানজ্যাকশনগুলি একটি ব্লকে অন্তর্ভুক্ত করা হয়-এর উপর আমার আগের পোস্টটি দেখুন। + +## এবং শ্বাস নিন {#and-breathe} + +আমরা বেশ কিছুক্ষণ ধরে এটি করছি, তাই বিরতি নেওয়ার জন্য এটি যেকোনো জায়গার মতোই একটি ভালো জায়গা বলে মনে হচ্ছে। রহস্যময় জগতটি চলতে থাকে, এবং আমরা এই সিরিজের দ্বিতীয় অংশে অন্বেষণ চালিয়ে যাব। আসন্ন কিছু ধারণা: একটি বাস্তব নোডের সাথে সংযোগ, স্মার্ট কন্ট্র্যাক্ট এবং টোকেন। কোনো ফলো-আপ প্রশ্ন আছে? আমাকে জানান! আপনার প্রতিক্রিয়া প্রভাবিত করবে আমরা এখান থেকে কোথায় যাব। [টুইটার](https://twitter.com/wolovim)-এর মাধ্যমে অনুরোধ স্বাগত। diff --git a/public/content/translations/bn/developers/tutorials/all-you-can-cache/index.md b/public/content/translations/bn/developers/tutorials/all-you-can-cache/index.md new file mode 100644 index 00000000000..6b13e314a2b --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/all-you-can-cache/index.md @@ -0,0 +1,867 @@ +--- +title: "আপনি যা কিছু ক্যাশ করতে পারেন" +description: "সস্তা রোলআপ লেনদেনের জন্য কীভাবে একটি ক্যাশিং চুক্তি তৈরি এবং ব্যবহার করতে হয় তা শিখুন" +author: Ori Pomerantz +tags: [ "লেয়ার 2", "ক্যাশিং", "সংগ্রহস্থল" ] +skill: intermediate +published: 2022-09-15 +lang: bn +--- + +রোলআপ ব্যবহার করার সময়, ট্রানজ্যাকশনের একটি বাইটের খরচ একটি স্টোরেজ স্লটের খরচের চেয়ে অনেক বেশি ব্যয়বহুল। অতএব, অনচেইনে যতটা সম্ভব তথ্য ক্যাশ করা যুক্তিযুক্ত। + +এই আর্টিকেলে আপনি শিখবেন কীভাবে এমনভাবে একটি ক্যাশিং কন্ট্রাক্ট তৈরি এবং ব্যবহার করতে হয়, যাতে যে কোনো প্যারামিটার ভ্যালু যা একাধিকবার ব্যবহৃত হওয়ার সম্ভাবনা থাকে, তা ক্যাশ করা হবে এবং (প্রথমবার ব্যবহারের পরে) অনেক কম সংখ্যক বাইট দিয়ে ব্যবহারের জন্য উপলব্ধ থাকবে, এবং এই ক্যাশ ব্যবহারকারী অফচেইন কোড কীভাবে লিখতে হয়। + +আপনি যদি আর্টিকেলটি এড়িয়ে যেতে চান এবং শুধু সোর্স কোড দেখতে চান, [এটি এখানে](https://github.com/qbzzt/20220915-all-you-can-cache)। ডেভেলপমেন্ট স্ট্যাকটি হলো [Foundry](https://getfoundry.sh/introduction/installation/)। + +## সামগ্রিক ডিজাইন {#overall-design} + +সরলতার জন্য আমরা ধরে নেব যে সমস্ত ট্রানজ্যাকশন প্যারামিটার `uint256` এবং 32 বাইট দীর্ঘ। যখন আমরা একটি ট্রানজ্যাকশন পাব, আমরা প্রতিটি প্যারামিটার এইভাবে পার্স করব: + +1. যদি প্রথম বাইট `0xFF` হয়, তাহলে পরবর্তী 32 বাইটকে একটি প্যারামিটার ভ্যালু হিসাবে নিন এবং এটি ক্যাশে লিখুন। + +2. যদি প্রথম বাইট `0xFE` হয়, তাহলে পরবর্তী 32 বাইটকে একটি প্যারামিটার ভ্যালু হিসাবে নিন কিন্তু ক্যাশে লিখবেন _না_। + +3. অন্য যেকোনো ভ্যালুর জন্য, উপরের চারটি বিটকে অতিরিক্ত বাইটের সংখ্যা হিসাবে এবং নীচের চারটি বিটকে ক্যাশ কী-এর সবচেয়ে তাৎপর্যপূর্ণ বিট হিসাবে নিন। এখানে কয়েকটি উদাহরণ দেওয়া হল: + + | কলডেটায় বাইট | ক্যাশ কী | + | :-------------- | -------: | + | 0x0F | 0x0F | + | 0x10,0x10 | 0x10 | + | 0x12,0xAC | 0x02AC | + | 0x2D,0xEA, 0xD6 | 0x0DEAD6 | + +## ক্যাশ ম্যানিপুলেশন {#cache-manipulation} + +ক্যাশটি [`Cache.sol`](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol)-এ প্রয়োগ করা হয়েছে। আসুন এটি লাইন বাই লাইন পর্যালোচনা করা যাক। + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + + +contract Cache { + + bytes1 public constant INTO_CACHE = 0xFF; + bytes1 public constant DONT_CACHE = 0xFE; +``` + +এই কনস্ট্যান্টগুলো সেই বিশেষ ক্ষেত্রগুলো ব্যাখ্যা করার জন্য ব্যবহৃত হয় যেখানে আমরা সমস্ত তথ্য প্রদান করি এবং চাই যে এটি ক্যাশে লেখা হোক বা না হোক। ক্যাশে লেখার জন্য, পূর্বে অব্যবহৃত স্টোরেজ স্লটে দুটি [`SSTORE`](https://www.evm.codes/#55) অপারেশন প্রয়োজন হয়, যার প্রতিটির খরচ ২২,১০০ গ্যাস। তাই আমরা এটিকে ঐচ্ছিক করে দিয়েছি। + +```solidity + + mapping(uint => uint) public val2key; +``` + +ভ্যালু এবং তাদের কী-এর মধ্যে একটি [ম্যাপিং](https://www.geeksforgeeks.org/solidity/solidity-mappings/)। ট্রানজ্যাকশন পাঠানোর আগে ভ্যালু এনকোড করার জন্য এই তথ্যটি প্রয়োজন। + +```solidity + // অবস্থান n-এ কী n+1 এর ভ্যালু থাকে, কারণ আমাদের শূন্যকে "ক্যাশে নেই" হিসাবে + // সংরক্ষণ করতে হবে। + uint[] public key2val; +``` + +আমরা কী থেকে ভ্যালু ম্যাপিংয়ের জন্য একটি অ্যারে ব্যবহার করতে পারি, কারণ আমরা কীগুলো বরাদ্দ করি এবং সরলতার জন্য আমরা এটি ক্রমানুসারে করি। + +```solidity + function cacheRead(uint _key) public view returns (uint) { + require(_key <= key2val.length, "প্রাথমিকীকরণ না করা ক্যাশ এন্ট্রি পড়া হচ্ছে"); + return key2val[_key-1]; + } // ক্যাশরিড +``` + +ক্যাশ থেকে একটি ভ্যালু পড়ুন। + +```solidity + // যদি ক্যাশে কোনো ভ্যালু আগে থেকে না থাকে তবে সেটি লিখুন + // শুধুমাত্র টেস্ট কাজ করার জন্য পাবলিক করা হয়েছে + function cacheWrite(uint _value) public returns (uint) { + // যদি ভ্যালুটি ইতিমধ্যে ক্যাশে থাকে, তাহলে বর্তমান কী রিটার্ন করুন + if (val2key[_value] != 0) { + return val2key[_value]; + } +``` + +একই ভ্যালু একাধিকবার ক্যাশে রাখার কোনো মানে হয় না। যদি ভ্যালুটি ইতিমধ্যে সেখানে থাকে, তবে বিদ্যমান কী-টি রিটার্ন করুন। + +```solidity + // যেহেতু 0xFE একটি বিশেষ ক্ষেত্র, ক্যাশ যে সবচেয়ে বড় কী ধারণ করতে + // পারে তা হলো 0x0D এবং এর পরে ১৫টি 0xFF। যদি ক্যাশের দৈর্ঘ্য ইতিমধ্যে ততটা + // বড় হয়, তাহলে ব্যর্থ হবে। + // 1 2 3 4 5 6 7 8 9 A B C D E F + require(key2val.length+1 < 0x0DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + "ক্যাশ ওভারফ্লো"); +``` + +আমার মনে হয় না আমরা কখনও এত বড় একটি ক্যাশ পাব (প্রায় 1.8\*1037টি এন্ট্রি, যা সঞ্চয় করতে প্রায় 1027 টিবি প্রয়োজন হবে)। তবে, ["640kB সবসময় যথেষ্ট হবে"](https://quoteinvestigator.com/2011/09/08/640k-enough/) এই কথাটা মনে করার মতো যথেষ্ট বয়স আমার হয়েছে। এই টেস্টটির খরচ খুব কম। + +```solidity + // পরবর্তী কী ব্যবহার করে ভ্যালুটি লিখুন + val2key[_value] = key2val.length+1; +``` + +রিভার্স লুকআপ যোগ করুন (ভ্যালু থেকে কী-তে)। + +```solidity + key2val.push(_value); +``` + +ফরওয়ার্ড লুকআপ যোগ করুন (কী থেকে ভ্যালু-তে)। যেহেতু আমরা ক্রমানুসারে ভ্যালু বরাদ্দ করি, তাই আমরা এটিকে শেষ অ্যারে ভ্যালুর পরে যোগ করতে পারি। + +```solidity + return key2val.length; + } // ক্যাশরাইট +``` + +`key2val`-এর নতুন দৈর্ঘ্য রিটার্ন করুন, যা হলো সেই সেল যেখানে নতুন ভ্যালুটি সংরক্ষণ করা হয়েছে। + +```solidity + function _calldataVal(uint startByte, uint length) + private pure returns (uint) +``` + +এই ফাংশনটি কলডেটা থেকে যেকোনো দৈর্ঘ্যের (৩২ বাইট পর্যন্ত, যা একটি ওয়ার্ড সাইজ) একটি ভ্যালু পড়ে। + +```solidity + { + uint _retVal; + + require(length < 0x21, + "_calldataVal দৈর্ঘ্যের সীমা 32 বাইট"); + require(length + startByte <= msg.data.length, + "_calldataVal কলডেটাসাইজের বাইরে পড়ার চেষ্টা করছে"); +``` + +এই ফাংশনটি ইন্টারনাল, তাই যদি বাকি কোড সঠিকভাবে লেখা হয় তবে এই টেস্টগুলো প্রয়োজন নেই। তবে, এগুলোর খরচ বেশি নয় তাই আমরা এগুলো রাখতেই পারি। + +```solidity + assembly { + _retVal := calldataload(startByte) + } +``` + +এই কোডটি [Yul](https://docs.soliditylang.org/en/v0.8.16/yul.html)-এ লেখা। এটি কলডেটা থেকে একটি 32 বাইটের ভ্যালু পড়ে। `startByte+32`-এর আগে কলডেটা শেষ হয়ে গেলেও এটি কাজ করে, কারণ EVM-এ ইনিশিয়ালাইজ না করা স্পেসকে শূন্য হিসাবে বিবেচনা করা হয়। + +```solidity + _retVal = _retVal >> (256-length*8); +``` + +আমাদের যে সবসময় একটি 32 বাইটের ভ্যালু প্রয়োজন, তা নয়। এটি অতিরিক্ত বাইটগুলো সরিয়ে দেয়। + +```solidity + return _retVal; + } // _কলডেটাভ্যাল + + + // _fromByte থেকে শুরু করে কলডেটা থেকে একটি একক প্যারামিটার পড়ুন + function _readParam(uint _fromByte) internal + returns (uint _nextByte, uint _parameterValue) + { +``` + +কলডেটা থেকে একটি একক প্যারামিটার পড়ুন। লক্ষ্য করুন যে আমাদের শুধুমাত্র পড়া ভ্যালুটিই নয়, পরবর্তী বাইটের অবস্থানও রিটার্ন করতে হবে, কারণ প্যারামিটারগুলো 1 বাইট থেকে 33 বাইট দীর্ঘ হতে পারে। + +```solidity + // প্রথম বাইটটি আমাদের বলে দেয় বাকি অংশ কীভাবে ব্যাখ্যা করতে হবে + uint8 _firstByte; + + _firstByte = uint8(_calldataVal(_fromByte, 1)); +``` + +Solidity সম্ভাব্য বিপজ্জনক [অন্তর্নিহিত টাইপ রূপান্তর](https://docs.soliditylang.org/en/v0.8.16/types.html#implicit-conversions) নিষিদ্ধ করে বাগের সংখ্যা কমানোর চেষ্টা করে। একটি ডাউনগ্রেড, উদাহরণস্বরূপ 256 বিট থেকে 8 বিটে, সুস্পষ্ট হতে হবে। + +```solidity + + // Read the value, but do not write it to the cache + if (_firstByte == uint8(DONT_CACHE)) + return(_fromByte+33, _calldataVal(_fromByte+1, 32)); + + // Read the value, and write it to the cache + if (_firstByte == uint8(INTO_CACHE)) { + uint _param = _calldataVal(_fromByte+1, 32); + cacheWrite(_param); + return(_fromByte+33, _param); + } + + // If we got here it means that we need to read from the cache + + // Number of extra bytes to read + uint8 _extraBytes = _firstByte / 16; +``` + +নিচের [নিবল](https://en.wikipedia.org/wiki/Nibble)টি নিন এবং ক্যাশ থেকে ভ্যালুটি পড়ার জন্য এটিকে অন্য বাইটগুলির সাথে একত্রিত করুন। + +```solidity + uint _key = (uint256(_firstByte & 0x0F) << (8*_extraBytes)) + + _calldataVal(_fromByte+1, _extraBytes); + + return (_fromByte+_extraBytes+1, cacheRead(_key)); + + } // _readParam + + + // Read n parameters (functions know how many parameters they expect) + function _readParams(uint _paramNum) internal returns (uint[] memory) { +``` + +আমরা কলডেটা থেকেই আমাদের প্যারামিটারের সংখ্যা জানতে পারতাম, কিন্তু যে ফাংশনগুলো আমাদের কল করে তারা জানে যে তারা কতগুলো প্যারামিটার আশা করে। তাদের কাছ থেকে জেনে নেওয়া সহজ। + +```solidity + // The parameters we read + uint[] memory params = new uint[](_paramNum); + + // Parameters start at byte 4, before that it's the function signature + uint _atByte = 4; + + for(uint i=0; i<_paramNum; i++) { + (_atByte, params[i]) = _readParam(_atByte); + } +``` + +আপনার প্রয়োজনীয় সংখ্যক প্যারামিটার না পাওয়া পর্যন্ত সেগুলো পড়তে থাকুন। আমরা যদি কলডেটার শেষ অতিক্রম করি, তাহলে `_readParams` কলটি বাতিল করে দেবে। + +```solidity + + return(params); + } // readParams + + // For testing _readParams, test reading four parameters + function fourParam() public + returns (uint256,uint256,uint256,uint256) + { + uint[] memory params; + params = _readParams(4); + return (params[0], params[1], params[2], params[3]); + } // fourParam +``` + +Foundry-এর একটি বড় সুবিধা হলো এটি আপনাকে Solidity-তে টেস্ট লিখতে দেয় ([নিচে ক্যাশের টেস্টিং দেখুন](#testing-the-cache))। এটি ইউনিট টেস্টকে অনেক সহজ করে তোলে। এটি একটি ফাংশন যা চারটি প্যারামিটার পড়ে এবং সেগুলোকে রিটার্ন করে যাতে টেস্টটি যাচাই করতে পারে যে সেগুলো সঠিক ছিল। + +```solidity + // Get a value, return bytes that will encode it (using the cache if possible) + function encodeVal(uint _val) public view returns(bytes memory) { +``` + +`encodeVal` একটি ফাংশন যা অফচেইন কোডকে কল করে ক্যাশ ব্যবহার করে এমন কলডেটা তৈরি করতে সাহায্য করে। এটি একটি একক ভ্যালু গ্রহণ করে এবং এটিকে এনকোড করা বাইটগুলো রিটার্ন করে। এই ফাংশনটি একটি `view`, তাই এটির জন্য কোনো ট্রানজ্যাকশনের প্রয়োজন হয় না এবং বাইরে থেকে কল করা হলে কোনো গ্যাস খরচ হয় না। + +```solidity + uint _key = val2key[_val]; + + // The value isn't in the cache yet, add it + if (_key == 0) + return bytes.concat(INTO_CACHE, bytes32(_val)); +``` + +[EVM](/developers/docs/evm/)-এ সমস্ত ইনিশিয়ালাইজ না করা স্টোরেজকে শূন্য বলে ধরে নেওয়া হয়। তাই যদি আমরা এমন একটি ভ্যালুর কী খুঁজি যা সেখানে নেই, আমরা একটি শূন্য পাই। সেই ক্ষেত্রে, এটিকে এনকোড করা বাইটগুলো হলো `INTO_CACHE` (যাতে এটি পরেরবার ক্যাশ করা হবে), যার পরে আসল ভ্যালুটি থাকে। + +```solidity + // If the key is <0x10, return it as a single byte + if (_key < 0x10) + return bytes.concat(bytes1(uint8(_key))); +``` + +একক বাইট সবচেয়ে সহজ। আমরা কেবল [`bytes.concat`](https://docs.soliditylang.org/en/v0.8.16/types.html#the-functions-bytes-concat-and-string-concat) ব্যবহার করে একটি `bytes` টাইপকে একটি বাইট অ্যারেতে পরিণত করি যা যেকোনো দৈর্ঘ্যের হতে পারে। নাম সত্ত্বেও, এটি শুধুমাত্র একটি আর্গুমেন্ট প্রদান করা হলেও ঠিকঠাক কাজ করে। + +```solidity + // Two byte value, encoded as 0x1vvv + if (_key < 0x1000) + return bytes.concat(bytes2(uint16(_key) | 0x1000)); +``` + +যখন আমাদের কাছে 163 এর চেয়ে কম একটি কী থাকে, তখন আমরা এটিকে দুটি বাইটে প্রকাশ করতে পারি। আমরা প্রথমে `_key`, যা একটি 256 বিটের ভ্যালু, কে একটি 16 বিটের ভ্যালুতে রূপান্তর করি এবং প্রথম বাইটে অতিরিক্ত বাইটের সংখ্যা যোগ করার জন্য লজিক্যাল or ব্যবহার করি। তারপর আমরা এটিকে একটি `bytes2` ভ্যালুতে পরিণত করি, যা `bytes`-এ রূপান্তরিত হতে পারে। + +```solidity + // There is probably a clever way to do the following lines as a loop, + // but it's a view function so I'm optimizing for programmer time and + // simplicity. + + if (_key < 16*256**2) + return bytes.concat(bytes3(uint24(_key) | (0x2 * 16 * 256**2))); + if (_key < 16*256**3) + return bytes.concat(bytes4(uint32(_key) | (0x3 * 16 * 256**3))); + . + . + . + if (_key < 16*256**14) + return bytes.concat(bytes15(uint120(_key) | (0xE * 16 * 256**14))); + if (_key < 16*256**15) + return bytes.concat(bytes16(uint128(_key) | (0xF * 16 * 256**15))); +``` + +অন্যান্য ভ্যালুগুলো (3 বাইট, 4 বাইট, ইত্যাদি) একইভাবে পরিচালনা করা হয়, শুধুমাত্র বিভিন্ন ফিল্ড আকারের সাথে। + +```solidity + // If we get here, something is wrong. + revert("Error in encodeVal, should not happen"); +``` + +যদি আমরা এখানে আসি তার মানে আমরা একটি কী পেয়েছি যা 16\*25615 এর চেয়ে কম নয়। কিন্তু `cacheWrite` কীগুলোকে সীমিত করে যাতে আমরা 14\*25616 পর্যন্তও পৌঁছাতে পারি না (যার প্রথম বাইট হবে 0xFE, তাই এটি `DONT_CACHE` এর মতো দেখাবে)। তবে ভবিষ্যতের কোনো প্রোগ্রামার যদি কোনো বাগ তৈরি করে তবে তার জন্য একটি পরীক্ষা যোগ করতে আমাদের বেশি খরচ হবে না। + +```solidity + } // encodeVal + +} // Cache +``` + +### ক্যাশের টেস্টিং {#testing-the-cache} + +Foundry-এর একটি সুবিধা হলো [এটি আপনাকে Solidity-তে টেস্ট লিখতে দেয়](https://getfoundry.sh/forge/tests/overview/), যা ইউনিট টেস্ট লেখা সহজ করে তোলে। `Cache` ক্লাসের জন্য টেস্টগুলো [এখানে](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/Cache.t.sol) আছে। যেহেতু টেস্টিং কোডটি পুনরাবৃত্তিমূলক, যেমনটা টেস্টে হয়ে থাকে, তাই এই নিবন্ধে শুধুমাত্র আকর্ষণীয় অংশগুলো ব্যাখ্যা করা হয়েছে। + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; + + +// Need to run `forge test -vv` for the console. +import "forge-std/console.sol"; +``` + +এটি শুধুমাত্র একটি বয়লারপ্লেট যা টেস্ট প্যাকেজ এবং `console.log` ব্যবহারের জন্য প্রয়োজন। + +```solidity +import "src/Cache.sol"; +``` + +আমরা যে কন্ট্রাক্টটি পরীক্ষা করছি তা আমাদের জানতে হবে। + +```solidity +contract CacheTest is Test { + Cache cache; + + function setUp() public { + cache = new Cache(); + } +``` + +`setUp` ফাংশনটি প্রতিটি পরীক্ষার আগে কল করা হয়। এই ক্ষেত্রে আমরা কেবল একটি নতুন ক্যাশ তৈরি করি, যাতে আমাদের পরীক্ষাগুলো একে অপরকে প্রভাবিত না করে। + +```solidity + function testCaching() public { +``` + +টেস্টগুলো হলো সেই ফাংশন যাদের নাম `test` দিয়ে শুরু হয়। এই ফাংশনটি মৌলিক ক্যাশ কার্যকারিতা পরীক্ষা করে, ভ্যালু লিখে এবং সেগুলো আবার পড়ে। + +```solidity + for(uint i=1; i<5000; i++) { + cache.cacheWrite(i*i); + } + + for(uint i=1; i<5000; i++) { + assertEq(cache.cacheRead(i), i*i); +``` + +এইভাবে আপনি [`assert...` ফাংশন](https://getfoundry.sh/reference/forge-std/std-assertions/) ব্যবহার করে আসল পরীক্ষাটি করেন। এই ক্ষেত্রে, আমরা পরীক্ষা করি যে আমরা যে মানটি লিখেছি সেটিই আমরা পড়েছি। আমরা `cache.cacheWrite`-এর ফলাফল বাতিল করতে পারি কারণ আমরা জানি যে ক্যাশ কীগুলি রৈখিকভাবে নির্ধারিত হয়। + +```solidity + } + } // testCaching + + + // Cache the same value multiple times, ensure that the key stays + // the same + function testRepeatCaching() public { + for(uint i=1; i<100; i++) { + uint _key1 = cache.cacheWrite(i); + uint _key2 = cache.cacheWrite(i); + assertEq(_key1, _key2); + } +``` + +প্রথমে আমরা প্রতিটি মান ক্যাশে দুইবার লিখি এবং নিশ্চিত করি যে কীগুলি একই আছে (অর্থাৎ দ্বিতীয় লেখাটি আসলে ঘটেনি)। + +```solidity + for(uint i=1; i<100; i+=3) { + uint _key = cache.cacheWrite(i); + assertEq(_key, i); + } + } // testRepeatCaching +``` + +তাত্ত্বিকভাবে এমন একটি বাগ থাকতে পারে যা পরপর ক্যাশ লেখাকে প্রভাবিত করে না। সুতরাং এখানে আমরা কিছু লেখা করি যা পরপর নয় এবং দেখি যে মানগুলি এখনও পুনরায় লেখা হয়নি। + +```solidity + // Read a uint from a memory buffer (to make sure we get back the parameters + // we sent out) + function toUint256(bytes memory _bytes, uint256 _start) internal pure + returns (uint256) +``` + +একটি `bytes memory` বাফার থেকে একটি 256 বিটের শব্দ পড়ুন। এই ইউটিলিটি ফাংশনটি আমাদের যাচাই করতে দেয় যে আমরা যখন ক্যাশ ব্যবহার করে একটি ফাংশন কল চালাই তখন আমরা সঠিক ফলাফল পাই। + +```solidity + { + require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); + uint256 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x20), _start)) + } +``` + +Yul `uint256` এর বাইরে ডেটা কাঠামো সমর্থন করে না, তাই যখন আপনি একটি আরও পরিশীলিত ডেটা কাঠামো উল্লেখ করেন, যেমন মেমরি বাফার `_bytes`, আপনি সেই কাঠামোর অ্যাড্রেস পান। Solidity `bytes memory` মানগুলিকে একটি 32 বাইটের শব্দ হিসাবে সংরক্ষণ করে যার মধ্যে দৈর্ঘ্য থাকে, যার পরে আসল বাইটগুলি থাকে, তাই `_start` নম্বর বাইট পেতে আমাদের `_bytes+32+_start` গণনা করতে হবে। + +```solidity + + return tempUint; + } // toUint256 + + // Function signature for fourParams(), courtesy of + // https://www.4byte.directory/signatures/?bytes4_signature=0x3edc1e6d + bytes4 constant FOUR_PARAMS = 0x3edc1e6d; + + // Just some constant values to see we're getting the correct values back + uint256 constant VAL_A = 0xDEAD60A7; + uint256 constant VAL_B = 0xBEEF; + uint256 constant VAL_C = 0x600D; + uint256 constant VAL_D = 0x600D60A7; +``` + +পরীক্ষার জন্য আমাদের কিছু ধ্রুবক প্রয়োজন। + +```solidity + function testReadParam() public { +``` + +`readParams` ব্যবহার করে এমন একটি ফাংশন `fourParams()` কল করুন, যাতে আমরা প্যারামিটারগুলি সঠিকভাবে পড়তে পারি কিনা তা পরীক্ষা করা যায়। + +```solidity + address _cacheAddr = address(cache); + bool _success; + bytes memory _callInput; + bytes memory _callOutput; +``` + +আমরা ক্যাশ ব্যবহার করে একটি ফাংশন কল করার জন্য সাধারণ ABI মেকানিজম ব্যবহার করতে পারি না, তাই আমাদের নিম্ন স্তরের [`
.call()`](https://docs.soliditylang.org/en/v0.8.16/types.html#members-of-addresses) মেকানিজম ব্যবহার করতে হবে। সেই মেকানিজম ইনপুট হিসাবে একটি `bytes memory` নেয়, এবং আউটপুট হিসাবে সেটি (পাশাপাশি একটি বুলিয়ান মান) ফেরত দেয়। + +```solidity + // First call, the cache is empty + _callInput = bytes.concat( + FOUR_PARAMS, +``` + +একই চুক্তির জন্য ক্যাশ করা ফাংশন (লেনদেন থেকে সরাসরি কলের জন্য) এবং নন-ক্যাশ করা ফাংশন (অন্যান্য স্মার্ট চুক্তি থেকে কলের জন্য) উভয়ই সমর্থন করা দরকারী। এটি করার জন্য আমাদের সঠিক ফাংশন কল করার জন্য Solidity মেকানিজমের উপর নির্ভর করা চালিয়ে যেতে হবে, সবকিছু একটি [ফলব্যাক ফাংশনে](https://docs.soliditylang.org/en/v0.8.16/contracts.html#fallback-function) রাখার পরিবর্তে। এটি করা কম্পোজেবিলিটিকে অনেক সহজ করে তোলে। একটি একক বাইট বেশিরভাগ ক্ষেত্রে ফাংশন সনাক্ত করার জন্য যথেষ্ট হবে, তাই আমরা তিনটি বাইট (16\*3=48 গ্যাস) অপচয় করছি। তবে, আমি যখন এটি লিখছি তখন সেই 48 গ্যাসের দাম 0.07 সেন্ট, যা সহজ, কম বাগ প্রবণ কোডের জন্য একটি যুক্তিসঙ্গত খরচ। + +```solidity + // First value, add it to the cache + cache.INTO_CACHE(), + bytes32(VAL_A), +``` + +প্রথম মান: একটি ফ্ল্যাগ যা বলে যে এটি একটি সম্পূর্ণ মান যা ক্যাশে লিখতে হবে, যার পরে মানের 32 বাইট থাকে। অন্য তিনটি মান একই রকম, তবে `VAL_B` ক্যাশে লেখা হয় না এবং `VAL_C` তৃতীয় এবং চতুর্থ উভয় প্যারামিটার। + +```solidity + . + . + . + ); + (_success, _callOutput) = _cacheAddr.call(_callInput); +``` + +এখানে আমরা আসলে `Cache` চুক্তি কল করি। + +```solidity + assertEq(_success, true); +``` + +আমরা আশা করি কলটি সফল হবে। + +```solidity + assertEq(cache.cacheRead(1), VAL_A); + assertEq(cache.cacheRead(2), VAL_C); +``` + +আমরা একটি খালি ক্যাশ দিয়ে শুরু করি এবং তারপর `VAL_A` এবং তারপর `VAL_C` যোগ করি। আমরা আশা করব প্রথমটির কী 1 হবে, এবং দ্বিতীয়টির 2 হবে। + +``` + assertEq(toUint256(_callOutput,0), VAL_A); + assertEq(toUint256(_callOutput,32), VAL_B); + assertEq(toUint256(_callOutput,64), VAL_C); + assertEq(toUint256(_callOutput,96), VAL_C); +``` + +আউটপুট হল চারটি প্যারামিটার। এখানে আমরা যাচাই করি যে এটি সঠিক। + +```solidity + // Second call, we can use the cache + _callInput = bytes.concat( + FOUR_PARAMS, + + // First value in the Cache + bytes1(0x01), +``` + +১৬ এর নিচে ক্যাশ কীগুলি মাত্র এক বাইটের হয়। + +```solidity + // Second value, don't add it to the cache + cache.DONT_CACHE(), + bytes32(VAL_B), + + // Third and fourth values, same value + bytes1(0x02), + bytes1(0x02) + ); + . + . + . + } // testReadParam +``` + +কল করার পরের পরীক্ষাগুলি প্রথম কলের পরের পরীক্ষাগুলির মতোই। + +```solidity + function testEncodeVal() public { +``` + +এই ফাংশনটি `testReadParam` এর মতোই, তবে এখানে প্যারামিটারগুলি স্পষ্টভাবে লেখার পরিবর্তে আমরা `encodeVal()` ব্যবহার করি। + +```solidity + . + . + . + _callInput = bytes.concat( + FOUR_PARAMS, + cache.encodeVal(VAL_A), + cache.encodeVal(VAL_B), + cache.encodeVal(VAL_C), + cache.encodeVal(VAL_D) + ); + . + . + . + assertEq(_callInput.length, 4+1*4); + } // testEncodeVal +``` + +`testEncodeVal()`-এ একমাত্র অতিরিক্ত পরীক্ষা হলো `_callInput`-এর দৈর্ঘ্য সঠিক কিনা তা যাচাই করা। প্রথম কলের জন্য এটি 4+33_4। দ্বিতীয়টির জন্য, যেখানে প্রতিটি মান ইতিমধ্যে ক্যাশে রয়েছে, এটি 4+1_4। + +```solidity + // Test encodeVal when the key is more than a single byte + // Maximum three bytes because filling the cache to four bytes takes + // too long. + function testEncodeValBig() public { + // Put a number of values in the cache. + // To keep things simple, use key n for value n. + for(uint i=1; i<0x1FFF; i++) { + cache.cacheWrite(i); + } +``` + +উপরের `testEncodeVal` ফাংশনটি ক্যাশে কেবল চারটি মান লেখে, তাই [ফাংশনের যে অংশটি মাল্টি-বাইট মানগুলির সাথে কাজ করে](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol#L144-L171) তা পরীক্ষা করা হয় না। কিন্তু সেই কোডটি জটিল এবং ভুল-প্রবণ। + +এই ফাংশনের প্রথম অংশটি একটি লুপ যা 1 থেকে 0x1FFF পর্যন্ত সমস্ত মান ক্যাশে ক্রমানুসারে লেখে, যাতে আমরা সেই মানগুলিকে এনকোড করতে পারি এবং জানতে পারি যে সেগুলি কোথায় যাচ্ছে। + +```solidity + . + . + . + + _callInput = bytes.concat( + FOUR_PARAMS, + cache.encodeVal(0x000F), // One byte 0x0F + cache.encodeVal(0x0010), // Two bytes 0x1010 + cache.encodeVal(0x0100), // Two bytes 0x1100 + cache.encodeVal(0x1000) // Three bytes 0x201000 + ); +``` + +এক বাইট, দুই বাইট, এবং তিন বাইটের মান পরীক্ষা করুন। আমরা এর বাইরে পরীক্ষা করি না কারণ পর্যাপ্ত স্ট্যাক এন্ট্রি (অন্তত 0x10000000, প্রায় এক চতুর্থাংশ বিলিয়ন) লিখতে অনেক সময় লাগবে। + +```solidity + . + . + . + . + } // testEncodeValBig + + + // Test what with an excessively small buffer we get a revert + function testShortCalldata() public { +``` + +যখন পর্যাপ্ত প্যারামিটার না থাকে তখন অস্বাভাবিক ক্ষেত্রে কী ঘটে তা পরীক্ষা করুন। + +```solidity + . + . + . + (_success, _callOutput) = _cacheAddr.call(_callInput); + assertEq(_success, false); + } // testShortCalldata +``` + +যেহেতু এটি রিভার্ট হয়, তাই আমাদের যে ফলাফল পাওয়া উচিত তা হলো `false`। + +``` + // Call with cache keys that aren't there + function testNoCacheKey() public { + . + . + . + _callInput = bytes.concat( + FOUR_PARAMS, + + // First value, add it to the cache + cache.INTO_CACHE(), + bytes32(VAL_A), + + // Second value + bytes1(0x0F), + bytes2(0x1234), + bytes11(0xA10102030405060708090A) + ); +``` + +এই ফাংশনটি চারটি সম্পূর্ণ বৈধ প্যারামিটার পায়, তবে ক্যাশটি খালি হওয়ায় সেখানে পড়ার জন্য কোনো মান নেই। + +```solidity + . + . + . + // Test what with an excessively long buffer everything works file + function testLongCalldata() public { + address _cacheAddr = address(cache); + bool _success; + bytes memory _callInput; + bytes memory _callOutput; + + // First call, the cache is empty + _callInput = bytes.concat( + FOUR_PARAMS, + + // First value, add it to the cache + cache.INTO_CACHE(), bytes32(VAL_A), + + // Second value, add it to the cache + cache.INTO_CACHE(), bytes32(VAL_B), + + // Third value, add it to the cache + cache.INTO_CACHE(), bytes32(VAL_C), + + // Fourth value, add it to the cache + cache.INTO_CACHE(), bytes32(VAL_D), + + // And another value for "good luck" + bytes4(0x31112233) + ); +``` + +এই ফাংশনটি পাঁচটি মান পাঠায়। আমরা জানি যে পঞ্চম মানটি উপেক্ষা করা হয় কারণ এটি একটি বৈধ ক্যাশ এন্ট্রি নয়, যা অন্তর্ভুক্ত না হলে একটি রিভার্ট ঘটাত। + +```solidity + (_success, _callOutput) = _cacheAddr.call(_callInput); + assertEq(_success, true); + . + . + . + } // testLongCalldata + +} // CacheTest + +``` + +## একটি নমুনা অ্যাপ্লিকেশন {#a-sample-app} + +Solidity-তে টেস্ট লেখা খুবই ভালো, কিন্তু দিনের শেষে একটি dapp-কে চেইনের বাইরে থেকে অনুরোধ প্রক্রিয়া করতে সক্ষম হতে হবে যাতে এটি কার্যকর হয়। এই নিবন্ধটি `WORM` দিয়ে একটি dapp-এ ক্যাশিং কীভাবে ব্যবহার করতে হয় তা প্রদর্শন করে, যার অর্থ "Write Once, Read Many"। যদি একটি কী এখনও লেখা না থাকে, আপনি তাতে একটি মান লিখতে পারেন। যদি কীটি ইতিমধ্যে লেখা থাকে, আপনি একটি রিভার্ট পাবেন। + +### চুক্তিটি {#the-contract} + +[এটি হলো চুক্তি](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/WORM.sol)। এটি মূলত `Cache` এবং `CacheTest` দিয়ে আমরা যা করেছি তার পুনরাবৃত্তি করে, তাই আমরা কেবল আকর্ষণীয় অংশগুলি কভার করব। + +```solidity +import "./Cache.sol"; + +contract WORM is Cache { +``` + +`Cache` ব্যবহার করার সবচেয়ে সহজ উপায় হল আমাদের নিজস্ব চুক্তিতে এটি ইনহেরিট করা। + +```solidity + function writeEntryCached() external { + uint[] memory params = _readParams(2); + writeEntry(params[0], params[1]); + } // writeEntryCached +``` + +এই ফাংশনটি উপরের `CacheTest`-এর `fourParam`-এর মতোই। যেহেতু আমরা ABI স্পেসিফিকেশন অনুসরণ করি না, তাই ফাংশনে কোনো প্যারামিটার ঘোষণা না করাই ভালো। + +```solidity + // Make it easier to call us + // Function signature for writeEntryCached(), courtesy of + // https://www.4byte.directory/signatures/?bytes4_signature=0xe4e4f2d3 + bytes4 constant public WRITE_ENTRY_CACHED = 0xe4e4f2d3; +``` + +`writeEntryCached` কল করা বাহ্যিক কোডকে ম্যানুয়ালি কলডেটা তৈরি করতে হবে, `worm.writeEntryCached` ব্যবহার করার পরিবর্তে, কারণ আমরা ABI স্পেসিফিকেশন অনুসরণ করি না। এই ধ্রুবক মানটি কেবল এটি লেখা সহজ করে তোলে। + +মনে রাখবেন যে যদিও আমরা `WRITE_ENTRY_CACHED`-কে একটি স্টেট ভেরিয়েবল হিসাবে সংজ্ঞায়িত করি, তবে এটি বাহ্যিকভাবে পড়ার জন্য এটির জন্য গেটার ফাংশন ব্যবহার করা প্রয়োজন, `worm.WRITE_ENTRY_CACHED()`। + +```solidity + function readEntry(uint key) public view + returns (uint _value, address _writtenBy, uint _writtenAtBlock) +``` + +পড়ার ফাংশনটি একটি `view`, তাই এটির জন্য কোনো লেনদেনের প্রয়োজন হয় না এবং কোনো গ্যাস খরচ হয় না। ফলস্বরূপ, প্যারামিটারের জন্য ক্যাশ ব্যবহার করার কোনো সুবিধা নেই। ভিউ ফাংশনগুলির সাথে স্ট্যান্ডার্ড মেকানিজম ব্যবহার করা ভালো যা সহজতর। + +### টেস্টিং কোড {#the-testing-code} + +[এটি হলো চুক্তির জন্য টেস্টিং কোড](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/WORM.t.sol)। আবার, চলুন কেবল যা আকর্ষণীয় তা দেখি। + +```solidity + function testWReadWrite() public { + worm.writeEntry(0xDEAD, 0x60A7); + + vm.expectRevert(bytes("entry already written")); + worm.writeEntry(0xDEAD, 0xBEEF); +``` + +[এটি (`vm.expectRevert`)](https://book.getfoundry.sh/cheatcodes/expect-revert#expectrevert) হলো যেভাবে আমরা একটি Foundry টেস্টে নির্দিষ্ট করি যে পরবর্তী কলটি ব্যর্থ হওয়া উচিত, এবং ব্যর্থতার জন্য রিপোর্ট করা কারণ। এটি প্রযোজ্য যখন আমরা `.` সিনট্যাক্স ব্যবহার করি।()` কলডেটা তৈরি করে এবং নিম্ন স্তরের ইন্টারফেস (`.call()`, ইত্যাদি) ব্যবহার করে চুক্তি কল করার পরিবর্তে। + +```solidity + function testReadWriteCached() public { + uint cacheGoat = worm.cacheWrite(0x60A7); +``` + +এখানে আমরা এই সত্যটি ব্যবহার করি যে `cacheWrite` ক্যাশ কী ফেরত দেয়। এটি এমন কিছু নয় যা আমরা উৎপাদনে ব্যবহার করার আশা করব, কারণ `cacheWrite` স্টেট পরিবর্তন করে, এবং তাই কেবল একটি লেনদেনের সময় কল করা যেতে পারে। লেনদেনের কোনো রিটার্ন মান থাকে না, যদি তাদের ফলাফল থাকে তবে সেই ফলাফলগুলি ইভেন্ট হিসাবে নির্গত হওয়ার কথা। তাই `cacheWrite` রিটার্ন মান কেবল অনচেইন কোড থেকে অ্যাক্সেসযোগ্য, এবং অনচেইন কোডের জন্য প্যারামিটার ক্যাশিংয়ের প্রয়োজন নেই। + +```solidity + (_success,) = address(worm).call(_callInput); +``` + +এইভাবে আমরা Solidity-কে বলি যে যদিও `.call()`-এর দুটি রিটার্ন মান রয়েছে, আমরা কেবল প্রথমটির প্রতি যত্নশীল। + +```solidity + (_success,) = address(worm).call(_callInput); + assertEq(_success, false); +``` + +যেহেতু আমরা নিম্ন স্তরের `
.call()` ফাংশন ব্যবহার করি, আমরা `vm.expectRevert()` ব্যবহার করতে পারি না এবং কল থেকে পাওয়া বুলিয়ান সাফল্য মানের দিকে তাকাতে হবে। + +```solidity + event EntryWritten(uint indexed key, uint indexed value); + + . + . + . + + _callInput = bytes.concat( + worm.WRITE_ENTRY_CACHED(), worm.encodeVal(a), worm.encodeVal(b)); + vm.expectEmit(true, true, false, false); + emit EntryWritten(a, b); + (_success,) = address(worm).call(_callInput); +``` + +এটি সেই উপায় যা আমরা যাচাই করি যে কোডটি Foundry-তে [সঠিকভাবে একটি ইভেন্ট নির্গত করে](https://getfoundry.sh/reference/cheatcodes/expect-emit/)। + +### ক্লায়েন্ট {#the-client} + +একটি জিনিস যা আপনি Solidity পরীক্ষার সাথে পান না তা হল JavaScript কোড যা আপনি আপনার নিজের অ্যাপ্লিকেশনে কাট এবং পেস্ট করতে পারেন। সেই কোডটি লেখার জন্য আমি WORM-কে [Optimism Goerli](https://community.optimism.io/docs/useful-tools/networks/#optimism-goerli)-তে ডিপ্লয় করেছি, যা [Optimism](https://www.optimism.io/)-এর নতুন টেস্টনেট। এটি ঠিকানা [`0xd34335b1d818cee54e3323d3246bd31d94e6a78a`](https://goerli-optimism.etherscan.io/address/0xd34335b1d818cee54e3323d3246bd31d94e6a78a)-এ রয়েছে। + +[আপনি ক্লায়েন্টের জন্য JavaScript কোড এখানে দেখতে পারেন](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/javascript/index.js)। এটি ব্যবহার করতে: + +1. গিট রিপোজিটরি ক্লোন করুন: + + ```sh + git clone https://github.com/qbzzt/20220915-all-you-can-cache.git + ``` + +2. প্রয়োজনীয় প্যাকেজ ইনস্টল করুন: + + ```sh + cd javascript + yarn + ``` + +3. কনফিগারেশন ফাইলটি অনুলিপি করুন: + + ```sh + cp .env.example .env + ``` + +4. আপনার কনফিগারেশনের জন্য `.env` সম্পাদনা করুন: + + | প্যারামিটার | মান | + | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | স্মৃতিচিহ্ন | একটি অ্যাকাউন্টের জন্য নেমোনিক যার একটি লেনদেনের জন্য অর্থ প্রদানের জন্য পর্যাপ্ত ETH আছে। [আপনি এখানে Optimism Goerli নেটওয়ার্কের জন্য বিনামূল্যে ETH পেতে পারেন](https://optimismfaucet.xyz/)। | + | OPTIMISM_GOERLI_URL | Optimism Goerli-এর URL। পাবলিক এন্ডপয়েন্ট, `https://goerli.optimism.io`, রেট সীমিত কিন্তু আমাদের এখানে যা প্রয়োজন তার জন্য যথেষ্ট | + +5. `index.js` চালান। + + ```sh + node index.js + ``` + + এই নমুনা অ্যাপ্লিকেশনটি প্রথমে WORM-এ একটি এন্ট্রি লেখে, কলডেটা এবং Etherscan-এ লেনদেনের একটি লিঙ্ক প্রদর্শন করে। তারপরে এটি সেই এন্ট্রিটি আবার পড়ে, এবং এটি যে কী ব্যবহার করে এবং এন্ট্রিতে থাকা মানগুলি (মান, ব্লক নম্বর এবং লেখক) প্রদর্শন করে। + +ক্লায়েন্টের বেশিরভাগই সাধারণ Dapp জাভাস্ক্রিপ্ট। তাই আবার আমরা কেবল আকর্ষণীয় অংশগুলি নিয়ে আলোচনা করব। + +```javascript +. +. +. +const main = async () => { + const func = await worm.WRITE_ENTRY_CACHED() + + // Need a new key every time + const key = await worm.encodeVal(Number(new Date())) +``` + +একটি প্রদত্ত স্লটে কেবল একবারই লেখা যায়, তাই আমরা স্লটগুলি পুনরায় ব্যবহার না করার জন্য টাইমস্ট্যাম্প ব্যবহার করি। + +```javascript +const val = await worm.encodeVal("0x600D") + +// Write an entry +const calldata = func + key.slice(2) + val.slice(2) +``` + +Ethers আশা করে যে কল ডেটা একটি হেক্স স্ট্রিং হবে, `0x` যার পরে একটি জোড় সংখ্যক হেক্সাডেসিমেল ডিজিট থাকবে। যেহেতু `key` এবং `val` উভয়ই `0x` দিয়ে শুরু হয়, তাই আমাদের সেই হেডারগুলি সরাতে হবে। + +```javascript +const tx = await worm.populateTransaction.writeEntryCached() +tx.data = calldata + +sentTx = await wallet.sendTransaction(tx) +``` + +Solidity টেস্টিং কোডের মতোই, আমরা সাধারণত একটি ক্যাশ করা ফাংশন কল করতে পারি না। পরিবর্তে, আমাদের একটি নিম্ন স্তরের প্রক্রিয়া ব্যবহার করতে হবে। + +```javascript + . + . + . + // Read the entry just written + const realKey = '0x' + key.slice(4) // remove the FF flag + const entryRead = await worm.readEntry(realKey) + . + . + . +``` + +এন্ট্রি পড়ার জন্য আমরা সাধারণ প্রক্রিয়া ব্যবহার করতে পারি। `view` ফাংশনগুলির সাথে প্যারামিটার ক্যাশিং ব্যবহার করার কোনো প্রয়োজন নেই। + +## উপসংহার {#conclusion} + +এই নিবন্ধের কোডটি একটি ধারণার প্রমাণ, উদ্দেশ্য হলো ধারণাটি বোঝা সহজ করা। একটি উৎপাদন-প্রস্তুত সিস্টেমের জন্য আপনি কিছু অতিরিক্ত কার্যকারিতা প্রয়োগ করতে চাইতে পারেন: + +- `uint256` নয় এমন মানগুলি হ্যান্ডেল করুন। উদাহরণস্বরূপ, স্ট্রিং। +- একটি গ্লোবাল ক্যাশের পরিবর্তে, ব্যবহারকারী এবং ক্যাশের মধ্যে একটি ম্যাপিং থাকতে পারে। বিভিন্ন ব্যবহারকারী বিভিন্ন মান ব্যবহার করেন। +- ঠিকানার জন্য ব্যবহৃত মানগুলি অন্যান্য উদ্দেশ্যে ব্যবহৃত মানগুলি থেকে পৃথক। শুধুমাত্র ঠিকানাগুলির জন্য একটি পৃথক ক্যাশ থাকা বুদ্ধিমানের কাজ হতে পারে। +- বর্তমানে, ক্যাশ কীগুলি একটি "প্রথম আসা, সবচেয়ে ছোট কী" অ্যালগরিদমে রয়েছে। প্রথম ষোলটি মান একটি একক বাইট হিসাবে পাঠানো যেতে পারে। পরবর্তী 4080টি মান দুটি বাইট হিসাবে পাঠানো যেতে পারে। পরবর্তী প্রায় মিলিয়ন মান তিন বাইটের, ইত্যাদি। একটি প্রোডাকশন সিস্টেমের ক্যাশ এন্ট্রিগুলিতে ব্যবহারের কাউন্টার রাখা উচিত এবং সেগুলিকে পুনর্গঠন করা উচিত যাতে ষোলটি _সবচেয়ে সাধারণ_ মান এক বাইট, পরবর্তী 4080টি সবচেয়ে সাধারণ মান দুই বাইট ইত্যাদি হয়। + + তবে, এটি একটি সম্ভাব্য বিপজ্জনক অপারেশন। নিম্নলিখিত ঘটনাগুলির ক্রম কল্পনা করুন: + + 1. নোয়াম নাইভ `encodeVal` কল করে যে ঠিকানায় সে টোকেন পাঠাতে চায় তা এনকোড করার জন্য। সেই ঠিকানাটি অ্যাপ্লিকেশনে ব্যবহৃত প্রথমগুলির মধ্যে একটি, তাই এনকোড করা মানটি হলো 0x06। এটি একটি `view` ফাংশন, কোনো লেনদেন নয়, তাই এটি নোয়াম এবং সে যে নোড ব্যবহার করে তার মধ্যে, এবং অন্য কেউ এ সম্পর্কে জানে না + + 2. ওয়েন ওনার ক্যাশ পুনর্বিন্যাস অপারেশন চালায়। খুব কম লোকই আসলে সেই ঠিকানাটি ব্যবহার করে, তাই এটি এখন 0x201122 হিসাবে এনকোড করা হয়েছে। একটি ভিন্ন মান, 1018, 0x06 বরাদ্দ করা হয়েছে। + + 3. নোয়াম নাইভ তার টোকেনগুলি 0x06-এ পাঠায়। সেগুলো `0x0000000000000000000000000de0b6b3a7640000` অ্যাড্রেসে চলে যায়, এবং যেহেতু সেই অ্যাড্রেসের প্রাইভেট কী কেউ জানে না, তাই সেগুলো সেখানেই আটকে থাকে। নোয়াম _খুশি নয়_। + + এই সমস্যা এবং এর সাথে সম্পর্কিত ক্যাশ রিঅর্ডারের সময় মেমপুলে থাকা ট্রানজ্যাকশনের সমস্যা সমাধানের উপায় আছে, কিন্তু আপনাকে এ বিষয়ে সচেতন থাকতে হবে। + +আমি এখানে Optimism-এর সাথে ক্যাশিং প্রদর্শন করেছি, কারণ আমি একজন Optimism কর্মচারী এবং এটিই আমার সবচেয়ে পরিচিত রোলআপ। কিন্তু এটি যেকোনো রোলআপের সাথে কাজ করা উচিত যা অভ্যন্তরীণ প্রক্রিয়াকরণের জন্য একটি ন্যূনতম খরচ নেয়, যাতে তুলনায় লেনদেন ডেটা L1-এ লেখা প্রধান ব্যয় হয়। + +[আমার আরও কাজের জন্য এখানে দেখুন](https://cryptodocguy.pro/)। + diff --git a/public/content/translations/bn/developers/tutorials/app-plasma/index.md b/public/content/translations/bn/developers/tutorials/app-plasma/index.md new file mode 100644 index 00000000000..8793f4fde3c --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/app-plasma/index.md @@ -0,0 +1,1255 @@ +--- +title: "একটি অ্যাপ-নির্দিষ্ট প্লাসমা লিখুন যা গোপনীয়তা রক্ষা করে" +description: "এই টিউটোরিয়ালে, আমরা ডিপোজিটের জন্য একটি আধা-গোপন ব্যাংক তৈরি করি। ব্যাংকটি একটি কেন্দ্রীভূত উপাদান; এটি প্রতিটি ব্যবহারকারীর ব্যালেন্স জানে। তবে, এই তথ্য অনচেইনে সংরক্ষণ করা হয় না। পরিবর্তে, ব্যাংক স্টেটের একটি হ্যাস পোস্ট করে। যখনই কোনো লেনদেন ঘটে, ব্যাংকটি নতুন হ্যাস পোস্ট করে, সঙ্গে একটি জিরো-নলেজ প্রুফ দিয়ে থাকে যে এটিতে একটি স্বাক্ষরিত লেনদেন রয়েছে যা হ্যাস স্টেটকে নতুনটিতে পরিবর্তন করে। এই টিউটোরিয়ালটি পড়ার পরে, আপনি কেবল কীভাবে জিরো-নলেজ প্রুফ ব্যবহার করবেন তা-ই বুঝবেন না, বরং কেন আপনি সেগুলি ব্যবহার করবেন এবং কীভাবে নিরাপদে তা করবেন তাও বুঝবেন।" +author: Ori Pomerantz +tags: [ "জিরো-নলেজ", "সার্ভার", "অফচেইন", "গোপনীয়তা" ] +skill: advanced +lang: bn +published: 2025-10-15 +--- + +## ভূমিকা {#introduction} + +[রোলআপ](/developers/docs/scaling/zk-rollups/) এর বিপরীতে, [প্লাসমা](/developers/docs/scaling/plasma) ইন্টিগ্রিটির জন্য ইথেরিয়াম মেইননেট ব্যবহার করে, কিন্তু অ্যাভেইলেবিলিটির জন্য নয়। এই নিবন্ধে, আমরা এমন একটি অ্যাপ্লিকেশন লিখি যা প্লাজমার মতো আচরণ করে, যেখানে ইথেরিয়াম ইন্টিগ্রিটির (কোনো অননুমোদিত পরিবর্তন নয়) নিশ্চয়তা দেয় কিন্তু অ্যাভেইলেবিলিটির নয় (একটি কেন্দ্রীভূত উপাদান ডাউন হয়ে পুরো সিস্টেমটিকে নিষ্ক্রিয় করতে পারে)। + +এখানে আমরা যে অ্যাপ্লিকেশনটি লিখি তা হল একটি গোপনীয়তা-সংরক্ষণকারী ব্যাংক। বিভিন্ন অ্যাড্রেসের ব্যালেন্সসহ অ্যাকাউন্ট থাকে এবং তারা অন্য অ্যাকাউন্টে টাকা (ETH) পাঠাতে পারে। ব্যাংক স্টেটের (অ্যাকাউন্ট এবং তাদের ব্যালেন্স) এবং লেনদেনের হ্যাস পোস্ট করে, কিন্তু প্রকৃত ব্যালেন্স অফচেইনে রাখে যেখানে সেগুলি ব্যক্তিগত থাকতে পারে। + +## ডিজাইন {#design} + +এটি কোনো প্রোডাকশন-রেডি সিস্টেম নয়, বরং একটি শিক্ষামূলক টুল। যেমন, এটি বেশ কয়েকটি সরলীকরণমূলক ধারণার উপর ভিত্তি করে লেখা হয়েছে। + +- নির্দিষ্ট অ্যাকাউন্ট পুল। এখানে একটি নির্দিষ্ট সংখ্যক অ্যাকাউন্ট রয়েছে এবং প্রতিটি অ্যাকাউন্ট একটি পূর্বনির্ধারিত অ্যাড্রেসের অন্তর্গত। এটি একটি অনেক সহজ সিস্টেম তৈরি করে কারণ জিরো-নলেজ প্রুফে পরিবর্তনশীল-আকারের ডেটা স্ট্রাকচার পরিচালনা করা কঠিন। একটি প্রোডাকশন-রেডি সিস্টেমের জন্য, আমরা স্টেট হ্যাস হিসাবে [মার্কল রুট](/developers/tutorials/merkle-proofs-for-offline-data-integrity/) ব্যবহার করতে পারি এবং প্রয়োজনীয় ব্যালেন্সের জন্য মার্কল প্রুফ প্রদান করতে পারি। + +- মেমরি স্টোরেজ। একটি প্রোডাকশন সিস্টেমে, রিস্টার্টের ক্ষেত্রে সেগুলি সংরক্ষণের জন্য আমাদের সমস্ত অ্যাকাউন্ট ব্যালেন্স ডিস্কে লিখতে হবে। এখানে, তথ্য যদি সহজভাবে হারিয়ে যায় তবে ঠিক আছে। + +- শুধুমাত্র ট্রান্সফার। একটি প্রোডাকশন সিস্টেমে ব্যাংকে সম্পদ জমা করার এবং সেগুলি উত্তোলন করার একটি উপায় প্রয়োজন হবে। কিন্তু এখানে উদ্দেশ্য শুধু ধারণাটি চিত্রিত করা, তাই এই ব্যাংক শুধুমাত্র ট্রান্সফারে সীমাবদ্ধ। + +### জিরো-নলেজ প্রুফ {#zero-knowledge-proofs} + +একটি মৌলিক স্তরে, একটি জিরো-নলেজ প্রুফ দেখায় যে প্রোভার কিছু ডেটা, _Dataprivate_ জানে, যেমন কিছু পাবলিক ডেটা, _Datapublic_, এবং _Dataprivate_ এর মধ্যে একটি সম্পর্ক _Relationship_ রয়েছে। ভেরিফায়ার _Relationship_ এবং _Datapublic_ জানে। + +গোপনীয়তা রক্ষা করতে, আমাদের স্টেট এবং লেনদেনগুলি ব্যক্তিগত হতে হবে। কিন্তু ইন্টিগ্রিটি নিশ্চিত করতে, আমাদের স্টেটের [ক্রিপ্টোগ্রাফিক হ্যাস](https://en.wikipedia.org/wiki/Cryptographic_hash_function) পাবলিক হতে হবে। যারা লেনদেন জমা দেয় তাদের কাছে প্রমাণ করার জন্য যে সেই লেনদেনগুলি সত্যিই ঘটেছে, আমাদের লেনদেন হ্যাস পোস্ট করতে হবে। + +বেশিরভাগ ক্ষেত্রে, _Dataprivate_ হল জিরো-নলেজ প্রুফ প্রোগ্রামের ইনপুট এবং _Datapublic_ হল আউটপুট। + +_Dataprivate_-এর এই ফিল্ডগুলি: + +- _Staten_, পুরনো স্টেট +- _Staten+1_, নতুন স্টেট +- _Transaction_, একটি লেনদেন যা পুরনো স্টেট থেকে নতুনটিতে পরিবর্তিত হয়। এই লেনদেনে এই ফিল্ডগুলি অন্তর্ভুক্ত করা প্রয়োজন: + - _Destination address_ যা ট্রান্সফার গ্রহণ করে + - _Amount_ যা ট্রান্সফার করা হচ্ছে + - _Nonce_ প্রতিটি লেনদেন শুধুমাত্র একবার প্রক্রিয়াজাত করা যাবে তা নিশ্চিত করার জন্য। + উৎস অ্যাড্রেস লেনদেনে থাকার প্রয়োজন নেই, কারণ এটি সিগনেচার থেকে পুনরুদ্ধার করা যায়। +- _Signature_, একটি সিগনেচার যা লেনদেন সম্পাদনের জন্য অনুমোদিত। আমাদের ক্ষেত্রে, একটি লেনদেন সম্পাদনের জন্য অনুমোদিত একমাত্র অ্যাড্রেস হল উৎস অ্যাড্রেস। যেহেতু আমাদের জিরো-নলেজ সিস্টেম যেভাবে কাজ করে, সেহেতু ইথেরিয়াম সিগনেচার ছাড়াও আমাদের অ্যাকাউন্টের পাবলিক কী প্রয়োজন। + +_Datapublic_-এর ফিল্ডগুলি হল: + +- _Hash(Staten)_ পুরনো স্টেটের হ্যাস +- _Hash(Staten+1)_ নতুন স্টেটের হ্যাস +- _Hash(Transaction)_ হল সেই লেনদেনের হ্যাস যা স্টেটকে _Staten_ থেকে _Staten+1_-এ পরিবর্তন করে। + +সম্পর্কটি বেশ কয়েকটি শর্ত পরীক্ষা করে: + +- পাবলিক হ্যাসগুলো প্রকৃতপক্ষে ব্যক্তিগত ফিল্ডগুলোর জন্য সঠিক হ্যাস। +- লেনদেনটি, যখন পুরনো স্টেটের উপর প্রয়োগ করা হয়, তখন নতুন স্টেট তৈরি হয়। +- সিগনেচারটি লেনদেনের উৎস অ্যাড্রেস থেকে আসে। + +ক্রিপ্টোগ্রাফিক হ্যাস ফাংশনের বৈশিষ্ট্যের কারণে, এই শর্তগুলি প্রমাণ করাই ইন্টিগ্রিটি নিশ্চিত করার জন্য যথেষ্ট। + +### ডেটা স্ট্রাকচার {#data-structures} + +প্রাথমিক ডেটা স্ট্রাকচার হল সার্ভার দ্বারা ধারণকৃত স্টেট। প্রতিটি অ্যাকাউন্টের জন্য, সার্ভার অ্যাকাউন্ট ব্যালেন্স এবং একটি [ননস](https://en.wikipedia.org/wiki/Cryptographic_nonce) ট্র্যাক করে, যা [রিপ্লে অ্যাটাক](https://en.wikipedia.org/wiki/Replay_attack) প্রতিরোধ করতে ব্যবহৃত হয়। + +### উপাদান {#components} + +এই সিস্টেমের জন্য দুটি উপাদান প্রয়োজন: + +- _সার্ভার_ যা লেনদেন গ্রহণ করে, সেগুলি প্রক্রিয়া করে, এবং জিরো-নলেজ প্রুফের সাথে চেইনে হ্যাস পোস্ট করে। +- একটি _স্মার্ট কন্ট্র্যাক্ট_ যা হ্যাস সংরক্ষণ করে এবং স্টেট ট্রানজিশনগুলি বৈধ তা নিশ্চিত করার জন্য জিরো-নলেজ প্রুফ যাচাই করে। + +### ডেটা এবং কন্ট্রোল ফ্লো {#flows} + +এগুলি হল সেই উপায়গুলি যার মাধ্যমে বিভিন্ন উপাদান একটি অ্যাকাউন্ট থেকে অন্য অ্যাকাউন্টে ট্রান্সফারের জন্য যোগাযোগ করে। + +1. একটি ওয়েব ব্রাউজার স্বাক্ষরকারীর অ্যাকাউন্ট থেকে একটি ভিন্ন অ্যাকাউন্টে ট্রান্সফারের জন্য একটি স্বাক্ষরিত লেনদেন জমা দেয়। + +2. সার্ভার যাচাই করে যে লেনদেনটি বৈধ: + + - স্বাক্ষরকারীর ব্যাংকে একটি অ্যাকাউন্ট আছে যার পর্যাপ্ত ব্যালেন্স রয়েছে। + - প্রাপকের ব্যাংকে একটি অ্যাকাউন্ট আছে। + +3. সার্ভার স্বাক্ষরকারীর ব্যালেন্স থেকে ট্রান্সফার করা পরিমাণ বিয়োগ করে এবং প্রাপকের ব্যালেন্সে যোগ করে নতুন স্টেট গণনা করে। + +4. সার্ভার একটি জিরো-নলেজ প্রুফ গণনা করে যে স্টেট পরিবর্তনটি একটি বৈধ পরিবর্তন। + +5. সার্ভার ইথেরিয়ামে একটি লেনদেন জমা দেয় যার মধ্যে রয়েছে: + + - নতুন স্টেট হ্যাস + - লেনদেন হ্যাস (যাতে লেনদেন প্রেরক জানতে পারে যে এটি প্রক্রিয়াজাত হয়েছে) + - জিরো-নলেজ প্রুফ যা প্রমাণ করে যে নতুন স্টেটে ট্রানজিশনটি বৈধ + +6. স্মার্ট কন্ট্র্যাক্ট জিরো-নলেজ প্রুফ যাচাই করে। + +7. যদি জিরো-নলেজ প্রুফ ঠিক থাকে, স্মার্ট কন্ট্র্যাক্ট এই কাজগুলি সম্পাদন করে: + - বর্তমান স্টেট হ্যাসকে নতুন স্টেট হ্যাসে আপডেট করুন + - নতুন স্টেট হ্যাস এবং লেনদেন হ্যাস সহ একটি লগ এন্ট্রি নির্গত করুন + +### টুলস {#tools} + +ক্লায়েন্ট-সাইড কোডের জন্য, আমরা [Vite](https://vite.dev/), [React](https://react.dev/), [Viem](https://viem.sh/), এবং [Wagmi](https://wagmi.sh/) ব্যবহার করতে যাচ্ছি। এগুলি ইন্ডাস্ট্রি-স্ট্যান্ডার্ড টুলস; আপনি যদি এগুলির সাথে পরিচিত না হন, তবে আপনি [এই টিউটোরিয়ালটি](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/) ব্যবহার করতে পারেন। + +সার্ভারের বেশিরভাগ অংশ JavaScript ব্যবহার করে [Node](https://nodejs.org/en)-এ লেখা হয়েছে। জিরো-নলেজ অংশটি [Noir](https://noir-lang.org/)-এ লেখা হয়েছে। আমাদের সংস্করণ `1.0.0-beta.10` প্রয়োজন, তাই আপনি [নির্দেশনা অনুযায়ী Noir ইনস্টল](https://noir-lang.org/docs/getting_started/quick_start) করার পরে, চালান: + +``` +noirup -v 1.0.0-beta.10 +``` + +আমরা যে ব্লকচেইনটি ব্যবহার করি তা হল `anvil`, একটি স্থানীয় টেস্টিং ব্লকচেইন যা [Foundry](https://getfoundry.sh/introduction/installation)-এর একটি অংশ। + +## বাস্তবায়ন {#implementation} + +যেহেতু এটি একটি জটিল সিস্টেম, আমরা এটি ধাপে ধাপে বাস্তবায়ন করব। + +### স্টেজ ১ - ম্যানুয়াল জিরো নলেজ {#stage-1} + +প্রথম স্টেজের জন্য, আমরা ব্রাউজারে একটি লেনদেনে স্বাক্ষর করব এবং তারপর ম্যানুয়ালি জিরো-নলেজ প্রুফে তথ্য প্রদান করব। জিরো-নলেজ কোড `server/noir/Prover.toml`-এ সেই তথ্য পাওয়ার আশা করে (যা [এখানে](https://noir-lang.org/docs/getting_started/project_breakdown#provertoml-1) নথিভুক্ত আছে)। + +এটি বাস্তবে কাজ করতে দেখতে: + +1. নিশ্চিত করুন যে আপনার [Node](https://nodejs.org/en/download) এবং [Noir](https://noir-lang.org/install) ইনস্টল করা আছে। বিশেষত, এগুলি একটি UNIX সিস্টেমে যেমন macOS, Linux, বা [WSL](https://learn.microsoft.com/en-us/windows/wsl/install)-এ ইনস্টল করুন। + +2. স্টেজ ১-এর কোড ডাউনলোড করুন এবং ক্লায়েন্ট কোড পরিবেশন করতে ওয়েব সার্ভার শুরু করুন। + + ```sh + git clone https://github.com/qbzzt/250911-zk-bank.git -b 01-manual-zk + cd 250911-zk-bank + cd client + npm install + npm run dev + ``` + + এখানে আপনার একটি ওয়েব সার্ভার প্রয়োজন হওয়ার কারণ হল, কিছু ধরণের জালিয়াতি প্রতিরোধ করতে, অনেক ওয়ালেট (যেমন MetaMask) ডিস্ক থেকে সরাসরি পরিবেশন করা ফাইল গ্রহণ করে না + +3. ওয়ালেট সহ একটি ব্রাউজার খুলুন। + +4. ওয়ালেটে, একটি নতুন পাসফ্রেজ লিখুন। মনে রাখবেন যে এটি আপনার বিদ্যমান পাসফ্রেজ মুছে ফেলবে, তাই _নিশ্চিত করুন যে আপনার একটি ব্যাকআপ আছে_। + + পাসফ্রেজটি হল `test test test test test test test test test test test junk`, যা anvil-এর জন্য ডিফল্ট টেস্টিং পাসফ্রেজ। + +5. [ক্লায়েন্ট-সাইড কোডে](http://localhost:5173/) ব্রাউজ করুন। + +6. ওয়ালেটের সাথে সংযোগ করুন এবং আপনার গন্তব্য অ্যাকাউন্ট ও পরিমাণ নির্বাচন করুন। + +7. **Sign**-এ ক্লিক করুন এবং লেনদেনটিতে স্বাক্ষর করুন। + +8. **Prover.toml** শিরোনামের অধীনে, আপনি একটি টেক্সট পাবেন। `server/noir/Prover.toml` কে সেই টেক্সট দিয়ে প্রতিস্থাপন করুন। + +9. জিরো-নলেজ প্রুফ এক্সিকিউট করুন। + + ```sh + cd ../server/noir + nargo execute + ``` + + আউটপুটটি এর অনুরূপ হওয়া উচিত + + ``` + ori@CryptoDocGuy:~/noir/250911-zk-bank/server/noir$ nargo execute + + [zkBank] Circuit witness successfully solved + [zkBank] Witness saved to target/zkBank.gz + [zkBank] Circuit output: (0x199aa62af8c1d562a6ec96e66347bf3240ab2afb5d022c895e6bf6a5e617167b, 0x0cfc0a67cb7308e4e9b254026b54204e34f6c8b041be207e64c5db77d95dd82d, 0x450cf9da6e180d6159290554ae3d8787, 0x6d8bc5a15b9037e52fb59b6b98722a85) + ``` + +10. মেসেজটি সঠিকভাবে হ্যাস করা হয়েছে কিনা তা দেখতে ওয়েব ব্রাউজারে দেখা হ্যাসের সাথে শেষ দুটি মান তুলনা করুন। + +#### `server/noir/Prover.toml` {#server-noir-prover-toml} + +[এই ফাইলটি](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Prover.toml) Noir দ্বারা প্রত্যাশিত তথ্য ফরম্যাট দেখায়। + +```toml +message="send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 500 finney (milliEth) 0 " +``` + +মেসেজটি টেক্সট ফরম্যাটে থাকে, যা ব্যবহারকারীর জন্য বোঝা সহজ করে তোলে (যা স্বাক্ষর করার সময় প্রয়োজন) এবং Noir কোডের জন্য পার্স করা সহজ করে। একদিকে ভগ্নাংশমূলক ট্রান্সফার সক্ষম করতে এবং অন্যদিকে সহজে পঠনযোগ্য করতে পরিমাণটি ফিনিতে উদ্ধৃত করা হয়েছে। শেষ সংখ্যাটি হল [ননস](https://en.wikipedia.org/wiki/Cryptographic_nonce)। + +স্ট্রিংটি 100 অক্ষর দীর্ঘ। জিরো-নলেজ প্রুফ পরিবর্তনশীল-আকারের ডেটা ভালোভাবে হ্যান্ডেল করতে পারে না, তাই প্রায়ই ডেটা প্যাড করার প্রয়োজন হয়। + +```toml +pubKeyX=["0x83",...,"0x75"] +pubKeyY=["0x35",...,"0xa5"] +signature=["0xb1",...,"0x0d"] +``` + +এই তিনটি প্যারামিটার হল নির্দিষ্ট-আকারের বাইট অ্যারে। + +```toml +[[accounts]] +address="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +balance=100_000 +nonce=0 + +[[accounts]] +address="0x70997970C51812dc3A010C7d01b50e0d17dc79C8" +balance=100_000 +nonce=0 +``` + +এটি স্ট্রাকচারের একটি অ্যারে নির্দিষ্ট করার উপায়। প্রতিটি এন্ট্রির জন্য, আমরা অ্যাড্রেস, ব্যালেন্স (মিলিইথ এ.কে.এ. তে) নির্দিষ্ট করি। [ফিনি](https://cryptovalleyjournal.com/glossary/finney/)), এবং পরবর্তী ননস মান। + +#### `client/src/Transfer.tsx` {#client-src-transfer-tsx} + +[এই ফাইলটি](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/client/src/Transfer.tsx) ক্লায়েন্ট-সাইড প্রসেসিং বাস্তবায়ন করে এবং `server/noir/Prover.toml` ফাইল (যা জিরো-নলেজ প্যারামিটার অন্তর্ভুক্ত করে) তৈরি করে। + +এখানে আরও আকর্ষণীয় অংশগুলির ব্যাখ্যা দেওয়া হল। + +```tsx +export default attrs => { +``` + +এই ফাংশনটি `Transfer` React কম্পোনেন্ট তৈরি করে, যা অন্য ফাইলগুলো ইম্পোর্ট করতে পারে। + +```tsx + const accounts = [ + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", + "0x90F79bf6EB2c4f870365E785982E1f101E93b906", + "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + ] +``` + +এগুলো হল অ্যাকাউন্ট অ্যাড্রেস, `test ...` দ্বারা তৈরি অ্যাড্রেস। test junk` পাসফ্রেজ। আপনি যদি নিজের অ্যাড্রেস ব্যবহার করতে চান, তাহলে শুধু এই সংজ্ঞাটি পরিবর্তন করুন। + +```tsx + const account = useAccount() + const wallet = createWalletClient({ + transport: custom(window.ethereum!) + }) +``` + +এই [Wagmi হুকগুলি](https://wagmi.sh/react/api/hooks) আমাদের [viem](https://viem.sh/) লাইব্রেরি এবং ওয়ালেটে অ্যাক্সেস করতে দেয়। + +```tsx + const message = `send ${toAccount} ${ethAmount*1000} finney (milliEth) ${nonce}`.padEnd(100, " ") +``` + +এটি স্পেস দিয়ে প্যাড করা মেসেজ। যখনই [`useState`](https://react.dev/reference/react/useState) ভেরিয়েবলগুলোর মধ্যে একটি পরিবর্তন হয়, তখন কম্পোনেন্টটি আবার আঁকা হয় এবং `message` আপডেট করা হয়। + +```tsx + const sign = async () => { +``` + +এই ফাংশনটি ব্যবহারকারী যখন **Sign** বোতামে ক্লিক করে তখন কল করা হয়। মেসেজটি স্বয়ংক্রিয়ভাবে আপডেট করা হয়, কিন্তু সিগনেচারের জন্য ওয়ালেটে ব্যবহারকারীর অনুমোদন প্রয়োজন, এবং প্রয়োজন না হলে আমরা এর জন্য জিজ্ঞাসা করতে চাই না। + +```tsx + const signature = await wallet.signMessage({ + account: fromAccount, + message, + }) +``` + +ওয়ালেটকে [মেসেজে স্বাক্ষর করতে](https://viem.sh/docs/accounts/local/signMessage) বলুন। + +```tsx + const hash = hashMessage(message) +``` + +মেসেজ হ্যাস পান। এটি ব্যবহারকারীকে ডিবাগিং (Noir কোডের) করার জন্য প্রদান করা সহায়ক। + +```tsx + const pubKey = await recoverPublicKey({ + hash, + signature + }) +``` + +[পাবলিক কী পান](https://viem.sh/docs/utilities/recoverPublicKey)। এটি [Noir `ecrecover`](https://github.com/colinnielsen/ecrecover-noir) ফাংশনের জন্য প্রয়োজন। + +```tsx + setSignature(signature) + setHash(hash) + setPubKey(pubKey) +``` + +স্টেট ভেরিয়েবল সেট করুন। এটি করার মাধ্যমে কম্পোনেন্টটি আবার আঁকা হয় (`sign` ফাংশন থেকে বের হওয়ার পরে) এবং ব্যবহারকারীকে আপডেট করা মানগুলি দেখায়। + +```tsx + let proverToml = ` +``` + +`Prover.toml`-এর জন্য টেক্সট। + +```tsx +message="${message}" + +pubKeyX=${hexToArray(pubKey.slice(4,4+2*32))} +pubKeyY=${hexToArray(pubKey.slice(4+2*32))} +``` + +Viem আমাদের একটি 65-বাইট হেক্সাডেসিমেল স্ট্রিং হিসাবে পাবলিক কী প্রদান করে। প্রথম বাইটটি হল `0x04`, একটি সংস্করণ মার্কার। এর পরে পাবলিক কী-র `x` এর জন্য 32 বাইট এবং তারপর পাবলিক কী-র `y` এর জন্য 32 বাইট থাকে। + +তবে, Noir এই তথ্য দুটি বাইট অ্যারে হিসাবে পেতে চায়, একটি `x`-এর জন্য এবং একটি `y`-এর জন্য। জিরো-নলেজ প্রুফের অংশ হিসাবে পার্স করার পরিবর্তে এখানে ক্লায়েন্টে পার্স করা সহজ। + +মনে রাখবেন যে এটি সাধারণত জিরো-নলেজে একটি ভালো অভ্যাস। জিরো-নলেজ প্রুফের ভিতরের কোড ব্যয়বহুল, তাই যে কোনো প্রসেসিং যা জিরো-নলেজ প্রুফের বাইরে করা যায়, তা জিরো-নলেজ প্রুফের বাইরেই _করা উচিত_। + +```tsx +signature=${hexToArray(signature.slice(2,-2))} +``` + +সিগনেচারটিও একটি 65-বাইট হেক্সাডেসিমেল স্ট্রিং হিসাবে প্রদান করা হয়। তবে, শেষ বাইটটি শুধুমাত্র পাবলিক কী পুনরুদ্ধার করার জন্য প্রয়োজন। যেহেতু পাবলিক কী ইতিমধ্যেই Noir কোডে প্রদান করা হবে, তাই সিগনেচার যাচাই করার জন্য আমাদের এটির প্রয়োজন নেই, এবং Noir কোডেরও এটির প্রয়োজন নেই। + +```tsx +${accounts.map(accountInProverToml).reduce((a,b) => a+b, "")} +` +``` + +অ্যাকাউন্টগুলো প্রদান করুন। + +```tsx + setProverToml(proverToml) + } + + return ( + <> +

Transfer

+``` + +এটি কম্পোনেন্টের HTML (আরও সঠিকভাবে, [JSX](https://react.dev/learn/writing-markup-with-jsx)) ফরম্যাট। + +#### `server/noir/src/main.nr` {#server-noir-src-main-nr} + +[এই ফাইলটি](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/src/main.nr) হল আসল জিরো-নলেজ কোড। + +``` +use std::hash::pedersen_hash; +``` + +[পেডারসেন হ্যাস](https://rya-sge.github.io/access-denied/2024/05/07/pedersen-hash-function/) [Noir স্ট্যান্ডার্ড লাইব্রেরি](https://noir-lang.org/docs/noir/standard_library/cryptographic_primitives/hashes#pedersen_hash)-র সাথে প্রদান করা হয়। জিরো-নলেজ প্রুফ সাধারণত এই হ্যাস ফাংশনটি ব্যবহার করে। স্ট্যান্ডার্ড হ্যাস ফাংশনগুলির তুলনায় এটি [অ্যারিথমেটিক সার্কিট](https://rareskills.io/post/arithmetic-circuit)-এর ভিতরে গণনা করা অনেক সহজ। + +``` +use keccak256::keccak256; +use dep::ecrecover; +``` + +এই দুটি ফাংশন হল এক্সটার্নাল লাইব্রেরি, যা [`Nargo.toml`](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Nargo.toml)-এ সংজ্ঞায়িত। এগুলি ঠিক তাদের নামের মতোই, একটি ফাংশন যা [keccak256 হ্যাস](https://emn178.github.io/online-tools/keccak_256.html) গণনা করে এবং একটি ফাংশন যা ইথেরিয়াম সিগনেচার যাচাই করে এবং স্বাক্ষরকারীর ইথেরিয়াম অ্যাড্রেস পুনরুদ্ধার করে। + +``` +global ACCOUNT_NUMBER : u32 = 5; +``` + +Noir [Rust](https://www.rust-lang.org/) দ্বারা অনুপ্রাণিত। ভেরিয়েবলগুলো, ডিফল্টরূপে, ধ্রুবক। আমরা এইভাবে গ্লোবাল কনফিগারেশন ধ্রুবক সংজ্ঞায়িত করি। বিশেষত, `ACCOUNT_NUMBER` হল আমাদের সংরক্ষিত অ্যাকাউন্টের সংখ্যা। + +`u` নামের ডেটা টাইপগুলি হল সেই সংখ্যক বিট, আনসাইন্ড। একমাত্র সমর্থিত টাইপগুলি হল `u8`, `u16`, `u32`, `u64`, এবং `u128`। + +``` +global FLAT_ACCOUNT_FIELDS : u32 = 2; +``` + +এই ভেরিয়েবলটি অ্যাকাউন্টের পেডারসেন হ্যাসের জন্য ব্যবহৃত হয়, যা নীচে ব্যাখ্যা করা হয়েছে। + +``` +global MESSAGE_LENGTH : u32 = 100; +``` + +উপরে যেমন ব্যাখ্যা করা হয়েছে, মেসেজের দৈর্ঘ্য নির্দিষ্ট। এটি এখানে নির্দিষ্ট করা হয়েছে। + +``` +global ASCII_MESSAGE_LENGTH : [u8; 3] = [0x31, 0x30, 0x30]; +global HASH_BUFFER_SIZE : u32 = 26+3+MESSAGE_LENGTH; +``` + +[EIP-191 সিগনেচার](https://eips.ethereum.org/EIPS/eip-191)-এর জন্য একটি 26-বাইট প্রিফিক্স সহ একটি বাফার প্রয়োজন, তারপরে ASCII-তে মেসেজের দৈর্ঘ্য, এবং অবশেষে মেসেজটি নিজেই। + +``` +struct Account { + balance: u128, + address: Field, + nonce: u32, +} +``` + +একটি অ্যাকাউন্ট সম্পর্কে আমরা যে তথ্য সংরক্ষণ করি। [`Field`](https://noir-lang.org/docs/noir/concepts/data_types/fields) হল একটি সংখ্যা, সাধারণত 253 বিট পর্যন্ত, যা জিরো-নলেজ প্রুফ বাস্তবায়নকারী [অ্যারিথমেটিক সার্কিট](https://rareskills.io/post/arithmetic-circuit)-এ সরাসরি ব্যবহার করা যেতে পারে। এখানে আমরা একটি 160-বিট ইথেরিয়াম অ্যাড্রেস সংরক্ষণ করতে `Field` ব্যবহার করি। + +``` +struct TransferTxn { + from: Field, + to: Field, + amount: u128, + nonce: u32 +} +``` + +একটি ট্রান্সফার লেনদেনের জন্য আমরা যে তথ্য সংরক্ষণ করি। + +``` +fn flatten_account(account: Account) -> [Field; FLAT_ACCOUNT_FIELDS] { +``` + +একটি ফাংশন সংজ্ঞা। প্যারামিটারটি হল `Account` তথ্য। ফলাফল হল `Field` ভেরিয়েবলের একটি অ্যারে, যার দৈর্ঘ্য `FLAT_ACCOUNT_FIELDS` + +``` + let flat = [ + account.address, + ((account.balance << 32) + account.nonce.into()).into(), + ]; +``` + +অ্যারের প্রথম মান হল অ্যাকাউন্ট অ্যাড্রেস। দ্বিতীয়টিতে ব্যালেন্স এবং ননস উভয়ই অন্তর্ভুক্ত। `.into()` কলগুলো একটি সংখ্যাকে তার প্রয়োজনীয় ডেটা টাইপে পরিবর্তন করে। `account.nonce` একটি `u32` মান, কিন্তু এটিকে `account.balance « 32`, একটি `u128` মানের সাথে যোগ করতে হলে, এটিকে একটি `u128` হতে হবে। সেটি হল প্রথম `.into()`। দ্বিতীয়টি `u128` ফলাফলকে একটি `Field`-এ রূপান্তরিত করে যাতে এটি অ্যারেতে ফিট হয়। + +``` + flat +} +``` + +Noir-এ, ফাংশনগুলি শুধুমাত্র শেষে একটি মান ফেরত দিতে পারে (কোনো আর্লি রিটার্ন নেই)। রিটার্ন মান নির্দিষ্ট করার জন্য, আপনি ফাংশনের শেষ বন্ধনীর ঠিক আগে এটি মূল্যায়ন করেন। + +``` +fn flatten_accounts(accounts: [Account; ACCOUNT_NUMBER]) -> [Field; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER] { +``` + +এই ফাংশনটি অ্যাকাউন্ট অ্যারেটিকে একটি `Field` অ্যারেতে পরিণত করে, যা একটি পেটারসেন হ্যাসের ইনপুট হিসাবে ব্যবহার করা যেতে পারে। + +``` + let mut flat: [Field; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER] = [0; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER]; +``` + +এটি একটি পরিবর্তনযোগ্য ভেরিয়েবল নির্দিষ্ট করার উপায়, অর্থাৎ, ধ্রুবক _নয়_। Noir-এর ভেরিয়েবলগুলোর সবসময় একটি মান থাকতে হবে, তাই আমরা এই ভেরিয়েবলটিকে সব শূন্য দিয়ে শুরু করি। + +``` + for i in 0..ACCOUNT_NUMBER { +``` + +এটি একটি `for` লুপ। মনে রাখবেন যে সীমানাগুলো ধ্রুবক। Noir লুপগুলোর সীমানা কম্পাইল করার সময় জানা থাকতে হবে। এর কারণ হল অ্যারিথমেটিক সার্কিট ফ্লো কন্ট্রোল সমর্থন করে না। একটি `for` লুপ প্রসেস করার সময়, কম্পাইলার সহজভাবে এর ভিতরের কোডটি একাধিকবার রাখে, প্রতিটি পুনরাবৃত্তির জন্য একবার। + +``` + let fields = flatten_account(accounts[i]); + for j in 0..FLAT_ACCOUNT_FIELDS { + flat[i*FLAT_ACCOUNT_FIELDS + j] = fields[j]; + } + } + + flat +} + +fn hash_accounts(accounts: [Account; ACCOUNT_NUMBER]) -> Field { + pedersen_hash(flatten_accounts(accounts)) +} +``` + +অবশেষে, আমরা সেই ফাংশনে পৌঁছলাম যা অ্যাকাউন্ট অ্যারে হ্যাস করে। + +``` +fn find_account(accounts: [Account; ACCOUNT_NUMBER], address: Field) -> u32 { + let mut account : u32 = ACCOUNT_NUMBER; + + for i in 0..ACCOUNT_NUMBER { + if accounts[i].address == address { + account = i; + } + } +``` + +এই ফাংশনটি একটি নির্দিষ্ট অ্যাড্রেস সহ অ্যাকাউন্ট খুঁজে বের করে। এই ফাংশনটি স্ট্যান্ডার্ড কোডে ভয়ানকভাবে অদক্ষ হবে কারণ এটি অ্যাড্রেস খুঁজে পাওয়ার পরেও সমস্ত অ্যাকাউন্টের উপর পুনরাবৃত্তি করে। + +তবে, জিরো-নলেজ প্রুফে কোনো ফ্লো কন্ট্রোল নেই। যদি আমাদের কখনো কোনো শর্ত পরীক্ষা করার প্রয়োজন হয়, তবে আমাদের প্রতিবারই তা পরীক্ষা করতে হবে। + +`if` স্টেটমেন্টের ক্ষেত্রেও একই রকম ঘটনা ঘটে। উপরের লুপের `if` স্টেটমেন্টটি এই গাণিতিক স্টেটমেন্টগুলিতে অনুবাদ করা হয়েছে। + +_conditionresult = accounts[i].address == address_ // সমান হলে এক, অন্যথায় শূন্য + +_accountnew = conditionresult\*i + (1-conditionresult)\*accountold_ + +```rust + assert (account < ACCOUNT_NUMBER, f"{address} does not have an account"); + + account +} +``` + +[`assert`](https://noir-lang.org/docs/dev/noir/concepts/assert) ফাংশনটি যদি অ্যাসারশনটি মিথ্যা হয় তবে জিরো-নলেজ প্রুফ ক্র্যাশ করে দেয়। এই ক্ষেত্রে, যদি আমরা প্রাসঙ্গিক অ্যাড্রেস সহ কোনো অ্যাকাউন্ট খুঁজে না পাই। অ্যাড্রেস রিপোর্ট করার জন্য, আমরা একটি [ফরম্যাট স্ট্রিং](https://noir-lang.org/docs/noir/concepts/data_types/strings#format-strings) ব্যবহার করি। + +```rust +fn apply_transfer_txn(accounts: [Account; ACCOUNT_NUMBER], txn: TransferTxn) -> [Account; ACCOUNT_NUMBER] { +``` + +এই ফাংশনটি একটি ট্রান্সফার লেনদেন প্রয়োগ করে এবং নতুন অ্যাকাউন্ট অ্যারে ফেরত দেয়। + +```rust + let from = find_account(accounts, txn.from); + let to = find_account(accounts, txn.to); + + let (txnFrom, txnAmount, txnNonce, accountNonce) = + (txn.from, txn.amount, txn.nonce, accounts[from].nonce); +``` + +আমরা Noir-এর ফরম্যাট স্ট্রিং-এর ভিতরে স্ট্রাকচার উপাদান অ্যাক্সেস করতে পারি না, তাই আমরা একটি ব্যবহারযোগ্য কপি তৈরি করি। + +```rust + assert (accounts[from].balance >= txn.amount, + f"{txnFrom} does not have {txnAmount} finney"); + + assert (accounts[from].nonce == txn.nonce, + f"Transaction has nonce {txnNonce}, but the account is expected to use {accountNonce}"); +``` + +এগুলো দুটি শর্ত যা একটি লেনদেনকে অবৈধ করে তুলতে পারে। + +```rust + let mut newAccounts = accounts; + + newAccounts[from].balance -= txn.amount; + newAccounts[from].nonce += 1; + newAccounts[to].balance += txn.amount; + + newAccounts +} +``` + +নতুন অ্যাকাউন্ট অ্যারে তৈরি করুন এবং তারপর এটি ফেরত দিন। + +```rust +fn readAddress(messageBytes: [u8; MESSAGE_LENGTH]) -> Field +``` + +এই ফাংশনটি মেসেজ থেকে অ্যাড্রেস পড়ে। + +```rust +{ + let mut result : Field = 0; + + for i in 7..47 { +``` + +অ্যাড্রেস সবসময় 20 বাইট (এ.কে.এ. 40 হেক্সাডেসিমেল অঙ্ক) লম্বা হয় এবং ৭ নম্বর অক্ষর থেকে শুরু হয়। 40 হেক্সাডেসিমেল অঙ্ক) দীর্ঘ, এবং অক্ষর #7 থেকে শুরু হয়। + +```rust + result *= 0x10; + if messageBytes[i] >= 48 & messageBytes[i] <= 57 { // 0-9 + result += (messageBytes[i]-48).into(); + } + if messageBytes[i] >= 65 & messageBytes[i] <= 70 { // A-F + result += (messageBytes[i]-65+10).into() + } + if messageBytes[i] >= 97 & messageBytes[i] <= 102 { // a-f + result += (messageBytes[i]-97+10).into() + } + } + + result +} + +fn readAmountAndNonce(messageBytes: [u8; MESSAGE_LENGTH]) -> (u128, u32) +``` + +মেসেজ থেকে পরিমাণ এবং ননস পড়ুন। + +```rust +{ + let mut amount : u128 = 0; + let mut nonce: u32 = 0; + let mut stillReadingAmount: bool = true; + let mut lookingForNonce: bool = false; + let mut stillReadingNonce: bool = false; +``` + +মেসেজে, অ্যাড্রেসের পরের প্রথম সংখ্যাটি হল ট্রান্সফারের জন্য ফিনির পরিমাণ (এ.কে.এ. হাজার ভাগের এক ETH)। হাজার ভাগের এক ETH) ট্রান্সফার করার জন্য। দ্বিতীয় সংখ্যাটি হল ননস। তাদের মধ্যে যেকোনো টেক্সট উপেক্ষা করা হয়। + +```rust + for i in 48..MESSAGE_LENGTH { + if messageBytes[i] >= 48 & messageBytes[i] <= 57 { // 0-9 + let digit = (messageBytes[i]-48); + + if stillReadingAmount { + amount = amount*10 + digit.into(); + } + + if lookingForNonce { // We just found it + stillReadingNonce = true; + lookingForNonce = false; + } + + if stillReadingNonce { + nonce = nonce*10 + digit.into(); + } + } else { + if stillReadingAmount { + stillReadingAmount = false; + lookingForNonce = true; + } + if stillReadingNonce { + stillReadingNonce = false; + } + } + } + + (amount, nonce) +} +``` + +একটি [টাপল](https://noir-lang.org/docs/noir/concepts/data_types/tuples) ফেরত দেওয়া হল Noir-এ একটি ফাংশন থেকে একাধিক মান ফেরত দেওয়ার উপায়। + +```rust +fn readTransferTxn(message: str) -> TransferTxn +{ + let mut txn: TransferTxn = TransferTxn { from: 0, to: 0, amount:0, nonce:0 }; + let messageBytes = message.as_bytes(); + + txn.to = readAddress(messageBytes); + let (amount, nonce) = readAmountAndNonce(messageBytes); + txn.amount = amount; + txn.nonce = nonce; + + txn +} +``` + +এই ফাংশনটি মেসেজকে বাইটে রূপান্তর করে, তারপর পরিমাণগুলোকে একটি `TransferTxn`-এ রূপান্তর করে। + +```rust +// Viem-এর hashMessage-এর সমতুল্য +// https://viem.sh/docs/utilities/hashMessage#hashmessage +fn hashMessage(message: str) -> [u8;32] { +``` + +আমরা অ্যাকাউন্টগুলির জন্য পেডারসেন হ্যাস ব্যবহার করতে পেরেছি কারণ সেগুলি শুধুমাত্র জিরো-নলেজ প্রুফের ভিতরে হ্যাস করা হয়। তবে, এই কোডে আমাদের মেসেজের সিগনেচার পরীক্ষা করতে হবে, যা ব্রাউজার দ্বারা তৈরি হয়। এর জন্য, আমাদের [EIP 191](https://eips.ethereum.org/EIPS/eip-191)-এ ইথেরিয়াম সাইনিং ফরম্যাট অনুসরণ করতে হবে। এর মানে হল আমাদের একটি স্ট্যান্ডার্ড প্রিফিক্স, ASCII-তে মেসেজের দৈর্ঘ্য এবং মেসেজটি নিজেই সহ একটি সম্মিলিত বাফার তৈরি করতে হবে, এবং এটিকে হ্যাস করার জন্য ইথেরিয়ামের স্ট্যান্ডার্ড keccak256 ব্যবহার করতে হবে। + +```rust + // ASCII prefix + let prefix_bytes = [ + 0x19, // \x19 + 0x45, // 'E' + 0x74, // 't' + 0x68, // 'h' + 0x65, // 'e' + 0x72, // 'r' + 0x65, // 'e' + 0x75, // 'u' + 0x6D, // 'm' + 0x20, // ' ' + 0x53, // 'S' + 0x69, // 'i' + 0x67, // 'g' + 0x6E, // 'n' + 0x65, // 'e' + 0x64, // 'd' + 0x20, // ' ' + 0x4D, // 'M' + 0x65, // 'e' + 0x73, // 's' + 0x73, // 's' + 0x61, // 'a' + 0x67, // 'g' + 0x65, // 'e' + 0x3A, // ':' + 0x0A // '\n' + ]; +``` + +এমন পরিস্থিতি এড়াতে যেখানে একটি অ্যাপ্লিকেশন ব্যবহারকারীকে এমন একটি মেসেজে স্বাক্ষর করতে বলে যা একটি লেনদেন হিসাবে বা অন্য কোনো উদ্দেশ্যে ব্যবহার করা যেতে পারে, EIP 191 নির্দিষ্ট করে যে সমস্ত স্বাক্ষরিত মেসেজ 0x19 অক্ষর (একটি বৈধ ASCII অক্ষর নয়) দিয়ে শুরু হবে, তারপরে `Ethereum Signed Message:` এবং একটি নিউলাইন থাকবে। + +```rust + let mut buffer: [u8; HASH_BUFFER_SIZE] = [0u8; HASH_BUFFER_SIZE]; + for i in 0..26 { + buffer[i] = prefix_bytes[i]; + } + + let messageBytes : [u8; MESSAGE_LENGTH] = message.as_bytes(); + + if MESSAGE_LENGTH <= 9 { + for i in 0..1 { + buffer[i+26] = ASCII_MESSAGE_LENGTH[i]; + } + + for i in 0..MESSAGE_LENGTH { + buffer[i+26+1] = messageBytes[i]; + } + } + + if MESSAGE_LENGTH >= 10 & MESSAGE_LENGTH <= 99 { + for i in 0..2 { + buffer[i+26] = ASCII_MESSAGE_LENGTH[i]; + } + + for i in 0..MESSAGE_LENGTH { + buffer[i+26+2] = messageBytes[i]; + } + } + + if MESSAGE_LENGTH >= 100 { + for i in 0..3 { + buffer[i+26] = ASCII_MESSAGE_LENGTH[i]; + } + + for i in 0..MESSAGE_LENGTH { + buffer[i+26+3] = messageBytes[i]; + } + } + + assert(MESSAGE_LENGTH < 1000, "Messages whose length is over three digits are not supported"); +``` + +999 পর্যন্ত মেসেজের দৈর্ঘ্য হ্যান্ডেল করুন এবং এর চেয়ে বেশি হলে ব্যর্থ হন। আমি এই কোডটি যুক্ত করেছি, যদিও মেসেজের দৈর্ঘ্য একটি ধ্রুবক, কারণ এটি পরিবর্তন করা সহজ করে তোলে। একটি প্রোডাকশন সিস্টেমে, আপনি সম্ভবত ধরে নেবেন যে ভাল পারফরম্যান্সের জন্য `MESSAGE_LENGTH` পরিবর্তন হয় না। + +```rust + keccak256::keccak256(buffer, HASH_BUFFER_SIZE) +} +``` + +ইথেরিয়াম স্ট্যান্ডার্ড `keccak256` ফাংশন ব্যবহার করুন। + +```rust +fn signatureToAddressAndHash( + message: str, + pubKeyX: [u8; 32], + pubKeyY: [u8; 32], + signature: [u8; 64] + ) -> (Field, Field, Field) // address, first 16 bytes of hash, last 16 bytes of hash +{ +``` + +এই ফাংশনটি সিগনেচার যাচাই করে, যার জন্য মেসেজ হ্যাস প্রয়োজন। এটি তখন আমাদের স্বাক্ষরকারী অ্যাড্রেস এবং মেসেজ হ্যাস প্রদান করে। মেসেজ হ্যাস দুটি `Field` মান হিসাবে সরবরাহ করা হয় কারণ সেগুলি একটি বাইট অ্যারের চেয়ে প্রোগ্রামের বাকি অংশে ব্যবহার করা সহজ। + +আমাদের দুটি `Field` মান ব্যবহার করতে হবে কারণ ফিল্ড গণনাগুলি একটি বড় সংখ্যার [মডিউলো](https://en.wikipedia.org/wiki/Modulo) করা হয়, কিন্তু সেই সংখ্যাটি সাধারণত 256 বিটের কম (অন্যথায় EVM-এ সেই গণনাগুলি করা কঠিন হবে)। + +```rust + let hash = hashMessage(message); + + let mut (hash1, hash2) = (0,0); + + for i in 0..16 { + hash1 = hash1*256 + hash[31-i].into(); + hash2 = hash2*256 + hash[15-i].into(); + } +``` + +`hash1` এবং `hash2` কে পরিবর্তনযোগ্য ভেরিয়েবল হিসাবে নির্দিষ্ট করুন, এবং হ্যাসটি বাইট বাই বাইট তাতে লিখুন। + +```rust + ( + ecrecover::ecrecover(pubKeyX, pubKeyY, signature, hash), +``` + +এটি [Solidity-র `ecrecover`](https://docs.soliditylang.org/en/v0.8.30/cheatsheet.html#mathematical-and-cryptographic-functions) এর মতো, তবে দুটি গুরুত্বপূর্ণ পার্থক্য রয়েছে: + +- যদি সিগনেচারটি বৈধ না হয়, তাহলে কলটি একটি `assert` ব্যর্থ করে এবং প্রোগ্রামটি বন্ধ হয়ে যায়। +- যদিও পাবলিক কী সিগনেচার এবং হ্যাস থেকে পুনরুদ্ধার করা যায়, এটি এমন একটি প্রসেসিং যা বাইরে করা যেতে পারে এবং তাই, জিরো-নলেজ প্রুফের ভিতরে করা মূল্যবান নয়। যদি কেউ এখানে আমাদের সাথে প্রতারণা করার চেষ্টা করে, তাহলে সিগনেচার যাচাইকরণ ব্যর্থ হবে। + +```rust + hash1, + hash2 + ) +} + +fn main( + accounts: [Account; ACCOUNT_NUMBER], + message: str, + pubKeyX: [u8; 32], + pubKeyY: [u8; 32], + signature: [u8; 64], + ) -> pub ( + Field, // Hash of old accounts array + Field, // Hash of new accounts array + Field, // First 16 bytes of message hash + Field, // Last 16 bytes of message hash + ) +``` + +অবশেষে, আমরা `main` ফাংশনে পৌঁছেছি। আমাদের প্রমাণ করতে হবে যে আমাদের কাছে একটি লেনদেন রয়েছে যা অ্যাকাউন্টগুলির হ্যাসকে পুরনো মান থেকে নতুনটিতে বৈধভাবে পরিবর্তন করে। আমাদের এটাও প্রমাণ করতে হবে যে এর এই নির্দিষ্ট লেনদেন হ্যাস রয়েছে যাতে যে ব্যক্তি এটি পাঠিয়েছে সে জানতে পারে যে তার লেনদেন প্রক্রিয়া করা হয়েছে। + +```rust +{ + let mut txn = readTransferTxn(message); +``` + +আমাদের `txn` কে পরিবর্তনযোগ্য হতে হবে কারণ আমরা মেসেজ থেকে থেকে অ্যাড্রেস পড়ি না, আমরা এটি সিগনেচার থেকে পড়ি। + +```rust + let (fromAddress, txnHash1, txnHash2) = signatureToAddressAndHash( + message, + pubKeyX, + pubKeyY, + signature); + + txn.from = fromAddress; + + let newAccounts = apply_transfer_txn(accounts, txn); + + ( + hash_accounts(accounts), + hash_accounts(newAccounts), + txnHash1, + txnHash2 + ) +} +``` + +### স্টেজ ২ - একটি সার্ভার যুক্ত করা {#stage-2} + +দ্বিতীয় পর্যায়ে, আমরা একটি সার্ভার যুক্ত করি যা ব্রাউজার থেকে ট্রান্সফার লেনদেন গ্রহণ করে এবং বাস্তবায়ন করে। + +এটি বাস্তবে কাজ করতে দেখতে: + +1. যদি Vite চলমান থাকে তবে এটি বন্ধ করুন। + +2. সার্ভার সহ ব্রাঞ্চটি ডাউনলোড করুন এবং নিশ্চিত করুন যে আপনার সমস্ত প্রয়োজনীয় মডিউল রয়েছে। + + ```sh + git checkout 02-add-server + cd client + npm install + cd ../server + npm install + ``` + + Noir কোড কম্পাইল করার প্রয়োজন নেই, এটি স্টেজ ১-এর জন্য ব্যবহৃত কোডের মতোই। + +3. সার্ভার শুরু করুন। + + ```sh + npm run start + ``` + +4. একটি পৃথক কমান্ড-লাইন উইন্ডোতে, ব্রাউজার কোড পরিবেশন করতে Vite চালান। + + ```sh + cd client + npm run dev + ``` + +5. [http://localhost:5173](http://localhost:5173) এ ক্লায়েন্ট কোডে ব্রাউজ করুন + +6. একটি লেনদেন জারি করার আগে, আপনাকে ননস এবং আপনি যে পরিমাণ পাঠাতে পারেন তা জানতে হবে। এই তথ্য পেতে, **Update account data** এ ক্লিক করুন এবং মেসেজে স্বাক্ষর করুন। + + এখানে আমাদের একটি দ্বিধা রয়েছে। একদিকে, আমরা এমন একটি মেসেজে স্বাক্ষর করতে চাই না যা পুনরায় ব্যবহার করা যেতে পারে (একটি [রিপ্লে অ্যাটাক](https://en.wikipedia.org/wiki/Replay_attack)), যার কারণেই আমরা প্রথম স্থানে একটি ননস চাই। তবে, আমাদের এখনো কোনো ননস নেই। সমাধান হল এমন একটি ননস বেছে নেওয়া যা শুধুমাত্র একবার ব্যবহার করা যাবে এবং যা আমাদের উভয় দিকেই ইতিমধ্যে রয়েছে, যেমন বর্তমান সময়। + + এই সমাধানের সমস্যা হল যে সময় পুরোপুরি সিঙ্ক্রোনাইজড নাও হতে পারে। তাই এর পরিবর্তে, আমরা এমন একটি মানের উপর স্বাক্ষর করি যা প্রতি মিনিটে পরিবর্তন হয়। এর মানে হল যে রিপ্লে অ্যাটাকের জন্য আমাদের দুর্বলতার সময়সীমা সর্বোচ্চ এক মিনিট। বিবেচনা করে যে প্রোডাকশনে স্বাক্ষরিত অনুরোধটি TLS দ্বারা সুরক্ষিত থাকবে, এবং টানেলের অন্য প্রান্ত—সার্ভার—ইতিমধ্যেই ব্যালেন্স এবং ননস প্রকাশ করতে পারে (কাজ করার জন্য তাদের এটি জানতে হবে), এটি একটি গ্রহণযোগ্য ঝুঁকি। + +7. ব্রাউজার ব্যালেন্স এবং ননস ফেরত পাওয়ার পরে, এটি ট্রান্সফার ফর্ম দেখায়। গন্তব্য অ্যাড্রেস এবং পরিমাণ নির্বাচন করুন এবং **Transfer** এ ক্লিক করুন। এই অনুরোধে স্বাক্ষর করুন। + +8. ট্রান্সফার দেখতে, হয় **Update account data** করুন অথবা যে উইন্ডোতে আপনি সার্ভার চালাচ্ছেন সেখানে দেখুন। সার্ভার প্রতিবার পরিবর্তনের সময় স্টেট লগ করে। + + ``` + ori@CryptoDocGuy:~/x/250911-zk-bank/server$ npm run start + + > server@1.0.0 start + > node --experimental-json-modules index.mjs + + Listening on port 3000 + Txn send 0x90F79bf6EB2c4f870365E785982E1f101E93b906 36000 finney (milliEth) 0 processed + New state: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 has 64000 (1) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 has 100000 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC has 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 has 136000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 has 100000 (0) + Txn send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 7200 finney (milliEth) 1 processed + New state: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 has 56800 (2) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 has 107200 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC has 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 has 136000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 has 100000 (0) + Txn send 0x90F79bf6EB2c4f870365E785982E1f101E93b906 3000 finney (milliEth) 2 processed + New state: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 has 53800 (3) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 has 107200 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC has 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 has 139000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 has 100000 (0) + ``` + +#### `server/index.mjs` {#server-index-mjs-1} + +[এই ফাইলটি](https://github.com/qbzzt/250911-zk-bank/blob/02-add-server/server/index.mjs) সার্ভার প্রসেস ধারণ করে এবং [`main.nr`](https://github.com/qbzzt/250911-zk-bank/blob/02-add-server/server/noir/src/main.nr) এ থাকা Noir কোডের সাথে মিথস্ক্রিয়া করে। এখানে আকর্ষণীয় অংশগুলির একটি ব্যাখ্যা দেওয়া হল। + +```js +import { Noir } from '@noir-lang/noir_js' +``` + +[noir.js](https://www.npmjs.com/package/@noir-lang/noir_js) লাইব্রেরি JavaScript কোড এবং Noir কোডের মধ্যে ইন্টারফেস করে। + +```js +const circuit = JSON.parse(await fs.readFile("./noir/target/zkBank.json")) +const noir = new Noir(circuit) +``` + +অ্যারিথমেটিক সার্কিট—পূর্ববর্তী পর্যায়ে তৈরি করা কম্পাইল করা Noir প্রোগ্রাম—লোড করুন এবং এটি কার্যকর করার জন্য প্রস্তুত করুন। + +```js +// আমরা শুধুমাত্র একটি স্বাক্ষরিত অনুরোধের জবাবে অ্যাকাউন্ট তথ্য প্রদান করি +const accountInformation = async signature => { + const fromAddress = await recoverAddress({ + hash: hashMessage("Get account data " + Math.floor((new Date().getTime())/60000)), + signature + }) +``` + +অ্যাকাউন্ট তথ্য প্রদান করতে, আমাদের শুধুমাত্র সিগনেচার প্রয়োজন। কারণ হল আমরা ইতিমধ্যেই জানি মেসেজটি কী হবে, এবং তাই মেসেজ হ্যাস। + +```js +const processMessage = async (message, signature) => { +``` + +একটি মেসেজ প্রসেস করুন এবং এটি যে লেনদেন এনকোড করে তা কার্যকর করুন। + +```js + // Get the public key + const pubKey = await recoverPublicKey({ + hash, + signature + }) +``` + +এখন যেহেতু আমরা সার্ভারে জাভাস্ক্রিপ্ট চালাচ্ছি, আমরা ক্লায়েন্টের পরিবর্তে সেখানে পাবলিক কী পুনরুদ্ধার করতে পারি। + +```js + let noirResult + try { + noirResult = await noir.execute({ + message, + signature: signature.slice(2,-2).match(/.{2}/g).map(x => `0x${x}`), + pubKeyX, + pubKeyY, + accounts: Accounts + }) +``` + +`noir.execute` Noir প্রোগ্রামটি চালায়। প্যারামিটারগুলি [`Prover.toml`](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Prover.toml) এ প্রদত্ত প্যারামিটারগুলির সমতুল্য। মনে রাখবেন যে দীর্ঘ মানগুলি হেক্সাডেসিমেল স্ট্রিংগুলির একটি অ্যারে হিসাবে প্রদান করা হয় (`["0x60", "0xA7"]`), একক হেক্সাডেসিমেল মান হিসাবে নয় (`0x60A7`), যেভাবে Viem এটি করে। + +```js + } catch (err) { + console.log(`Noir error: ${err}`) + throw Error("Invalid transaction, not processed") + } +``` + +যদি কোনো ত্রুটি থাকে, তবে এটি ধরুন এবং তারপর একটি সরলীকৃত সংস্করণ ক্লায়েন্টে রিলে করুন। + +```js + Accounts[fromAccountNumber].nonce++ + Accounts[fromAccountNumber].balance -= amount + Accounts[toAccountNumber].balance += amount +``` + +লেনদেনটি প্রয়োগ করুন। আমরা এটি ইতিমধ্যেই Noir কোডে করেছি, কিন্তু সেখান থেকে ফলাফল বের করার চেয়ে এখানে এটি আবার করা সহজ। + +```js +let Accounts = [ + { + address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + balance: 5000, + nonce: 0, + }, +``` + +প্রাথমিক `Accounts` স্ট্রাকচার। + +### স্টেজ ৩ - ইথেরিয়াম স্মার্ট কন্ট্র্যাক্ট {#stage-3} + +1. সার্ভার এবং ক্লায়েন্ট প্রসেস বন্ধ করুন। + +2. স্মার্ট কন্ট্র্যাক্ট সহ ব্রাঞ্চটি ডাউনলোড করুন এবং নিশ্চিত করুন যে আপনার সমস্ত প্রয়োজনীয় মডিউল রয়েছে। + + ```sh + git checkout 03-smart-contracts + cd client + npm install + cd ../server + npm install + ``` + +3. একটি পৃথক কমান্ড-লাইন উইন্ডোতে `anvil` চালান। + +4. ভেরিফিকেশন কী এবং সলিডিটি ভেরিফায়ার জেনারেট করুন, তারপর ভেরিফায়ার কোডটি সলিডিটি প্রজেক্টে কপি করুন। + + ```sh + cd noir + bb write_vk -b ./target/zkBank.json -o ./target --oracle_hash keccak + bb write_solidity_verifier -k ./target/vk -o ./target/Verifier.sol + cp target/Verifier.sol ../../smart-contracts/src + ``` + +5. স্মার্ট কন্ট্র্যাক্টে যান এবং `anvil` ব্লকচেইন ব্যবহার করার জন্য পরিবেশ ভেরিয়েবল সেট করুন। + + ```sh + cd ../../smart-contracts + export ETH_RPC_URL=http://localhost:8545 + ETH_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + ``` + +6. `Verifier.sol` ডিপ্লয় করুন এবং অ্যাড্রেসটি একটি এনভায়রনমেন্ট ভেরিয়েবলে সংরক্ষণ করুন। + + ```sh + VERIFIER_ADDRESS=`forge create src/Verifier.sol:HonkVerifier --private-key $ETH_PRIVATE_KEY --optimize --broadcast | awk '/Deployed to:/ {print $3}'` + echo $VERIFIER_ADDRESS + ``` + +7. `ZkBank` কন্ট্রাক্ট ডিপ্লয় করুন। + + ```sh + ZKBANK_ADDRESS=`forge create ZkBank --private-key $ETH_PRIVATE_KEY --broadcast --constructor-args $VERIFIER_ADDRESS 0x199aa62af8c1d562a6ec96e66347bf3240ab2afb5d022c895e6bf6a5e617167b | awk '/Deployed to:/ {print $3}'` + echo $ZKBANK_ADDRESS + ``` + + `0x199..67b` মানটি হল `Accounts`-এর প্রাথমিক স্টেটের পেডারসেন হ্যাস। যদি আপনি `server/index.mjs`-এ এই প্রাথমিক স্টেট পরিবর্তন করেন, তবে আপনি জিরো-নলেজ প্রুফ দ্বারা রিপোর্ট করা প্রাথমিক হ্যাস দেখতে একটি লেনদেন চালাতে পারেন। + +8. সার্ভারটি চালান। + + ```sh + cd ../server + npm run start + ``` + +9. একটি ভিন্ন কমান্ড-লাইন উইন্ডোতে ক্লায়েন্ট চালান। + + ```sh + cd client + npm run dev + ``` + +10. কিছু লেনদেন চালান। + +11. স্টেটটি অনচেইনে পরিবর্তিত হয়েছে কিনা তা যাচাই করতে, সার্ভার প্রসেসটি রিস্টার্ট করুন। দেখুন `ZkBank` আর লেনদেন গ্রহণ করছে না, কারণ লেনদেনগুলিতে আসল হ্যাস মান অনচেইনে সংরক্ষিত হ্যাস মান থেকে ভিন্ন। + + এটি প্রত্যাশিত ধরনের ত্রুটি। + + ``` + ori@CryptoDocGuy:~/x/250911-zk-bank/server$ npm run start + + > server@1.0.0 start + > node --experimental-json-modules index.mjs + + Listening on port 3000 + Verification error: ContractFunctionExecutionError: The contract function "processTransaction" reverted with the following reason: + Wrong old state hash + + Contract Call: + address: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + function: processTransaction(bytes _proof, bytes32[] _publicInputs) + args: (0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf00000000000000000000000000000000000000000000000b75c020998797da7800000000000000000000000000000000000000000000000 + ``` + +#### `server/index.mjs` {#server-index-mjs-2} + +এই ফাইলের পরিবর্তনগুলি বেশিরভাগই আসল প্রুফ তৈরি করা এবং এটি অনচেইনে জমা দেওয়ার সাথে সম্পর্কিত। + +```js +import { exec } from 'child_process' +import util from 'util' + +const execPromise = util.promisify(exec) +``` + +আমাদের অনচেইনে পাঠানোর জন্য আসল প্রুফ তৈরি করতে [Barretenberg প্যাকেজ](https://github.com/AztecProtocol/aztec-packages/tree/next/barretenberg) ব্যবহার করতে হবে। আমরা এই প্যাকেজটি হয় কমান্ড-লাইন ইন্টারফেস (`bb`) চালিয়ে অথবা [JavaScript লাইব্রেরি, `bb.js`](https://www.npmjs.com/package/@aztec/bb.js) ব্যবহার করে ব্যবহার করতে পারি। JavaScript লাইব্রেরিটি নেটিভভাবে কোড চালানোর চেয়ে অনেক ধীর, তাই আমরা কমান্ড-লাইন ব্যবহার করার জন্য এখানে [`exec`](https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback) ব্যবহার করি। + +মনে রাখবেন যে আপনি যদি `bb.js` ব্যবহার করার সিদ্ধান্ত নেন, তবে আপনাকে এমন একটি সংস্করণ ব্যবহার করতে হবে যা আপনার ব্যবহৃত Noir-এর সংস্করণের সাথে সামঞ্জস্যপূর্ণ। লেখার সময়, বর্তমান Noir সংস্করণ (1.0.0-beta.11) `bb.js` সংস্করণ 0.87 ব্যবহার করে। + +```js +const zkBankAddress = process.env.ZKBANK_ADDRESS || "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" +``` + +এখানে অ্যাড্রেসটি হল সেইটি যা আপনি একটি পরিষ্কার `anvil` দিয়ে শুরু করে এবং উপরের নির্দেশাবলী অনুসরণ করে পান। + +```js +const walletClient = createWalletClient({ + chain: anvil, + transport: http(), + account: privateKeyToAccount("0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6") +}) +``` + +এই প্রাইভেট কীটি `anvil`-এর ডিফল্ট প্রি-ফান্ডেড অ্যাকাউন্টগুলির মধ্যে একটি। + +```js +const generateProof = async (witness, fileID) => { +``` + +`bb` এক্সিকিউটেবল ব্যবহার করে একটি প্রুফ জেনারেট করুন। + +```js + const fname = `witness-${fileID}.gz` + await fs.writeFile(fname, witness) +``` + +উইটনেস একটি ফাইলে লিখুন। + +```js + await execPromise(`bb prove -b ./noir/target/zkBank.json -w ${fname} -o ${fileID} --oracle_hash keccak --output_format fields`) +``` + +প্রকৃতপক্ষে প্রুফটি তৈরি করুন। এই ধাপটি পাবলিক ভেরিয়েবল সহ একটি ফাইলও তৈরি করে, কিন্তু আমাদের সেটির প্রয়োজন নেই। আমরা `noir.execute` থেকে সেই ভেরিয়েবলগুলো ইতিমধ্যেই পেয়েছি। + +```js + const proof = "0x" + JSON.parse(await fs.readFile(`./${fileID}/proof_fields.json`)).reduce((a,b) => a+b, "").replace(/0x/g, "") +``` + +প্রুফটি হল `Field` মানগুলির একটি JSON অ্যারে, যার প্রতিটি একটি হেক্সাডেসিমেল মান হিসাবে উপস্থাপিত। তবে, আমাদের এটি লেনদেনে একটি একক `bytes` মান হিসাবে পাঠাতে হবে, যা Viem একটি বড় হেক্সাডেসিমেল স্ট্রিং দ্বারা উপস্থাপন করে। এখানে আমরা সমস্ত মানগুলিকে একত্রিত করে, সমস্ত `0x` সরিয়ে এবং শেষে একটি যোগ করে বিন্যাস পরিবর্তন করি। + +```js + await execPromise(`rm -r ${fname} ${fileID}`) + + return proof +} +``` + +পরিষ্কার করুন এবং প্রুফটি ফেরত দিন। + +```js +const processMessage = async (message, signature) => { + . + . + . + + const publicFields = noirResult.returnValue.map(x=>'0x' + x.slice(2).padStart(64, "0")) +``` + +পাবলিক ফিল্ডগুলিকে 32-বাইট মানের একটি অ্যারে হতে হবে। তবে, যেহেতু আমাদের লেনদেন হ্যাস দুটি `Field` মানের মধ্যে ভাগ করতে হয়েছিল, তাই এটি একটি 16-বাইট মান হিসাবে প্রদর্শিত হয়। এখানে আমরা শূন্য যোগ করি যাতে Viem বুঝতে পারে এটি আসলে 32 বাইট। + +```js + const proof = await generateProof(noirResult.witness, `${fromAddress}-${nonce}`) +``` + +প্রতিটি অ্যাড্রেস প্রতিটি ননস শুধুমাত্র একবার ব্যবহার করে যাতে আমরা উইটনেস ফাইল এবং আউটপুট ডিরেক্টরির জন্য একটি অনন্য শনাক্তকারী হিসাবে `fromAddress` এবং `nonce`-এর একটি সমন্বয় ব্যবহার করতে পারি। + +```js + try { + await zkBank.write.processTransaction([ + proof, publicFields]) + } catch (err) { + console.log(`Verification error: ${err}`) + throw Error("Can't verify the transaction onchain") + } + . + . + . +} +``` + +লেনদেনটি চেইনে পাঠান। + +#### `smart-contracts/src/ZkBank.sol` {#smart-contracts-src-zkbank-sol} + +এটি অনচেইন কোড যা লেনদেন গ্রহণ করে। + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.21; + +import {HonkVerifier} from "./Verifier.sol"; + +contract ZkBank { + HonkVerifier immutable myVerifier; + bytes32 currentStateHash; + + constructor(address _verifierAddress, bytes32 _initialStateHash) { + currentStateHash = _initialStateHash; + myVerifier = HonkVerifier(_verifierAddress); + } +``` + +অনচেইন কোডকে দুটি ভেরিয়েবলের ট্র্যাক রাখতে হবে: ভেরিফায়ার (একটি পৃথক কন্ট্রাক্ট যা `nargo` দ্বারা তৈরি) এবং বর্তমান স্টেট হ্যাস। + +```solidity + event TransactionProcessed( + bytes32 indexed transactionHash, + bytes32 oldStateHash, + bytes32 newStateHash + ); +``` + +যখনই স্টেট পরিবর্তন হয়, আমরা একটি `TransactionProcessed` ইভেন্ট নির্গত করি। + +```solidity + function processTransaction( + bytes calldata _proof, + bytes32[] calldata _publicFields + ) public { +``` + +এই ফাংশনটি লেনদেন প্রক্রিয়া করে। এটি প্রুফ (`bytes` হিসাবে) এবং পাবলিক ইনপুট (`bytes32` অ্যারে হিসাবে) গ্রহণ করে, যে ফরম্যাটে ভেরিফায়ারের প্রয়োজন (অনচেইন প্রসেসিং এবং তাই গ্যাস খরচ কমাতে)। + +```solidity + require(_publicInputs[0] == currentStateHash, + "Wrong old state hash"); +``` + +জিরো-নলেজ প্রুফটি হতে হবে যে লেনদেনটি আমাদের বর্তমান হ্যাস থেকে একটি নতুনটিতে পরিবর্তিত হয়। + +```solidity + myVerifier.verify(_proof, _publicFields); +``` + +জিরো-নলেজ প্রুফ যাচাই করার জন্য ভেরিফায়ার কন্ট্রাক্টে কল করুন। জিরো-নলেজ প্রুফ ভুল হলে এই ধাপটি লেনদেনটিকে বাতিল করে দেয়। + +```solidity + currentStateHash = _publicFields[1]; + + emit TransactionProcessed( + _publicFields[2]<<128 | _publicFields[3], + _publicFields[0], + _publicFields[1] + ); + } +} +``` + +যদি সবকিছু ঠিক থাকে, তাহলে স্টেট হ্যাসটি নতুন মানে আপডেট করুন এবং একটি `TransactionProcessed` ইভেন্ট নির্গত করুন। + +## কেন্দ্রীভূত উপাদান দ্বারা অপব্যবহার {#abuses} + +তথ্য নিরাপত্তা তিনটি বৈশিষ্ট্য নিয়ে গঠিত: + +- _গোপনীয়তা_, ব্যবহারকারীরা যে তথ্য পড়ার জন্য অনুমোদিত নন তা পড়তে পারে না। +- _অখণ্ডতা_, অনুমোদিত ব্যবহারকারী ছাড়া এবং অনুমোদিত পদ্ধতিতে তথ্য পরিবর্তন করা যায় না। +- _প্রাপ্যতা_, অনুমোদিত ব্যবহারকারীরা সিস্টেমটি ব্যবহার করতে পারেন। + +এই সিস্টেমে, জিরো-নলেজ প্রুফের মাধ্যমে অখণ্ডতা প্রদান করা হয়। প্রাপ্যতা নিশ্চিত করা অনেক কঠিন, এবং গোপনীয়তা অসম্ভব, কারণ ব্যাংককে প্রতিটি অ্যাকাউন্টের ব্যালেন্স এবং সমস্ত লেনদেন জানতে হয়। একটি সত্তার কাছে থাকা তথ্য শেয়ার করা থেকে বিরত রাখার কোনো উপায় নেই। + +[স্টেলথ অ্যাড্রেস](https://vitalik.eth.limo/general/2023/01/20/stealth.html) ব্যবহার করে একটি সত্যিকারের গোপনীয় ব্যাংক তৈরি করা সম্ভব হতে পারে, কিন্তু তা এই নিবন্ধের সুযোগের বাইরে। + +### মিথ্যা তথ্য {#false-info} + +সার্ভার যেভাবে অখণ্ডতা লঙ্ঘন করতে পারে তার একটি উপায় হল [ডেটা অনুরোধ করা হলে](https://github.com/qbzzt/250911-zk-bank/blob/03-smart-contracts/server/index.mjs#L278-L291) মিথ্যা তথ্য প্রদান করা। + +এটি সমাধান করতে, আমরা একটি দ্বিতীয় Noir প্রোগ্রাম লিখতে পারি যা অ্যাকাউন্টগুলিকে ব্যক্তিগত ইনপুট হিসাবে এবং যে অ্যাড্রেসের জন্য তথ্য অনুরোধ করা হয়েছে তা পাবলিক ইনপুট হিসাবে গ্রহণ করে। আউটপুট হল সেই অ্যাড্রেসের ব্যালেন্স এবং ননস, এবং অ্যাকাউন্টগুলির হ্যাস। + +অবশ্যই, এই প্রুফটি অনচেইনে যাচাই করা যাবে না, কারণ আমরা অনচেইনে ননস এবং ব্যালেন্স পোস্ট করতে চাই না। তবে, এটি ব্রাউজারে চলমান ক্লায়েন্ট কোড দ্বারা যাচাই করা যেতে পারে। + +### বাধ্যতামূলক লেনদেন {#forced-txns} + +L2s-এ অ্যাভেইলেবিলিটি নিশ্চিত করা এবং সেন্সরশিপ প্রতিরোধের জন্য সাধারণ মেকানিজম হল [ফোর্সড ট্রানজাকশন](https://docs.optimism.io/stack/transactions/forced-transaction)। কিন্তু বাধ্যতামূলক লেনদেন জিরো-নলেজ প্রুফের সাথে একত্রিত হয় না। সার্ভার হল একমাত্র সত্তা যা লেনদেন যাচাই করতে পারে। + +আমরা `smart-contracts/src/ZkBank.sol` পরিবর্তন করে বাধ্যতামূলক লেনদেন গ্রহণ করতে পারি এবং সার্ভারকে সেগুলি প্রক্রিয়া না করা পর্যন্ত স্টেট পরিবর্তন করা থেকে বিরত রাখতে পারি। তবে, এটি আমাদের একটি সহজ ডিনাইয়াল-অব-সার্ভিস অ্যাটাকের জন্য উন্মুক্ত করে। যদি একটি বাধ্যতামূলক লেনদেন অবৈধ হয় এবং তাই প্রক্রিয়া করা অসম্ভব হয়? + +সমাধান হল একটি জিরো-নলেজ প্রুফ থাকা যে একটি বাধ্যতামূলক লেনদেন অবৈধ। এটি সার্ভারকে তিনটি বিকল্প দেয়: + +- বাধ্যতামূলক লেনদেনটি প্রক্রিয়া করুন, এটি প্রক্রিয়া করা হয়েছে এবং নতুন স্টেট হ্যাস রয়েছে তার একটি জিরো-নলেজ প্রুফ প্রদান করুন। +- বাধ্যতামূলক লেনদেনটি প্রত্যাখ্যান করুন, এবং কন্ট্রাক্টকে একটি জিরো-নলেজ প্রুফ প্রদান করুন যে লেনদেনটি অবৈধ (অজানা অ্যাড্রেস, খারাপ ননস, বা অপর্যাপ্ত ব্যালেন্স)। +- বাধ্যতামূলক লেনদেন উপেক্ষা করুন। সার্ভারকে প্রকৃতপক্ষে লেনদেনটি প্রক্রিয়া করতে বাধ্য করার কোনো উপায় নেই, তবে এর মানে হল পুরো সিস্টেমটি अनुपলব্ধ। + +#### উপস্থিতি বন্ড {#avail-bonds} + +বাস্তব-জীবনের বাস্তবায়নে, সার্ভারটি চালু রাখার জন্য সম্ভবত কোনো ধরনের লাভের উদ্দেশ্য থাকবে। আমরা এই প্রণোদনাটিকে আরও শক্তিশালী করতে পারি সার্ভারকে একটি অ্যাভেইলেবিলিটি বন্ড পোস্ট করতে বলে যা যে কেউ বার্ন করতে পারবে যদি একটি ফোর্সড ট্রানজাকশন একটি নির্দিষ্ট সময়ের মধ্যে প্রক্রিয়াজাত না হয়। + +### খারাপ Noir কোড {#bad-noir-code} + +সাধারণত, একটি স্মার্ট কন্ট্র্যাক্টের উপর মানুষের বিশ্বাস অর্জনের জন্য আমরা সোর্স কোডটি একটি [ব্লক এক্সপ্লোরারে](https://eth.blockscout.com/address/0x7D16d2c4e96BCFC8f815E15b771aC847EcbDB48b?tab=contract) আপলোড করি। তবে, জিরো-নলেজ প্রুফের ক্ষেত্রে, এটি অপর্যাপ্ত। + +`Verifier.sol` ভেরিফিকেশন কী ধারণ করে, যা Noir প্রোগ্রামের একটি ফাংশন। তবে, সেই কী আমাদের বলে না যে Noir প্রোগ্রামটি কী ছিল। প্রকৃতপক্ষে একটি বিশ্বস্ত সমাধান পেতে, আপনাকে Noir প্রোগ্রাম (এবং এটি তৈরি করা সংস্করণ) আপলোড করতে হবে। অন্যথায়, জিরো-নলেজ প্রুফগুলি একটি ভিন্ন প্রোগ্রামকে প্রতিফলিত করতে পারে, যার একটি ব্যাক ডোর রয়েছে। + +ব্লক এক্সপ্লোরাররা আমাদের Noir প্রোগ্রাম আপলোড এবং যাচাই করার অনুমতি না দেওয়া পর্যন্ত, আপনার এটি নিজেই করা উচিত (বিশেষত [IPFS](/developers/tutorials/ipfs-decentralized-ui/)-এ)। তাহলে অভিজ্ঞ ব্যবহারকারীরা সোর্স কোড ডাউনলোড করতে, নিজেরা কম্পাইল করতে, `Verifier.sol` তৈরি করতে এবং এটি অনচেইনে থাকা কোডের সাথে অভিন্ন কিনা তা যাচাই করতে সক্ষম হবেন। + +## উপসংহার {#conclusion} + +প্লাজমা-টাইপ অ্যাপ্লিকেশনগুলির জন্য তথ্য সংরক্ষণের জন্য একটি কেন্দ্রীভূত উপাদান প্রয়োজন। এটি সম্ভাব্য দুর্বলতা উন্মুক্ত করে কিন্তু, বিনিময়ে, আমাদের ব্লকচেইনে উপলব্ধ নয় এমন উপায়ে গোপনীয়তা রক্ষা করতে দেয়। জিরো-নলেজ প্রুফের মাধ্যমে আমরা অখণ্ডতা নিশ্চিত করতে পারি এবং সম্ভবত কেন্দ্রীভূত উপাদানটি চালানো যার জন্য অর্থনৈতিকভাবে সুবিধাজনক হবে তার জন্য প্রাপ্যতা বজায় রাখতে পারি। + +[আমার আরও কাজের জন্য এখানে দেখুন](https://cryptodocguy.pro/)। + +## স্বীকৃতি {#acknowledgements} + +- Josh Crites এই নিবন্ধের একটি খসড়া পড়েছিলেন এবং একটি কাঁটাযুক্ত Noir সমস্যায় আমাকে সাহায্য করেছিলেন। + +যেকোনো অবশিষ্ট ত্রুটির জন্য আমি দায়ী। diff --git a/public/content/translations/bn/developers/tutorials/calling-a-smart-contract-from-javascript/index.md b/public/content/translations/bn/developers/tutorials/calling-a-smart-contract-from-javascript/index.md new file mode 100644 index 00000000000..b5905f1d5e8 --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/calling-a-smart-contract-from-javascript/index.md @@ -0,0 +1,131 @@ +--- +title: "JavaScript থেকে একটি স্মার্ট কন্ট্র্যাক্ট কল করা" +description: "একটি Dai টোকেনের উদাহরণ ব্যবহার করে JavaScript থেকে কীভাবে একটি স্মার্ট কন্ট্র্যাক্ট ফাংশন কল করতে হয়" +author: jdourlens +tags: [ "লেনদেনসমূহ", "ফ্রন্টএন্ড", "JavaScript", "web3.js" ] +skill: beginner +lang: bn +published: 2020-04-19 +source: EthereumDev +sourceUrl: https://ethereumdev.io/calling-a-smart-contract-from-javascript/ +address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" +--- + +এই টিউটোরিয়ালে আমরা দেখব কিভাবে জাভাস্ক্রিপ্ট থেকে একটি [স্মার্ট কন্ট্র্যাক্ট](/developers/docs/smart-contracts/) ফাংশন কল করতে হয়। প্রথমটি হলো একটি স্মার্ট কন্ট্র্যাক্টের স্টেট পড়া (যেমন, একটি ERC20 হোল্ডারের ব্যালেন্স), তারপর আমরা একটি টোকেন ট্রান্সফার করে ব্লকচেইনের স্টেট পরিবর্তন করব। ব্লকচেইনের সাথে ইন্টারঅ্যাক্ট করার জন্য আপনার ইতিমধ্যেই [একটি JS এনভায়রনমেন্ট সেট আপ করার](/developers/tutorials/set-up-web3js-to-use-ethereum-in-javascript/) সাথে পরিচিত হওয়া উচিত। + +এই উদাহরণের জন্য আমরা DAI টোকেন নিয়ে খেলব, পরীক্ষার উদ্দেশ্যে আমরা ganache-cli ব্যবহার করে ব্লকচেইন ফর্ক করব এবং এমন একটি অ্যাড্রেস আনলক করব যাতে ইতিমধ্যে প্রচুর DAI রয়েছে: + +```bash +ganache-cli -f https://mainnet.infura.io/v3/[আপনার INFURA কী] -d -i 66 1 --unlock 0x4d10ae710Bd8D1C31bd7465c8CBC3add6F279E81 +``` + +একটি স্মার্ট কন্ট্র্যাক্টের সাথে ইন্টারঅ্যাক্ট করার জন্য আমাদের এটির অ্যাড্রেস এবং ABI প্রয়োজন হবে: + +```js +const ERC20TransferABI = [ + { + constant: false, + inputs: [ + { + name: "_to", + type: "address", + }, + { + name: "_value", + type: "uint256", + }, + ], + name: "transfer", + outputs: [ + { + name: "", + type: "bool", + }, + ], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: true, + inputs: [ + { + name: "_owner", + type: "address", + }, + ], + name: "balanceOf", + outputs: [ + { + name: "balance", + type: "uint256", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, +] + +const DAI_ADDRESS = "0x6b175474e89094c44da98b954eedeac495271d0f" +``` + +এই প্রজেক্টের জন্য আমরা শুধুমাত্র `balanceOf` এবং `transfer` ফাংশন রাখার জন্য সম্পূর্ণ ERC20 ABI ছেঁটে ফেলেছি কিন্তু আপনি [সম্পূর্ণ ERC20 ABI এখানে](https://ethereumdev.io/abi-for-erc20-contract-on-ethereum/) খুঁজে পেতে পারেন। + +এরপর আমাদের স্মার্ট কন্ট্র্যাক্টটিকে ইনস্ট্যানশিয়েট করতে হবে: + +```js +const web3 = new Web3("http://localhost:8545") + +const daiToken = new web3.eth.Contract(ERC20TransferABI, DAI_ADDRESS) +``` + +আমরা দুটি অ্যাড্রেসও সেট আপ করব: + +- যে ট্রান্সফারটি পাবে এবং +- যেটি আমরা ইতিমধ্যে আনলক করেছি সেটি এটি পাঠাবে: + +```js +const senderAddress = "0x4d10ae710Bd8D1C31bd7465c8CBC3add6F279E81" +const receiverAddress = "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" +``` + +পরবর্তী অংশে আমরা `balanceOf` ফাংশনটি কল করব উভয় অ্যাড্রেসে বর্তমানে কী পরিমাণ টোকেন রয়েছে তা জানতে। + +## কল: একটি স্মার্ট কন্ট্র্যাক্ট থেকে ভ্যালু পড়া {#call-reading-value-from-a-smart-contract} + +প্রথম উদাহরণটি একটি “কনস্ট্যান্ট” মেথড কল করবে এবং কোনো লেনদেন না পাঠিয়ে EVM-এ এর স্মার্ট কন্ট্র্যাক্ট মেথডটি কার্যকর করবে। এর জন্য আমরা একটি অ্যাড্রেসের ERC20 ব্যালেন্স পড়ব। [ERC20 টোকেন সম্পর্কে আমাদের আর্টিকেলটি পড়ুন](/developers/tutorials/understand-the-erc-20-token-smart-contract/)। + +আপনি যেটির জন্য ABI প্রদান করেছেন সেই ইনস্ট্যানশিয়েটেড স্মার্ট কন্ট্র্যাক্ট মেথডগুলো নিম্নলিখিত উপায়ে অ্যাক্সেস করতে পারেন: `yourContract.methods.methodname`। `call` ফাংশন ব্যবহার করে আপনি ফাংশনটি কার্যকর করার ফলাফল পাবেন। + +```js +daiToken.methods.balanceOf(senderAddress).call(function (err, res) { + if (err) { + console.log("একটি ত্রুটি ঘটেছে", err) + return + } + console.log("ব্যালেন্স হলো: ", res) +}) +``` + +মনে রাখবেন যে DAI ERC20-এর 18টি ডেসিমেল আছে যার অর্থ সঠিক পরিমাণ পেতে আপনাকে 18টি শূন্য সরাতে হবে। uint256 স্ট্রিং হিসাবে ফেরত দেওয়া হয় কারণ জাভাস্ক্রিপ্ট বড় সংখ্যাসূচক মান হ্যান্ডেল করে না। আপনি যদি নিশ্চিত না হন [JS-এ বড় সংখ্যা নিয়ে কীভাবে কাজ করতে হয়, bignumber.js সম্পর্কে আমাদের টিউটোরিয়ালটি দেখুন](https://ethereumdev.io/how-to-deal-with-big-numbers-in-javascript/)। + +## পাঠানো: একটি স্মার্ট কন্ট্র্যাক্ট ফাংশনে একটি লেনদেন পাঠানো {#send-sending-a-transaction-to-a-smart-contract-function} + +দ্বিতীয় উদাহরণের জন্য আমরা আমাদের দ্বিতীয় অ্যাড্রেসে 10 DAI পাঠাতে DAI স্মার্ট কন্ট্র্যাক্টের transfer ফাংশনটিকে কল করব। transfer ফাংশন দুটি প্যারামিটার গ্রহণ করে: প্রাপকের অ্যাড্রেস এবং ট্রান্সফারের জন্য টোকেনের পরিমাণ: + +```js +daiToken.methods + .transfer(receiverAddress, "100000000000000000000") + .send({ from: senderAddress }, function (err, res) { + if (err) { + console.log("একটি ত্রুটি ঘটেছে", err) + return + } + console.log("লেনদেনের হ্যাস: " + res) + }) +``` + +কল ফাংশনটি লেনদেনের হ্যাস রিটার্ন করে যা ব্লকচেইনে মাইন করা হবে। ইথেরিয়ামে, লেনদেনের হ্যাসগুলো অনুমানযোগ্য - এভাবেই আমরা একটি লেনদেন কার্যকর হওয়ার আগে তার হ্যাস পেতে পারি ([হ্যাসগুলো কীভাবে গণনা করা হয় তা এখানে জানুন](https://ethereum.stackexchange.com/questions/45648/how-to-calculate-the-assigned-txhash-of-a-transaction))। + +যেহেতু ফাংশনটি শুধুমাত্র ব্লকচেইনে লেনদেন জমা দেয়, তাই এটি কখন মাইন করা হয়েছে এবং ব্লকচেইনে অন্তর্ভুক্ত হয়েছে তা না জানা পর্যন্ত আমরা ফলাফল দেখতে পারি না। পরবর্তী টিউটোরিয়ালে আমরা শিখব [কীভাবে একটি লেনদেনের হ্যাস জেনে ব্লকচেইনে সেটি কার্যকর হওয়ার জন্য অপেক্ষা করতে হয়](https://ethereumdev.io/waiting-for-a-transaction-to-be-mined-on-ethereum-with-js/)। diff --git a/public/content/translations/bn/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md b/public/content/translations/bn/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md new file mode 100644 index 00000000000..e7b06c7d21f --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md @@ -0,0 +1,585 @@ +--- +title: "আপনার কন্ট্র্যাক্টের জন্য একটি ইউজার ইন্টারফেস তৈরি করা" +description: "TypeScript, React, Vite এবং Wagmi-এর মতো আধুনিক উপাদান ব্যবহার করে, আমরা একটি আধুনিক, কিন্তু সংক্ষিপ্ত, ইউজার ইন্টারফেস দেখব এবং শিখব কীভাবে ইউজার ইন্টারফেসের সাথে একটি ওয়ালেট সংযোগ করতে হয়, তথ্য পড়ার জন্য একটি স্মার্ট কন্ট্র্যাক্ট কল করা, একটি স্মার্ট কন্ট্র্যাক্টে লেনদেন পাঠানো, এবং পরিবর্তন শনাক্ত করতে একটি স্মার্ট কন্ট্র্যাক্ট থেকে ইভেন্টগুলি নিরীক্ষণ করা।" +author: Ori Pomerantz +tags: [ "typescript", "react", "vite", "wagmi", "ফ্রন্টএন্ড" ] +skill: beginner +published: 2023-11-01 +lang: bn +sidebarDepth: 3 +--- + +আপনি একটি ফিচার খুঁজে পেয়েছেন যা ইথেরিয়াম ইকোসিস্টেমে আমাদের প্রয়োজন। আপনি এটি বাস্তবায়নের জন্য স্মার্ট কন্ট্র্যাক্ট লিখেছেন, এবং সম্ভবত কিছু সম্পর্কিত কোড যা অফচেইন চলে। এটা দারুণ! দুর্ভাগ্যবশত, একটি ইউজার ইন্টারফেস ছাড়া আপনার কোনো ব্যবহারকারী থাকবে না, এবং শেষবার যখন আপনি একটি ওয়েব সাইট লিখেছিলেন তখন মানুষ ডায়াল-আপ মডেম ব্যবহার করত এবং জাভাস্ক্রিপ্ট নতুন ছিল। + +এই নিবন্ধটি আপনার জন্য। আমি ধরে নিচ্ছি আপনি প্রোগ্রামিং জানেন, এবং হয়তো কিছুটা জাভাস্ক্রিপ্ট এবং HTML জানেন, কিন্তু আপনার ইউজার ইন্টারফেসের দক্ষতা মরিচা ধরা এবং পুরানো। একসাথে আমরা একটি সহজ আধুনিক অ্যাপ্লিকেশন দেখব যাতে আপনি দেখতে পারেন আজকাল এটি কীভাবে করা হয়। + +## এটি কেন গুরুত্বপূর্ণ {#why-important} + +তত্ত্বগতভাবে, আপনি আপনার কন্ট্র্যাক্টের সাথে ইন্টারঅ্যাক্ট করার জন্য লোকেদের [Etherscan](https://holesky.etherscan.io/address/0x432d810484add7454ddb3b5311f0ac2e95cecea8#writeContract) বা [Blockscout](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=write_contract) ব্যবহার করতে দিতে পারেন। অভিজ্ঞ ইথেরিয়ানদের জন্য এটি দারুণ হবে। কিন্তু আমরা [আরও এক বিলিয়ন মানুষকে](https://blog.ethereum.org/2021/05/07/ethereum-for-the-next-billion) পরিষেবা দেওয়ার চেষ্টা করছি। একটি দুর্দান্ত ব্যবহারকারী অভিজ্ঞতা ছাড়া এটি ঘটবে না, এবং একটি বন্ধুত্বপূর্ণ ইউজার ইন্টারফেস এর একটি বড় অংশ। + +## গ্রিটার অ্যাপ্লিকেশন {#greeter-app} + +একটি আধুনিক UI কীভাবে কাজ করে তার পিছনে অনেক তত্ত্ব রয়েছে, এবং [অনেক ভাল সাইট](https://react.dev/learn/thinking-in-react) [যা এটি ব্যাখ্যা করে](https://wagmi.sh/core/getting-started) তা রয়েছে। সেই সাইটগুলির দ্বারা করা চমৎকার কাজ পুনরাবৃত্তি করার পরিবর্তে, আমি ধরে নেব যে আপনি করে শেখা পছন্দ করেন এবং এমন একটি অ্যাপ্লিকেশন দিয়ে শুরু করতে চান যা নিয়ে আপনি খেলতে পারেন। কাজগুলি সম্পন্ন করার জন্য আপনার এখনও তত্ত্বের প্রয়োজন, এবং আমরা এটিতে আসব - আমরা কেবল সোর্স ফাইল ধরে ধরে যাব, এবং যখন আমরা সেগুলিতে আসব তখন বিষয়গুলি নিয়ে আলোচনা করব। + +### ইনস্টলেশন {#installation} + +1. প্রয়োজনে, আপনার ওয়ালেটে [Holesky ব্লকচেইন](https://chainlist.org/?search=holesky&testnets=true) যোগ করুন এবং [টেস্ট ETH পান](https://www.holeskyfaucet.io/)। + +2. গিটহাব রিপোজিটরিটি ক্লোন করুন। + + ```sh + git clone https://github.com/qbzzt/20230801-modern-ui.git + ``` + +3. প্রয়োজনীয় প্যাকেজগুলি ইনস্টল করুন। + + ```sh + cd 20230801-modern-ui + pnpm install + ``` + +4. অ্যাপ্লিকেশনটি শুরু করুন। + + ```sh + pnpm dev + ``` + +5. অ্যাপ্লিকেশন দ্বারা দেখানো URL-এ ব্রাউজ করুন। বেশিরভাগ ক্ষেত্রে, এটি হল [http://localhost:5173/](http://localhost:5173/)। + +6. আপনি কন্ট্র্যাক্টের সোর্স কোড দেখতে পারেন, যা হার্ডহ্যাটের গ্রিটারের একটি সামান্য পরিবর্তিত সংস্করণ, [একটি ব্লকচেইন এক্সপ্লোরারে](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=contract)। + +### ফাইল ওয়াক থ্রু {#file-walk-through} + +#### `index.html` {#index-html} + +এই ফাইলটি স্ট্যান্ডার্ড HTML বয়লারপ্লেট, এই লাইনটি ছাড়া, যা স্ক্রিপ্ট ফাইলটি আমদানি করে। + +```html + +``` + +#### `src/main.tsx` {#main-tsx} + +ফাইলের এক্সটেনশনটি আমাদের বলে যে এই ফাইলটি একটি [রিয়্যাক্ট কম্পোনেন্ট](https://www.w3schools.com/react/react_components.asp) যা [টাইপস্ক্রিপ্টে](https://www.typescriptlang.org/) লেখা, জাভাস্ক্রিপ্টের একটি এক্সটেনশন যা [টাইপ চেকিং](https://en.wikipedia.org/wiki/Type_system#Type_checking) সমর্থন করে। টাইপস্ক্রিপ্ট জাভাস্ক্রিপ্টে কম্পাইল করা হয়, তাই আমরা এটি ক্লায়েন্ট-সাইড এক্সিকিউশনের জন্য ব্যবহার করতে পারি। + +```tsx +import '@rainbow-me/rainbowkit/styles.css' +import { RainbowKitProvider } from '@rainbow-me/rainbowkit' +import * as React from 'react' +import * as ReactDOM from 'react-dom/client' +import { WagmiConfig } from 'wagmi' +import { chains, config } from './wagmi' +``` + +আমাদের প্রয়োজনীয় লাইব্রেরি কোডটি ইম্পোর্ট করুন। + +```tsx +import { App } from './App' +``` + +অ্যাপ্লিকেশনটি বাস্তবায়নকারী রিয়্যাক্ট কম্পোনেন্টটি ইম্পোর্ট করুন (নিচে দেখুন)। + +```tsx +ReactDOM.createRoot(document.getElementById('root')!).render( +``` + +রুট রিয়্যাক্ট কম্পোনেন্টটি তৈরি করুন। `render`-এর প্যারামিটারটি হল [JSX](https://www.w3schools.com/react/react_jsx.asp), একটি এক্সটেনশন ভাষা যা HTML এবং জাভাস্ক্রিপ্ট/টাইপস্ক্রিপ্ট উভয়ই ব্যবহার করে। এখানকার বিস্ময়সূচক চিহ্নটি টাইপস্ক্রিপ্ট কম্পোনেন্টকে বলে: "আপনি জানেন না যে `document.getElementById('root')` `ReactDOM.createRoot`-এর জন্য একটি বৈধ প্যারামিটার হবে, কিন্তু চিন্তা করবেন না - আমি ডেভেলপার এবং আমি আপনাকে বলছি যে এটি থাকবে"। + +```tsx + +``` + +অ্যাপ্লিকেশনটি [একটি `React.StrictMode` কম্পোনেন্টের](https://react.dev/reference/react/StrictMode) ভিতরে যাচ্ছে। এই কম্পোনেন্টটি রিয়্যাক্ট লাইব্রেরিকে অতিরিক্ত ডিবাগিং চেক যোগ করতে বলে, যা ডেভেলপমেন্টের সময় দরকারী। + +```tsx + +``` + +অ্যাপ্লিকেশনটি [একটি `WagmiConfig` কম্পোনেন্টের](https://wagmi.sh/react/api/WagmiProvider) ভিতরেও রয়েছে। wagmi (উই আর গোয়িং টু মেক ইট) লাইব্রেরি একটি ইথেরিয়াম ডিসেন্ট্রালাইজড এপ্লিকেশন লেখার জন্য রিয়্যাক্ট UI সংজ্ঞাগুলিকে viem লাইব্রেরির সাথে সংযুক্ত করে। + +```tsx + +``` + +এবং পরিশেষে, [একটি `RainbowKitProvider` কম্পোনেন্ট](https://www.rainbowkit.com/)। এই কম্পোনেন্টটি লগ ইন করা এবং ওয়ালেট এবং অ্যাপ্লিকেশনের মধ্যে যোগাযোগ পরিচালনা করে। + +```tsx + +``` + +এখন আমরা অ্যাপ্লিকেশনের জন্য কম্পোনেন্টটি পেতে পারি, যা আসলে UI বাস্তবায়ন করে। কম্পোনেন্টের শেষে `/>` রিয়্যাক্টকে বলে যে XML স্ট্যান্ডার্ড অনুযায়ী এই কম্পোনেন্টের ভিতরে কোনো সংজ্ঞা নেই। + +```tsx + + + , +) +``` + +অবশ্যই, আমাদের অন্যান্য কম্পোনেন্টগুলি বন্ধ করতে হবে। + +#### `src/App.tsx` {#app-tsx} + +```tsx +import { ConnectButton } from '@rainbow-me/rainbowkit' +import { useAccount } from 'wagmi' +import { Greeter } from './components/Greeter' + +export function App() { +``` + +এটি একটি রিয়্যাক্ট কম্পোনেন্ট তৈরি করার স্ট্যান্ডার্ড উপায় - একটি ফাংশন সংজ্ঞায়িত করুন যা প্রতিবার রেন্ডার করার প্রয়োজন হলে কল করা হয়। এই ফাংশনটিতে সাধারণত উপরে কিছু টাইপস্ক্রিপ্ট বা জাভাস্ক্রিপ্ট কোড থাকে, তারপর একটি `return` স্টেটমেন্ট থাকে যা JSX কোড ফেরত দেয়। + +```tsx + const { isConnected } = useAccount() +``` + +এখানে আমরা [`useAccount`](https://wagmi.sh/react/api/hooks/useAccount) ব্যবহার করি এটি পরীক্ষা করতে যে আমরা একটি ওয়ালেটের মাধ্যমে একটি ব্লকচেইনের সাথে সংযুক্ত আছি কি না। + +প্রচলিতভাবে, রিয়্যাক্টে `use...` নামক ফাংশনগুলি হল [হুক](https://www.w3schools.com/react/react_hooks.asp) যা কিছু ধরণের ডেটা ফেরত দেয়। আপনি যখন এই ধরনের হুক ব্যবহার করেন, তখন আপনার কম্পোনেন্ট শুধুমাত্র ডেটা পায় না, বরং যখন সেই ডেটা পরিবর্তন হয় তখন কম্পোনেন্টটি আপডেট করা তথ্য দিয়ে পুনরায় রেন্ডার করা হয়। + +```tsx + return ( + <> +``` + +একটি রিয়্যাক্ট কম্পোনেন্টের JSX _অবশ্যই_ একটি কম্পোনেন্ট ফেরত দেবে। যখন আমাদের একাধিক কম্পোনেন্ট থাকে এবং আমাদের এমন কিছু থাকে না যা "স্বাভাবিকভাবে" মোড়ানো হয় তখন আমরা একটি খালি কম্পোনেন্ট ব্যবহার করি (`<> ...` `) সেগুলিকে একটি একক কম্পোনেন্টে পরিণত করতে। + +```tsx +

Greeter

+ +``` + +আমরা RainbowKit থেকে [`ConnectButton` কম্পোনেন্টটি](https://www.rainbowkit.com/docs/connect-button) পাই। যখন আমরা সংযুক্ত থাকি না, তখন এটি আমাদের একটি `Connect Wallet` বোতাম দেয় যা একটি মোডাল খোলে যা ওয়ালেটগুলি ব্যাখ্যা করে এবং আপনাকে বেছে নিতে দেয় যে আপনি কোনটি ব্যবহার করবেন। যখন আমরা সংযুক্ত থাকি, তখন এটি আমাদের ব্যবহৃত ব্লকচেইন, আমাদের অ্যাকাউন্টের ঠিকানা এবং আমাদের ETH ব্যালেন্স প্রদর্শন করে। আমরা এই ডিসপ্লেগুলি ব্যবহার করে নেটওয়ার্ক পরিবর্তন করতে বা সংযোগ বিচ্ছিন্ন করতে পারি। + +```tsx + {isConnected && ( +``` + +যখন আমাদের একটি JSX-এ আসল জাভাস্ক্রিপ্ট (বা টাইপস্ক্রিপ্ট যা জাভাস্ক্রিপ্টে কম্পাইল করা হবে) প্রবেশ করাতে হয়, আমরা বন্ধনী (`{}`) ব্যবহার করি। + +`a && b` সিনট্যাক্সটি [`a ?`-এর সংক্ষিপ্ত রূপ b : a`](https://www.w3schools.com/react/react_es6_ternary.asp)-এর জন্য। অর্থাৎ, যদি `a`সত্য হয় তবে এটি`b`তে মূল্যায়ন করে এবং অন্যথায় এটি`a`তে মূল্যায়ন করে (যা`false`, `0` ইত্যাদি হতে পারে)। রিয়্যাক্টকে বলার এটি একটি সহজ উপায় যে একটি কম্পোনেন্ট শুধুমাত্র একটি নির্দিষ্ট শর্ত পূরণ হলে প্রদর্শিত হবে। + +এই ক্ষেত্রে, আমরা ব্যবহারকারীকে `Greeter` শুধুমাত্র তখনই দেখাতে চাই যদি ব্যবহারকারী একটি ব্লকচেইনের সাথে সংযুক্ত থাকে। + +```tsx + + )} + + ) +} +``` + +#### `src/components/Greeter.tsx` {#greeter-tsx} + +এই ফাইলটিতে বেশিরভাগ UI কার্যকারিতা রয়েছে। এটিতে এমন সংজ্ঞা রয়েছে যা সাধারণত একাধিক ফাইলে থাকত, কিন্তু যেহেতু এটি একটি টিউটোরিয়াল তাই প্রোগ্রামটি প্রথমবার বোঝার জন্য সহজ করার জন্য অপ্টিমাইজ করা হয়েছে, পারফরম্যান্স বা রক্ষণাবেক্ষণের সহজতার পরিবর্তে। + +```tsx +import { useState, ChangeEventHandler } from 'react' +import { useNetwork, + useReadContract, + usePrepareContractWrite, + useContractWrite, + useContractEvent + } from 'wagmi' +``` + +আমরা এই লাইব্রেরি ফাংশনগুলি ব্যবহার করি। আবারও, সেগুলি যেখানে ব্যবহার করা হয় সেখানে নিচে ব্যাখ্যা করা হয়েছে। + +```tsx +import { AddressType } from 'abitype' +``` + +[`abitype` লাইব্রেরি](https://abitype.dev/) আমাদের বিভিন্ন ইথেরিয়াম ডেটা প্রকারের জন্য টাইপস্ক্রিপ্ট সংজ্ঞা প্রদান করে, যেমন [`AddressType`](https://abitype.dev/config#addresstype)। + +```tsx +let greeterABI = [ + . + . + . +] as const // greeterABI +``` + +`Greeter` কন্ট্র্যাক্টের জন্য ABI। +আপনি যদি একই সময়ে কন্ট্র্যাক্ট এবং UI ডেভেলপ করেন তবে আপনি সাধারণত সেগুলিকে একই রিপোজিটরিতে রাখবেন এবং আপনার অ্যাপ্লিকেশনে একটি ফাইল হিসাবে সলিডিটি কম্পাইলার দ্বারা উত্পন্ন ABI ব্যবহার করবেন। তবে, এখানে এটি প্রয়োজনীয় নয় কারণ কন্ট্র্যাক্টটি ইতিমধ্যে তৈরি করা হয়েছে এবং এটি পরিবর্তন হবে না। + +```tsx +type AddressPerBlockchainType = { + [key: number]: AddressType +} +``` + +টাইপস্ক্রিপ্ট দৃঢ়ভাবে টাইপ করা হয়। আমরা এই সংজ্ঞাটি ব্যবহার করি সেই ঠিকানাটি নির্দিষ্ট করতে যেখানে `Greeter` কন্ট্র্যাক্টটি বিভিন্ন চেইনে ডিপ্লয় করা হয়েছে। কী একটি সংখ্যা (chainId), এবং মানটি হল একটি `AddressType` (একটি ঠিকানা)। + +```tsx +const contractAddrs: AddressPerBlockchainType = { + // Holesky + 17000: '0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8', + + // Sepolia + 11155111: '0x7143d5c190F048C8d19fe325b748b081903E3BF0' +} +``` + +দুটি সমর্থিত নেটওয়ার্কে কন্ট্র্যাক্টের ঠিকানা: [Holesky](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=contact_code) এবং [Sepolia](https://eth-sepolia.blockscout.com/address/0x7143d5c190F048C8d19fe325b748b081903E3BF0?tab=contact_code)। + +দ্রষ্টব্য: আসলে একটি তৃতীয় সংজ্ঞা রয়েছে, রেডস্টোন হোলস্কির জন্য, এটি নীচে ব্যাখ্যা করা হবে। + +```tsx +type ShowObjectAttrsType = { + name: string, + object: any +} +``` + +এই প্রকারটি `ShowObject` কম্পোনেন্টের একটি প্যারামিটার হিসাবে ব্যবহৃত হয় (পরে ব্যাখ্যা করা হয়েছে)। এটিতে অবজেক্টের নাম এবং তার মান অন্তর্ভুক্ত রয়েছে, যা ডিবাগিংয়ের উদ্দেশ্যে প্রদর্শিত হয়। + +```tsx +type ShowGreetingAttrsType = { + greeting: string | undefined +} +``` + +যেকোন মুহূর্তে আমরা হয় জানতে পারি অভিবাদনটি কী (কারণ আমরা এটি ব্লকচেইন থেকে পড়েছি) অথবা নাও জানতে পারি (কারণ আমরা এখনও এটি পাইনি)। তাই এমন একটি প্রকার থাকা দরকারী যা একটি স্ট্রিং বা কিছুই হতে পারে। + +##### `Greeter` কম্পোনেন্ট {#greeter-component} + +```tsx +const Greeter = () => { +``` + +অবশেষে, আমরা কম্পোনেন্টটি সংজ্ঞায়িত করতে পারি। + +```tsx + const { chain } = useNetwork() +``` + +আমরা যে চেইনটি ব্যবহার করছি সে সম্পর্কে তথ্য, সৌজন্যে [wagmi](https://wagmi.sh/react/hooks/useNetwork)। +যেহেতু এটি একটি হুক (`use...`), তাই প্রতিবার এই তথ্য পরিবর্তন হলে কম্পোনেন্টটি পুনরায় আঁকা হয়। + +```tsx + const greeterAddr = chain && contractAddrs[chain.id] +``` + +Greeter কন্ট্র্যাক্টের ঠিকানা, যা চেইন অনুযায়ী পরিবর্তিত হয় (এবং যা `undefined` হয় যদি আমাদের কাছে চেইন তথ্য না থাকে বা আমরা এমন একটি চেইনে থাকি যেখানে সেই কন্ট্র্যাক্টটি নেই)। + +```tsx + const readResults = useReadContract({ + address: greeterAddr, + abi: greeterABI, + functionName: "greet" , // No arguments + watch: true + }) +``` + +[`useReadContract` হুক](https://wagmi.sh/react/api/hooks/useReadContract) একটি কন্ট্র্যাক্ট থেকে তথ্য পড়ে। UI-তে `readResults` প্রসারিত করে আপনি দেখতে পারেন এটি ঠিক কী তথ্য ফেরত দেয়। এই ক্ষেত্রে আমরা চাই এটি দেখতে থাকুক যাতে অভিবাদন পরিবর্তন হলে আমরা অবহিত হই। + +**দ্রষ্টব্য:** আমরা [`setGreeting` ইভেন্টগুলি](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=logs) শুনতে পারি জানতে যে কখন অভিবাদন পরিবর্তন হয় এবং সেইভাবে আপডেট করতে। তবে, যদিও এটি আরও কার্যকর হতে পারে, এটি সব ক্ষেত্রে প্রযোজ্য হবে না। যখন ব্যবহারকারী একটি ভিন্ন চেইনে পরিবর্তন করে তখন অভিবাদনও পরিবর্তন হয়, কিন্তু সেই পরিবর্তনের সাথে কোনো ইভেন্ট থাকে না। আমরা কোডের একটি অংশ ইভেন্টগুলির জন্য শুনতে পারতাম এবং অন্যটি চেইন পরিবর্তনগুলি সনাক্ত করতে পারতাম, কিন্তু এটি কেবল [`watch` প্যারামিটার](https://wagmi.sh/react/api/hooks/useReadContract#watch-optional) সেট করার চেয়ে আরও জটিল হবে। + +```tsx + const [ newGreeting, setNewGreeting ] = useState("") +``` + +রিয়্যাক্টের [`useState` হুক](https://www.w3schools.com/react/react_usestate.asp) আমাদের একটি স্টেট ভেরিয়েবল নির্দিষ্ট করতে দেয়, যার মান কম্পোনেন্টের এক রেন্ডারিং থেকে অন্যটিতে স্থায়ী হয়। প্রাথমিক মান হল প্যারামিটার, এই ক্ষেত্রে খালি স্ট্রিং। + +`useState` হুক দুটি মান সহ একটি তালিকা ফেরত দেয়: + +1. স্টেট ভেরিয়েবলের বর্তমান মান। +2. প্রয়োজন হলে স্টেট ভেরিয়েবল পরিবর্তন করার জন্য একটি ফাংশন। যেহেতু এটি একটি হুক, তাই প্রতিবার এটি কল করা হলে কম্পোনেন্টটি আবার রেন্ডার করা হয়। + +এই ক্ষেত্রে, আমরা একটি স্টেট ভেরিয়েবল ব্যবহার করছি নতুন অভিবাদনের জন্য যা ব্যবহারকারী সেট করতে চায়। + +```tsx + const greetingChange : ChangeEventHandler = (evt) => + setNewGreeting(evt.target.value) +``` + +এটি নতুন অভিবাদন ইনপুট ফিল্ড পরিবর্তন হলে তার জন্য ইভেন্ট হ্যান্ডলার। প্রকারটি, [`ChangeEventHandler`](https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/forms_and_events/), নির্দিষ্ট করে যে এটি একটি HTML ইনপুট উপাদানের মান পরিবর্তনের জন্য একটি হ্যান্ডলার। `` অংশটি ব্যবহৃত হয় কারণ এটি একটি [জেনেরিক টাইপ](https://www.w3schools.com/typescript/typescript_basic_generics.php)। + +```tsx + const preparedTx = usePrepareContractWrite({ + address: greeterAddr, + abi: greeterABI, + functionName: 'setGreeting', + args: [ newGreeting ] + }) + const workingTx = useContractWrite(preparedTx.config) +``` + +এটি ক্লায়েন্টের দৃষ্টিকোণ থেকে একটি ব্লকচেইন লেনদেন জমা দেওয়ার প্রক্রিয়া: + +1. ব্লকচেইনের একটি নোডে [`eth_estimateGas`](https://docs.alchemy.com/reference/eth-estimategas) ব্যবহার করে লেনদেনটি পাঠান। +2. নোড থেকে একটি প্রতিক্রিয়ার জন্য অপেক্ষা করুন। +3. প্রতিক্রিয়াটি পাওয়ার পর, ব্যবহারকারীকে ওয়ালেটের মাধ্যমে লেনদেনটি সাইন করতে বলুন। এই ধাপটি _অবশ্যই_ নোডের প্রতিক্রিয়া পাওয়ার পরে ঘটতে হবে কারণ ব্যবহারকারীকে লেনদেন সাইন করার আগে লেনদেনের গ্যাস খরচ দেখানো হয়। +4. ব্যবহারকারীর অনুমোদনের জন্য অপেক্ষা করুন। +5. লেনদেনটি আবার পাঠান, এবার [`eth_sendRawTransaction`](https://docs.alchemy.com/reference/eth-sendrawtransaction) ব্যবহার করে। + +ধাপ ২ সম্ভবত একটি লক্ষণীয় পরিমাণ সময় নিতে পারে, যে সময়ে ব্যবহারকারীরা ভাববেন যে তাদের কমান্ডটি সত্যিই ইউজার ইন্টারফেস দ্বারা গৃহীত হয়েছে কিনা এবং কেন তাদের ইতিমধ্যে লেনদেন সাইন করতে বলা হচ্ছে না। এটি একটি খারাপ ব্যবহারকারী অভিজ্ঞতা (UX) তৈরি করে। + +সমাধান হল [প্রিপেয়ার হুক](https://wagmi.sh/react/prepare-hooks) ব্যবহার করা। প্রতিবার যখন একটি প্যারামিটার পরিবর্তন হয়, অবিলম্বে নোডকে `eth_estimateGas` অনুরোধ পাঠান। তারপর, যখন ব্যবহারকারী আসলে লেনদেনটি পাঠাতে চায় (এই ক্ষেত্রে **আপডেট গ্রিটিং** টিপে), গ্যাসের খরচ জানা থাকে এবং ব্যবহারকারী অবিলম্বে ওয়ালেট পৃষ্ঠাটি দেখতে পারে। + +```tsx + return ( +``` + +এখন আমরা অবশেষে ফেরত দেওয়ার জন্য আসল HTML তৈরি করতে পারি। + +```tsx + <> +

Greeter

+ { + !readResults.isError && !readResults.isLoading && + + } +
+``` + +একটি `ShowGreeting` কম্পোনেন্ট তৈরি করুন (নিচে ব্যাখ্যা করা হয়েছে), কিন্তু শুধুমাত্র যদি ব্লকচেইন থেকে অভিবাদনটি সফলভাবে পড়া হয়। + +```tsx + +``` + +এটি ইনপুট টেক্সট ফিল্ড যেখানে ব্যবহারকারী একটি নতুন অভিবাদন সেট করতে পারে। প্রতিবার ব্যবহারকারী একটি কী চাপলে, আমরা `greetingChange` কল করি যা `setNewGreeting` কল করে। `setNewGreeting` `useState` হুক থেকে আসার কারণে, এটি `Greeter` কম্পোনেন্টকে আবার রেন্ডার করায়। এর মানে হল: + +- আমাদের নতুন অভিবাদনের মান রাখতে `value` নির্দিষ্ট করতে হবে, কারণ অন্যথায় এটি ডিফল্ট, খালি স্ট্রিং-এ ফিরে যাবে। +- `usePrepareContractWrite` প্রতিবার `newGreeting` পরিবর্তনের সময় কল করা হয়, যার মানে এটি সর্বদা প্রস্তুত লেনদেনে সর্বশেষ `newGreeting` থাকবে। + +```tsx + +``` + +যদি `workingTx.write` না থাকে তবে আমরা এখনও অভিবাদন আপডেট পাঠানোর জন্য প্রয়োজনীয় তথ্যের জন্য অপেক্ষা করছি, তাই বোতামটি নিষ্ক্রিয় থাকে। যদি একটি `workingTx.write` মান থাকে তবে সেটি হল লেনদেন পাঠানোর জন্য কল করার ফাংশন। + +```tsx +
+ + + + + ) +} +``` + +অবশেষে, আমরা কী করছি তা দেখতে আপনাকে সাহায্য করার জন্য, আমরা যে তিনটি বস্তু ব্যবহার করি তা দেখান: + +- `readResults` +- `preparedTx` +- `workingTx` + +##### `ShowGreeting` কম্পোনেন্ট {#showgreeting-component} + +এই কম্পোনেন্টটি দেখায় + +```tsx +const ShowGreeting = (attrs : ShowGreetingAttrsType) => { +``` + +একটি কম্পোনেন্ট ফাংশন কম্পোনেন্টের সমস্ত অ্যাট্রিবিউট সহ একটি প্যারামিটার গ্রহণ করে। + +```tsx + return {attrs.greeting} +} +``` + +##### `ShowObject` কম্পোনেন্ট {#showobject-component} + +তথ্যের উদ্দেশ্যে, আমরা `ShowObject` কম্পোনেন্ট ব্যবহার করি গুরুত্বপূর্ণ বস্তুগুলি দেখানোর জন্য (`readResults` অভিবাদন পড়ার জন্য এবং `preparedTx` এবং `workingTx` আমাদের তৈরি করা লেনদেনের জন্য)। + +```tsx +const ShowObject = (attrs: ShowObjectAttrsType ) => { + const keys = Object.keys(attrs.object) + const funs = keys.filter(k => typeof attrs.object[k] == "function") + return <> +
+``` + +আমরা UI-কে সমস্ত তথ্য দিয়ে বিশৃঙ্খল করতে চাই না, তাই সেগুলি দেখতে বা বন্ধ করা সম্ভব করার জন্য, আমরা একটি [`details`](https://www.w3schools.com/tags/tag_details.asp) ট্যাগ ব্যবহার করি। + +```tsx + {attrs.name} +
+        {JSON.stringify(attrs.object, null, 2)}
+```
+
+বেশিরভাগ ফিল্ড [`JSON.stringify`](https://www.w3schools.com/js/js_json_stringify.asp) ব্যবহার করে প্রদর্শিত হয়।
+
+```tsx
+      
+ { funs.length > 0 && + <> + ফাংশন: +
    +``` + +ব্যতিক্রম হল ফাংশন, যা [JSON স্ট্যান্ডার্ডের](https://www.json.org/json-en.html) অংশ নয়, তাই সেগুলিকে আলাদাভাবে প্রদর্শন করতে হবে। + +```tsx + {funs.map((f, i) => +``` + +JSX-এর মধ্যে, `{` কার্লি ব্র্যাকেট `}` এর ভিতরের কোড জাভাস্ক্রিপ্ট হিসাবে ব্যাখ্যা করা হয়। তারপর, `(` রেগুলার ব্র্যাকেট `)` এর ভিতরের কোডটি আবার JSX হিসাবে ব্যাখ্যা করা হয়। + +```tsx + (
  • {f}
  • ) + )} +``` + +রিয়্যাক্টের [DOM ট্রি](https://www.w3schools.com/js/js_htmldom.asp)-তে ট্যাগগুলির জন্য স্বতন্ত্র শনাক্তকারী প্রয়োজন। এর মানে হল একই ট্যাগের শিশুদের (এই ক্ষেত্রে, [আনঅর্ডারড তালিকা](https://www.w3schools.com/tags/tag_ul.asp)) জন্য ভিন্ন `key` অ্যাট্রিবিউট প্রয়োজন। + +```tsx +
+ + } +
+ +} +``` + +বিভিন্ন HTML ট্যাগ শেষ করুন। + +##### চূড়ান্ত `export` {#the-final-export} + +```tsx +export { Greeter } +``` + +`Greeter` কম্পোনেন্টটি হল সেইটি যা আমাদের অ্যাপ্লিকেশনের জন্য এক্সপোর্ট করতে হবে। + +#### `src/wagmi.ts` {#wagmi-ts} + +অবশেষে, WAGMI সম্পর্কিত বিভিন্ন সংজ্ঞা `src/wagmi.ts`-এ রয়েছে। আমি এখানে সবকিছু ব্যাখ্যা করতে যাচ্ছি না, কারণ এর বেশিরভাগই বয়লারপ্লেট যা আপনার পরিবর্তন করার প্রয়োজন হওয়ার সম্ভাবনা কম। + +এখানকার কোডটি [গিটহাবে](https://github.com/qbzzt/20230801-modern-ui/blob/main/src/wagmi.ts) থাকা কোডের মতো ঠিক একই নয় কারণ পরে নিবন্ধে আমরা আরেকটি চেইন ([Redstone Holesky](https://redstone.xyz/docs/network-info)) যোগ করি। + +```ts +import { getDefaultWallets } from '@rainbow-me/rainbowkit' +import { configureChains, createConfig } from 'wagmi' +import { holesky, sepolia } from 'wagmi/chains' +``` + +অ্যাপ্লিকেশনটি সমর্থন করে এমন ব্লকচেইনগুলি ইম্পোর্ট করুন। আপনি সমর্থিত চেইনের তালিকা [viem গিটহাবে](https://github.com/wagmi-dev/viem/tree/main/src/chains/definitions) দেখতে পারেন। + +```ts +import { publicProvider } from 'wagmi/providers/public' + +const walletConnectProjectId = 'c96e690bb92b6311e8e9b2a6a22df575' +``` + +[WalletConnect](https://walletconnect.com/) ব্যবহার করতে সক্ষম হতে আপনার অ্যাপ্লিকেশনের জন্য একটি প্রজেক্ট আইডি প্রয়োজন। আপনি এটি [cloud.walletconnect.com](https://cloud.walletconnect.com/sign-in) থেকে পেতে পারেন। + +```ts +const { chains, publicClient, webSocketPublicClient } = configureChains( + [ holesky, sepolia ], + [ + publicProvider(), + ], +) + +const { connectors } = getDefaultWallets({ + appName: 'My wagmi + RainbowKit App', + chains, + projectId: walletConnectProjectId, +}) + +export const config = createConfig({ + autoConnect: true, + connectors, + publicClient, + webSocketPublicClient, +}) + +export { chains } +``` + +### আরেকটি ব্লকচেইন যোগ করা {#add-blockchain} + +আজকাল অনেক [L2 স্কেলিং সমাধান](/layer-2/) রয়েছে, এবং আপনি হয়তো এমন কিছু সমর্থন করতে চাইতে পারেন যা viem এখনও সমর্থন করে না। এটি করার জন্য, আপনি `src/wagmi.ts` পরিবর্তন করুন। এই নির্দেশাবলী ব্যাখ্যা করে কিভাবে [Redstone Holesky](https://redstone.xyz/docs/network-info) যোগ করতে হয়। + +1. viem থেকে `defineChain` টাইপ ইম্পোর্ট করুন। + + ```ts + import { defineChain } from 'viem' + ``` + +2. নেটওয়ার্ক সংজ্ঞা যোগ করুন। + + ```ts + const redstoneHolesky = defineChain({ + id: 17_001, + name: 'Redstone Holesky', + network: 'redstone-holesky', + nativeCurrency: { + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }, + rpcUrls: { + default: { + http: ['https://rpc.holesky.redstone.xyz'], + webSocket: ['wss://rpc.holesky.redstone.xyz/ws'], + }, + public: { + http: ['https://rpc.holesky.redstone.xyz'], + webSocket: ['wss://rpc.holesky.redstone.xyz/ws'], + }, + }, + blockExplorers: { + default: { name: 'Explorer', url: 'https://explorer.holesky.redstone.xyz' }, + }, + }) + ``` + +3. `configureChains` কলে নতুন চেইন যোগ করুন। + + ```ts + const { chains, publicClient, webSocketPublicClient } = configureChains( + [ holesky, sepolia, redstoneHolesky ], + [ publicProvider(), ], + ) + ``` + +4. নিশ্চিত করুন যে অ্যাপ্লিকেশনটি নতুন নেটওয়ার্কে আপনার কন্ট্র্যাক্টের জন্য ঠিকানাটি জানে। এই ক্ষেত্রে, আমরা `src/components/Greeter.tsx` পরিবর্তন করি: + + ```ts + const contractAddrs : AddressPerBlockchainType = { + // Holesky + 17000: '0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8', + + // Redstone Holesky + 17001: '0x4919517f82a1B89a32392E1BF72ec827ba9986D3', + + // Sepolia + 11155111: '0x7143d5c190F048C8d19fe325b748b081903E3BF0' + } + ``` + +## উপসংহার {#conclusion} + +অবশ্যই, আপনি `Greeter`-এর জন্য একটি ইউজার ইন্টারফেস সরবরাহ করার বিষয়ে সত্যিই চিন্তা করেন না। আপনি আপনার নিজের কন্ট্র্যাক্টের জন্য একটি ইউজার ইন্টারফেস তৈরি করতে চান। আপনার নিজের অ্যাপ্লিকেশন তৈরি করতে, এই ধাপগুলি চালান: + +1. একটি wagmi অ্যাপ্লিকেশন তৈরি করতে নির্দিষ্ট করুন। + + ```sh copy + pnpm create wagmi + ``` + +2. অ্যাপ্লিকেশনটির নাম দিন। + +3. **React** ফ্রেমওয়ার্ক নির্বাচন করুন। + +4. **Vite** ভেরিয়েন্ট নির্বাচন করুন। + +5. আপনি [Rainbow kit যোগ করতে পারেন](https://www.rainbowkit.com/docs/installation#manual-setup)। + +এখন যান এবং আপনার কন্ট্র্যাক্টগুলি বৃহত্তর বিশ্বের জন্য ব্যবহারযোগ্য করে তুলুন। + +[আমার আরও কাজের জন্য এখানে দেখুন](https://cryptodocguy.pro/)। + diff --git a/public/content/translations/bn/developers/tutorials/deploying-your-first-smart-contract/index.md b/public/content/translations/bn/developers/tutorials/deploying-your-first-smart-contract/index.md new file mode 100644 index 00000000000..626899db906 --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/deploying-your-first-smart-contract/index.md @@ -0,0 +1,101 @@ +--- +title: "আপনার প্রথম স্মার্ট কন্ট্র্যাক্ট ডিপ্লয় করা" +description: "একটি ইথেরিয়াম টেস্ট নেটওয়ার্কে আপনার প্রথম স্মার্ট কন্ট্র্যাক্ট ডিপ্লয় করার একটি ভূমিকা" +author: "jdourlens" +tags: + [ + "স্মার্ট কন্ট্র্যাক্ট", + "remix", + "সলিডিটি", + "ডেপ্লয়িং" + ] +skill: beginner +lang: bn +published: 2020-04-03 +source: EthereumDev +sourceUrl: https://ethereumdev.io/deploying-your-first-smart-contract/ +address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" +--- + +আমি অনুমান করছি যে আপনিও আমাদের মতোই ইথেরিয়াম ব্লকচেইনে আপনার প্রথম [স্মার্ট কন্ট্র্যাক্ট](/developers/docs/smart-contracts/) [ডিপ্লয়](/developers/docs/smart-contracts/deploying/) করতে এবং তার সাথে ইন্টারঅ্যাক্ট করতে উত্তেজিত। + +চিন্তা করবেন না, যেহেতু এটি আমাদের প্রথম স্মার্ট কন্ট্র্যাক্ট, আমরা এটিকে একটি [লোকাল টেস্ট নেটওয়ার্কে](/developers/docs/networks/) ডিপ্লয় করব যাতে এটি ডিপ্লয় করতে এবং আপনার ইচ্ছামত এটি নিয়ে খেলা করতে আপনার কোনো খরচ না হয়। + +## আমাদের কন্ট্র্যাক্ট লেখা {#writing-our-contract} + +প্রথম ধাপ হল [Remix ভিজিট করা](https://remix.ethereum.org/) এবং একটি নতুন ফাইল তৈরি করা। Remix ইন্টারফেসের উপরের বাম অংশে একটি নতুন ফাইল যোগ করুন এবং আপনার পছন্দের ফাইলের নাম লিখুন। + +![Remix ইন্টারফেসে একটি নতুন ফাইল যোগ করা হচ্ছে](./remix.png) + +নতুন ফাইলে, আমরা নিম্নলিখিত কোডটি পেস্ট করব। + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.17; + +contract Counter { + + // গণনার সংখ্যা রাখার জন্য আনসাইন্ড int টাইপের পাবলিক ভেরিয়েবল + uint256 public count = 0; + + // ফাংশন যা আমাদের কাউন্টারকে বৃদ্ধি করে + function increment() public { + count += 1; + } + + // কাউন্টের মান পাওয়ার জন্য অপ্রয়োজনীয় গেটার + function getCount() public view returns (uint256) { + return count; + } + +} +``` + +আপনি যদি প্রোগ্রামিংয়ে অভ্যস্ত হন তবে আপনি সহজেই অনুমান করতে পারবেন এই প্রোগ্রামটি কী করে। এখানে লাইন বাই লাইন একটি ব্যাখ্যা দেওয়া হল: + +- লাইন 4: আমরা `Counter` নামে একটি কন্ট্র্যাক্ট সংজ্ঞায়িত করি। +- লাইন 7: আমাদের কন্ট্র্যাক্ট 0 থেকে শুরু করে `count` নামের একটি আনসাইন্ড ইন্টিজার সংরক্ষণ করে। +- লাইন 10: প্রথম ফাংশনটি কন্ট্র্যাক্টের স্টেট পরিবর্তন করবে এবং আমাদের `count` ভেরিয়েবলকে `increment()` করবে। +- লাইন 15: দ্বিতীয় ফাংশনটি শুধুমাত্র একটি গেটার যা স্মার্ট কন্ট্র্যাক্টের বাইরে থেকে `count` ভেরিয়েবলের মান পড়তে সক্ষম। মনে রাখবেন, যেহেতু আমরা আমাদের `count` ভেরিয়েবলকে পাবলিক হিসেবে সংজ্ঞায়িত করেছি, এটি প্রয়োজনীয় নয় কিন্তু একটি উদাহরণ হিসেবে দেখানো হয়েছে। + +আমাদের প্রথম সহজ স্মার্ট কন্ট্র্যাক্টের জন্য এইটুকুই। আপনি হয়তো জানেন, এটি জাভা বা C++ এর মতো OOP (অবজেক্ট-ওরিয়েন্টেড প্রোগ্রামিং) ভাষাগুলির একটি ক্লাসের মতো দেখায়। এখন আমাদের কন্ট্র্যাক্ট নিয়ে খেলার সময়। + +## আমাদের কন্ট্র্যাক্ট ডিপ্লয় করা {#deploying-our-contract} + +যেহেতু আমরা আমাদের প্রথম স্মার্ট কন্ট্র্যাক্ট লিখেছি, এখন আমরা এটিকে ব্লকচেইনে ডিপ্লয় করব যাতে আমরা এটি নিয়ে খেলতে পারি। + +[ব্লকচেইনে স্মার্ট কন্ট্র্যাক্ট ডিপ্লয় করা](/developers/docs/smart-contracts/deploying/) আসলে কোনো প্রাপক নির্দিষ্ট না করে কম্পাইল করা স্মার্ট কন্ট্র্যাক্টের কোড ধারণকারী একটি লেনদেন পাঠানো মাত্র। + +আমরা প্রথমে বাম দিকের কম্পাইল আইকনে ক্লিক করে [কন্ট্র্যাক্টটি কম্পাইল করব](/developers/docs/smart-contracts/compiling/): + +![Remix টুলবারে কম্পাইল আইকন](./remix-compile-button.png) + +তারপর কম্পাইল বোতামে ক্লিক করুন: + +![Remix সলিডিটি কম্পাইলারে কম্পাইল বোতাম](./remix-compile.png) + +আপনি "Auto compile" বিকল্পটি নির্বাচন করতে পারেন যাতে আপনি যখন টেক্সট এডিটরে কন্টেন্ট সংরক্ষণ করবেন তখন কন্ট্র্যাক্টটি সর্বদা কম্পাইল করা হবে। + +তারপর "deploy and run transactions" স্ক্রিনে নেভিগেট করুন: + +![Remix টুলবারে ডিপ্লয় আইকন](./remix-deploy.png) + +একবার আপনি "deploy and run transactions" স্ক্রিনে এলে, আপনার কন্ট্র্যাক্টের নাম দেখা যাচ্ছে কিনা তা দুবার পরীক্ষা করুন এবং Deploy-এ ক্লিক করুন। যেমনটি আপনি পৃষ্ঠার শীর্ষে দেখতে পাচ্ছেন, বর্তমান এনভায়রনমেন্ট হল "JavaScript VM" যার মানে হল আমরা আমাদের স্মার্ট কন্ট্র্যাক্টটিকে একটি লোকাল টেস্ট ব্লকচেইনে ডিপ্লয় করব এবং তার সাথে ইন্টারঅ্যাক্ট করব যাতে দ্রুত এবং কোনো ফি ছাড়াই পরীক্ষা করা যায়। + +![Remix সলিডিটি কম্পাইলারে ডিপ্লয় বোতাম](./remix-deploy-button.png) + +একবার আপনি "Deploy" বোতামে ক্লিক করলে, আপনি দেখতে পাবেন আপনার কন্ট্র্যাক্টটি নীচে উপস্থিত হয়েছে। বাম দিকের তীরটিতে ক্লিক করে এটিকে প্রসারিত করুন যাতে আমরা আমাদের কন্ট্র্যাক্টের কন্টেন্ট দেখতে পারি। এটি হল আমাদের ভেরিয়েবল `counter`, আমাদের `increment()` ফাংশন এবং গেটার `getCounter()`। + +আপনি যদি `count` বা `getCount` বোতামে ক্লিক করেন, তবে এটি আসলে কন্ট্র্যাক্টের `count` ভেরিয়েবলের কন্টেন্ট পুনরুদ্ধার করবে এবং এটি প্রদর্শন করবে। যেহেতু আমরা এখনো `increment` ফাংশনটি কল করিনি, এটি 0 প্রদর্শন করবে। + +![Remix সলিডিটি কম্পাইলারে ফাংশন বোতাম](./remix-function-button.png) + +চলুন এখন বোতামে ক্লিক করে `increment` ফাংশনটিকে কল করি। আপনি দেখতে পাবেন যে লেনদেনগুলির লগ উইন্ডোর নীচে উপস্থিত হচ্ছে। আপনি দেখতে পাবেন যে `increment` বোতামের পরিবর্তে ডেটা পুনরুদ্ধারের জন্য বোতাম টিপলে লগগুলি আলাদা হয়। এর কারণ হল ব্লকচেইনে ডেটা পড়ার জন্য কোনো লেনদেন (লেখা) বা ফি প্রয়োজন হয় না। কারণ শুধুমাত্র ব্লকচেইনের স্টেট পরিবর্তন করার জন্য একটি লেনদেন করতে হয়: + +![লেনদেনের একটি লগ](./transaction-log.png) + +ইনক্রিমেন্ট বোতামটি চাপার পরে যা আমাদের `increment()` ফাংশনকে কল করার জন্য একটি লেনদেন তৈরি করবে, আমরা যদি কাউন্ট বা getCount বোতামগুলিতে আবার ক্লিক করি তাহলে আমরা আমাদের স্মার্ট কন্ট্র্যাক্টের নতুন আপডেট করা স্টেটটি পড়ব যেখানে কাউন্ট ভেরিয়েবলটি 0-এর চেয়ে বড় হবে। + +![স্মার্ট কন্ট্র্যাক্টের নতুন আপডেট করা স্টেট](./updated-state.png) + +পরবর্তী টিউটোরিয়ালে, আমরা কভার করব [কিভাবে আপনি আপনার স্মার্ট কন্ট্র্যাক্টে ইভেন্ট যোগ করতে পারেন](/developers/tutorials/logging-events-smart-contracts/)। লগিং ইভেন্টগুলি হল আপনার স্মার্ট কন্ট্র্যাক্ট ডিবাগ করার এবং একটি ফাংশন কল করার সময় কী ঘটছে তা বোঝার একটি সুবিধাজনক উপায়। diff --git a/public/content/translations/bn/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md b/public/content/translations/bn/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md new file mode 100644 index 00000000000..8f5e9e82f56 --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md @@ -0,0 +1,372 @@ +--- +title: "একটি স্থানীয়, মাল্টি-ক্লায়েন্ট টেস্টনেটে কীভাবে একটি dApp ডেভেলপ এবং পরীক্ষা করা যায়" +description: "এই গাইডটি আপনাকে প্রথমে একটি মাল্টি-ক্লায়েন্ট স্থানীয় Ethereum টেস্টনেটকে কীভাবে ইনস্ট্যানশিয়েট এবং কনফিগার করতে হয় তা দেখাবে এবং তারপরে dApp স্থাপন এবং পরীক্ষা করার জন্য টেস্টনেট ব্যবহার করবে।" +author: "Tedi Mitiku" +tags: + [ + "ক্লায়েন্ট", + "নোড", + "স্মার্ট কন্ট্র্যাক্ট", + "কম্পোজেবিলিটি", + "কনসেন্সাস লেয়ার", + "এক্সিকিউশন লেয়ার", + "পরীক্ষা" + ] +skill: intermediate +lang: bn +published: 2023-04-11 +--- + +## ভূমিকা {#introduction} + +এই গাইডটি আপনাকে একটি কনফিগারযোগ্য স্থানীয় Ethereum টেস্টনেট ইনস্ট্যানশিয়েট করার, এতে একটি স্মার্ট কন্ট্র্যাক্ট স্থাপন করার এবং আপনার dApp-এর বিরুদ্ধে পরীক্ষা চালানোর জন্য টেস্টনেট ব্যবহার করার প্রক্রিয়াটি দেখায়। এই গাইডটি dApp ডেভেলপারদের জন্য ডিজাইন করা হয়েছে যারা একটি লাইভ টেস্টনেট বা মেইননেটে স্থাপন করার আগে বিভিন্ন নেটওয়ার্ক কনফিগারেশনের বিরুদ্ধে স্থানীয়ভাবে তাদের dApps ডেভেলপ এবং পরীক্ষা করতে চান। + +এই গাইডে, আপনি যা যা করবেন: + +- [Kurtosis](https://www.kurtosis.com/) ব্যবহার করে [`eth-network-package`](https://github.com/kurtosis-tech/eth-network-package)-এর সাথে একটি স্থানীয় Ethereum টেস্টনেট ইনস্ট্যানশিয়েট করুন, +- একটি dApp কম্পাইল, স্থাপন এবং পরীক্ষা করার জন্য আপনার Hardhat dApp ডেভেলপমেন্ট পরিবেশকে স্থানীয় টেস্টনেটের সাথে সংযুক্ত করুন, এবং +- বিভিন্ন নেটওয়ার্ক কনফিগারেশনের বিরুদ্ধে ডেভেলপমেন্ট এবং পরীক্ষার কর্মপ্রবাহ সক্রিয় করতে, স্থানীয় টেস্টনেট কনফিগার করুন, যার মধ্যে নোডের সংখ্যা এবং নির্দিষ্ট EL/CL ক্লায়েন্ট পেয়ারিং-এর মতো প্যারামিটার অন্তর্ভুক্ত রয়েছে। + +### Kurtosis কী? {#what-is-kurtosis} + +[Kurtosis](https://www.kurtosis.com/) একটি কম্পোজেবল বিল্ড সিস্টেম যা মাল্টি-কন্টেইনার পরীক্ষার পরিবেশ কনফিগার করার জন্য ডিজাইন করা হয়েছে। এটি বিশেষভাবে ডেভেলপারদের পুনরুৎপাদনযোগ্য পরিবেশ তৈরি করতে সক্ষম করে যার জন্য ডাইনামিক সেটআপ লজিক প্রয়োজন, যেমন ব্লকচেইন টেস্টনেট। + +এই গাইডে, Kurtosis eth-network-package [`geth`](https://geth.ethereum.org/) এক্সিকিউশন লেয়ার (EL) ক্লায়েন্ট, সেইসাথে [`teku`](https://consensys.io/teku), [`lighthouse`](https://lighthouse.sigmaprime.io/), এবং [`lodestar`](https://lodestar.chainsafe.io/) কনসেন্সাস লেয়ার (CL) ক্লায়েন্টগুলির জন্য সমর্থন সহ একটি স্থানীয় Ethereum টেস্টনেট চালু করে। এই প্যাকেজটি Hardhat Network, Ganache, এবং Anvil-এর মতো ফ্রেমওয়ার্কের নেটওয়ার্কগুলির জন্য একটি কনফিগারযোগ্য এবং কম্পোজেবল বিকল্প হিসেবে কাজ করে। Kurtosis ডেভেলপারদের ব্যবহৃত টেস্টনেটের উপর অধিক নিয়ন্ত্রণ এবং নমনীয়তা প্রদান করে, যা [Ethereum Foundation-এর the Merge পরীক্ষা করার জন্য Kurtosis ব্যবহার করার](https://www.kurtosis.com/blog/testing-the-ethereum-merge) একটি প্রধান কারণ এবং নেটওয়ার্ক আপগ্রেড পরীক্ষার জন্য এটি ব্যবহার করা অব্যাহত রয়েছে। + +## Kurtosis সেট আপ করা {#setting-up-kurtosis} + +এগিয়ে যাওয়ার আগে, নিশ্চিত করুন আপনার কাছে আছে: + +- আপনার স্থানীয় মেশিনে [Docker ইঞ্জিন ইনস্টল এবং শুরু করেছেন](https://docs.kurtosis.com/install/#i-install--start-docker) +- [Kurtosis CLI ইনস্টল করেছেন](https://docs.kurtosis.com/install#ii-install-the-cli) (অথবা যদি আপনার কাছে CLI আগে থেকেই ইনস্টল করা থাকে তবে এটিকে সর্বশেষ রিলিজে আপগ্রেড করেছেন) +- [Node.js](https://nodejs.org/en), [yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable), এবং [npx](https://www.npmjs.com/package/npx) ইনস্টল করেছেন (আপনার dApp পরিবেশের জন্য) + +## একটি স্থানীয় Ethereum টেস্টনেট ইনস্ট্যানশিয়েট করা {#instantiate-testnet} + +একটি স্থানীয় Ethereum টেস্টনেট চালু করতে, রান করুন: + +```python +kurtosis --enclave local-eth-testnet run github.com/kurtosis-tech/eth-network-package +``` + +দ্রষ্টব্য: এই কমান্ডটি `--enclave` ফ্ল্যাগ ব্যবহার করে আপনার নেটওয়ার্কের নাম দেয়: "local-eth-testnet"। + +Kurtosis নির্দেশাবলী ব্যাখ্যা, যাচাই এবং তারপর কার্যকর করার জন্য কাজ করার সময় যে পদক্ষেপগুলি নিচ্ছে তা প্রিন্ট করবে। শেষে, আপনার নিম্নলিখিতটির মতো একটি আউটপুট দেখা উচিত: + +```python +INFO[2023-04-04T18:09:44-04:00] ====================================================== +INFO[2023-04-04T18:09:44-04:00] || Created enclave: local-eth-testnet || +INFO[2023-04-04T18:09:44-04:00] ====================================================== +Name: local-eth-testnet +UUID: 39372d756ae8 +Status: RUNNING +Creation Time: Tue, 04 Apr 2023 18:09:03 EDT + +========================================= Files Artifacts ========================================= +UUID Name +d4085a064230 cl-genesis-data +1c62cb792e4c el-genesis-data +bd60489b73a7 genesis-generation-config-cl +b2e593fe5228 genesis-generation-config-el +d552a54acf78 geth-prefunded-keys +5f7e661eb838 prysm-password +054e7338bb59 validator-keystore-0 + +========================================== User Services ========================================== +UUID Name Ports Status +e20f129ee0c5 cl-client-0-beacon http: 4000/tcp -> RUNNING + metrics: 5054/tcp -> + tcp-discovery: 9000/tcp -> 127.0.0.1:54263 + udp-discovery: 9000/udp -> 127.0.0.1:60470 +a8b6c926cdb4 cl-client-0-validator http: 5042/tcp -> 127.0.0.1:54267 RUNNING + metrics: 5064/tcp -> +d7b802f623e8 el-client-0 engine-rpc: 8551/tcp -> 127.0.0.1:54253 RUNNING + rpc: 8545/tcp -> 127.0.0.1:54251 + tcp-discovery: 30303/tcp -> 127.0.0.1:54254 + udp-discovery: 30303/udp -> 127.0.0.1:53834 + ws: 8546/tcp -> 127.0.0.1:54252 +514a829c0a84 prelaunch-data-generator-1680646157905431468 STOPPED +62bd62d0aa7a prelaunch-data-generator-1680646157915424301 STOPPED +05e9619e0e90 prelaunch-data-generator-1680646157922872635 STOPPED + +``` + +অভিনন্দন! আপনি Docker-এর উপর একটি CL (`lighthouse`) এবং EL ক্লায়েন্ট (`geth`) সহ একটি স্থানীয় Ethereum টেস্টনেট ইনস্ট্যানশিয়েট করতে Kurtosis ব্যবহার করেছেন। + +### পর্যালোচনা {#review-instantiate-testnet} + +এই বিভাগে, আপনি একটি কমান্ড কার্যকর করেছেন যা Kurtosis-কে একটি Kurtosis [Enclave](https://docs.kurtosis.com/advanced-concepts/enclaves/)-এর মধ্যে একটি স্থানীয় Ethereum টেস্টনেট চালু করতে GitHub-এ দূরবর্তীভাবে হোস্ট করা [`eth-network-package`](https://github.com/kurtosis-tech/eth-network-package) ব্যবহার করতে নির্দেশ দিয়েছে। আপনার এনক্লেভের ভিতরে, আপনি "ফাইল আর্টিফ্যাক্ট" এবং "ব্যবহারকারী পরিষেবা" উভয়ই পাবেন। + +আপনার এনক্লেভে থাকা [ফাইল আর্টিফ্যাক্ট](https://docs.kurtosis.com/advanced-concepts/files-artifacts/) EL এবং CL ক্লায়েন্টদের বুটস্ট্র্যাপ করার জন্য তৈরি এবং ব্যবহৃত সমস্ত ডেটা অন্তর্ভুক্ত করে। এই [ডকার ইমেজ](https://github.com/ethpandaops/ethereum-genesis-generator) থেকে তৈরি `prelaunch-data-generator` পরিষেবা ব্যবহার করে ডেটা তৈরি করা হয়েছিল + +ব্যবহারকারী পরিষেবাগুলি আপনার এনক্লেভে পরিচালিত সমস্ত কন্টেইনারাইজড পরিষেবা প্রদর্শন করে। আপনি লক্ষ্য করবেন যে একটি একক নোড, যেখানে একটি EL ক্লায়েন্ট এবং একটি CL ক্লায়েন্ট উভয়ই রয়েছে, তৈরি করা হয়েছে। + +## আপনার dApp ডেভেলপমেন্ট পরিবেশকে স্থানীয় Ethereum টেস্টনেটের সাথে সংযুক্ত করুন {#connect-your-dapp} + +### dApp ডেভেলপমেন্ট পরিবেশ সেটআপ করুন {#set-up-dapp-env} + +এখন যেহেতু আপনার একটি চলমান স্থানীয় টেস্টনেট আছে, আপনি আপনার dApp ডেভেলপমেন্ট পরিবেশকে আপনার স্থানীয় টেস্টনেট ব্যবহার করার জন্য সংযুক্ত করতে পারেন। এই গাইডে আপনার স্থানীয় টেস্টনেটে একটি ব্ল্যাকজ্যাক dApp স্থাপন করতে Hardhat ফ্রেমওয়ার্ক ব্যবহার করা হবে। + +আপনার dApp ডেভেলপমেন্ট পরিবেশ সেট আপ করতে, আমাদের স্যাম্পল dApp ধারণকারী রিপোজিটরিটি ক্লোন করুন এবং এর নির্ভরতাগুলি ইনস্টল করুন, রান করুন: + +```python +git clone https://github.com/kurtosis-tech/awesome-kurtosis.git && cd awesome-kurtosis/smart-contract-example && yarn +``` + +এখানে ব্যবহৃত [smart-contract-example](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example) ফোল্ডারে [Hardhat](https://hardhat.org/) ফ্রেমওয়ার্ক ব্যবহারকারী একজন dApp ডেভেলপারের জন্য সাধারণ সেটআপ রয়েছে: + +- [`contracts/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/contracts)-এ একটি Blackjack dApp-এর জন্য কয়েকটি সহজ স্মার্ট কন্ট্র্যাক্ট রয়েছে +- [`scripts/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/scripts)-এ আপনার স্থানীয় Ethereum নেটওয়ার্কে একটি টোকেন কন্ট্র্যাক্ট স্থাপন করার জন্য একটি স্ক্রিপ্ট রয়েছে +- [`test/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/test)-এ আপনার টোকেন কন্ট্র্যাক্টের জন্য একটি সহজ .js পরীক্ষা রয়েছে যা নিশ্চিত করে যে আমাদের Blackjack dApp-এর প্রতিটি খেলোয়াড়ের জন্য 1000 মিন্ট করা হয়েছে +- [`hardhat.config.ts`](https://github.com/kurtosis-tech/awesome-kurtosis/blob/main/smart-contract-example/hardhat.config.ts) আপনার Hardhat সেটআপ কনফিগার করে + +### স্থানীয় টেস্টনেট ব্যবহার করার জন্য Hardhat কনফিগার করুন {#configure-hardhat} + +আপনার dApp ডেভেলপমেন্ট পরিবেশ সেট আপ করার সাথে, আপনি এখন Kurtosis ব্যবহার করে তৈরি স্থানীয় Ethereum টেস্টনেট ব্যবহার করতে Hardhat-কে সংযুক্ত করবেন। এটি সম্পন্ন করতে, আপনার `hardhat.config.ts` কনফিগারেশন ফাইলের `localnet` স্ট্রাক্টে `<$YOUR_PORT>` কে যেকোনো `el-client-` পরিষেবা থেকে rpc uri আউটপুটের পোর্ট দিয়ে প্রতিস্থাপন করুন। এই স্যাম্পল ক্ষেত্রে, পোর্ট হবে `64248`। আপনার পোর্ট ভিন্ন হবে। + +`hardhat.config.ts`-এ উদাহরণ: + +```js +localnet: { +url: 'http://127.0.0.1:<$YOUR_PORT>',// TODO: $YOUR_PORT-কে ETH নেটওয়ার্ক কার্টোসিস প্যাকেজ দ্বারা উৎপাদিত একটি নোড URI-এর পোর্ট দিয়ে প্রতিস্থাপন করুন + +// এগুলি হল eth-নেটওয়ার্ক-প্যাকেজ দ্বারা তৈরি প্রিফান্ডেড টেস্ট অ্যাকাউন্টের সাথে যুক্ত প্রাইভেট কী +// +accounts: [ + "ef5177cd0b6b21c87db5a0bf35d4084a8a57a9d6a064f86d51ac85f2b873a4e2", + "48fcc39ae27a0e8bf0274021ae6ebd8fe4a0e12623d61464c498900b28feb567", + "7988b3a148716ff800414935b305436493e1f25237a2a03e5eebc343735e2f31", + "b3c409b6b0b3aa5e65ab2dc1930534608239a478106acf6f3d9178e9f9b00b35", + "df9bb6de5d3dc59595bcaa676397d837ff49441d211878c024eabda2cd067c9f", + "7da08f856b5956d40a72968f93396f6acff17193f013e8053f6fbb6c08c194d6", + ], +}, +``` + +একবার আপনি আপনার ফাইলটি সংরক্ষণ করলে, আপনার Hardhat dApp ডেভেলপমেন্ট পরিবেশ এখন আপনার স্থানীয় Ethereum টেস্টনেটের সাথে সংযুক্ত! আপনি রান করে যাচাই করতে পারেন যে আপনার টেস্টনেট কাজ করছে: + +```python +npx hardhat balances --network localnet +``` + +আউটপুটটি এইরকম দেখতে হবে: + +```python +0x878705ba3f8Bc32FCf7F4CAa1A35E72AF65CF766 has balance 10000000000000000000000000 +0x4E9A3d9D1cd2A2b2371b8b3F489aE72259886f1A has balance 10000000000000000000000000 +0xdF8466f277964Bb7a0FFD819403302C34DCD530A has balance 10000000000000000000000000 +0x5c613e39Fc0Ad91AfDA24587e6f52192d75FBA50 has balance 10000000000000000000000000 +0x375ae6107f8cC4cF34842B71C6F746a362Ad8EAc has balance 10000000000000000000000000 +0x1F6298457C5d76270325B724Da5d1953923a6B88 has balance 10000000000000000000000000 +``` + +এটি নিশ্চিত করে যে Hardhat আপনার স্থানীয় টেস্টনেট ব্যবহার করছে এবং `eth-network-package` দ্বারা তৈরি প্রি-ফান্ডেড অ্যাকাউন্টগুলি সনাক্ত করছে। + +### স্থানীয়ভাবে আপনার dApp স্থাপন এবং পরীক্ষা করুন {#deploy-and-test-dapp} + +dApp ডেভেলপমেন্ট পরিবেশটি স্থানীয় Ethereum টেস্টনেটের সাথে সম্পূর্ণরূপে সংযুক্ত থাকায়, আপনি এখন স্থানীয় টেস্টনেট ব্যবহার করে আপনার dApp-এর বিরুদ্ধে ডেভেলপমেন্ট এবং পরীক্ষার কর্মপ্রবাহ চালাতে পারেন। + +স্থানীয় প্রোটোটাইপিং এবং ডেভেলপমেন্টের জন্য `ChipToken.sol` স্মার্ট কন্ট্র্যাক্ট কম্পাইল এবং স্থাপন করতে, রান করুন: + +```python +npx hardhat compile +npx hardhat run scripts/deploy.ts --network localnet +``` + +আউটপুটটি এইরকম দেখতে হবে: + +```python +ChipToken এখানে স্থাপন করা হয়েছে: 0xAb2A01BC351770D09611Ac80f1DE076D56E0487d +``` + +এখন আপনার স্থানীয় dApp-এর বিরুদ্ধে `simple.js` পরীক্ষাটি চালিয়ে চেষ্টা করুন এটি নিশ্চিত করতে যে আমাদের Blackjack dApp-এর প্রতিটি খেলোয়াড়ের জন্য 1000 মিন্ট করা হয়েছে: + +আউটপুটটি এইরকম দেখতে হবে: + +```python +npx hardhat test --network localnet +``` + +আউটপুটটি এইরকম দেখতে হবে: + +```python +ChipToken + mint + ✔ প্লেয়ার ওয়ানের জন্য 1000 চিপ মিন্ট করা উচিত + + 1 পাসিং (654ms) +``` + +### পর্যালোচনা {#review-dapp-workflows} + +এই মুহূর্তে, আপনি একটি dApp ডেভেলপমেন্ট পরিবেশ সেট আপ করেছেন, এটিকে Kurtosis দ্বারা তৈরি একটি স্থানীয় Ethereum নেটওয়ার্কের সাথে সংযুক্ত করেছেন, এবং আপনার dApp-এর বিরুদ্ধে একটি সহজ পরীক্ষা কম্পাইল, স্থাপন এবং চালিয়েছেন। + +এখন আসুন অন্বেষণ করা যাক কীভাবে আপনি বিভিন্ন নেটওয়ার্ক কনফিগারেশনের অধীনে আমাদের ডিএ্যাপস পরীক্ষা করার জন্য অন্তর্নিহিত নেটওয়ার্ক কনফিগার করতে পারেন। + +## স্থানীয় Ethereum টেস্টনেট কনফিগার করা {#configure-testnet} + +### ক্লায়েন্ট কনফিগারেশন এবং নোডের সংখ্যা পরিবর্তন করা {#configure-client-config-and-num-nodes} + +আপনার স্থানীয় Ethereum টেস্টনেটটি বিভিন্ন EL এবং CL ক্লায়েন্ট পেয়ার, সেইসাথে বিভিন্ন সংখ্যক নোড ব্যবহার করার জন্য কনফিগার করা যেতে পারে, যা আপনি যে পরিস্থিতি এবং নির্দিষ্ট নেটওয়ার্ক কনফিগারেশন ডেভেলপ বা পরীক্ষা করতে চান তার উপর নির্ভর করে। এর মানে হল যে, একবার সেট আপ হয়ে গেলে, আপনি একটি কাস্টমাইজড স্থানীয় টেস্টনেট চালু করতে পারেন এবং একই কর্মপ্রবাহ (স্থাপন, পরীক্ষা, ইত্যাদি) চালানোর জন্য এটি ব্যবহার করতে পারেন। সবকিছু প্রত্যাশিতভাবে কাজ করে তা নিশ্চিত করার জন্য বিভিন্ন নেটওয়ার্ক কনফিগারেশনের অধীনে। আপনি যে অন্যান্য প্যারামিটারগুলি পরিবর্তন করতে পারেন সে সম্পর্কে আরও জানতে, এই লিঙ্কটি দেখুন। + +একবার চেষ্টা করে দেখুন! আপনি একটি JSON ফাইলের মাধ্যমে `eth-network-package`-এ বিভিন্ন কনফিগারেশন বিকল্প পাস করতে পারেন। এই নেটওয়ার্ক প্যারামস JSON ফাইলটি নির্দিষ্ট কনফিগারেশন সরবরাহ করে যা Kurtosis স্থানীয় Ethereum নেটওয়ার্ক সেট আপ করতে ব্যবহার করবে। + +ডিফল্ট কনফিগারেশন ফাইলটি নিন এবং বিভিন্ন EL/CL পেয়ার সহ দুটি নোড চালু করতে এটি সম্পাদনা করুন: + +- নোড 1 `geth`/`lighthouse` সহ +- নোড 2 `geth`/`lodestar` সহ +- নোড 3 `geth`/`teku` সহ + +এই কনফিগারেশনটি আপনার dApp পরীক্ষা করার জন্য Ethereum নোড বাস্তবায়নের একটি ভিন্নধর্মী নেটওয়ার্ক তৈরি করে। আপনার কনফিগারেশন ফাইলটি এখন এইরকম দেখতে হবে: + +```yaml +{ + "participants": + [ + { + "el_client_type": "geth", + "el_client_image": "", + "el_client_log_level": "", + "cl_client_type": "lighthouse", + "cl_client_image": "", + "cl_client_log_level": "", + "beacon_extra_params": [], + "el_extra_params": [], + "validator_extra_params": [], + "builder_network_params": null, + }, + { + "el_client_type": "geth", + "el_client_image": "", + "el_client_log_level": "", + "cl_client_type": "lodestar", + "cl_client_image": "", + "cl_client_log_level": "", + "beacon_extra_params": [], + "el_extra_params": [], + "validator_extra_params": [], + "builder_network_params": null, + }, + { + "el_client_type": "geth", + "el_client_image": "", + "el_client_log_level": "", + "cl_client_type": "teku", + "cl_client_image": "", + "cl_client_log_level": "", + "beacon_extra_params": [], + "el_extra_params": [], + "validator_extra_params": [], + "builder_network_params": null, + }, + ], + "network_params": + { + "preregistered_validator_keys_mnemonic": "giant issue aisle success illegal bike spike question tent bar rely arctic volcano long crawl hungry vocal artwork sniff fantasy very lucky have athlete", + "num_validator_keys_per_node": 64, + "network_id": "3151908", + "deposit_contract_address": "0x4242424242424242424242424242424242424242", + "seconds_per_slot": 12, + "genesis_delay": 120, + "capella_fork_epoch": 5, + }, +} +``` + +প্রতিটি `participants` স্ট্রাক্ট নেটওয়ার্কের একটি নোডের সাথে ম্যাপ করে, তাই 3টি `participants` স্ট্রাক্ট Kurtosis-কে আপনার নেটওয়ার্কে 3টি নোড চালু করতে বলবে। প্রতিটি `participants` স্ট্রাক্ট আপনাকে সেই নির্দিষ্ট নোডের জন্য ব্যবহৃত EL এবং CL পেয়ার নির্দিষ্ট করার অনুমতি দেবে। + +`network_params` স্ট্রাক্টটি নেটওয়ার্ক সেটিংস কনফিগার করে যা প্রতিটি নোডের জন্য জেনেসিস ফাইল তৈরি করতে ব্যবহৃত হয় এবং সেইসাথে নেটওয়ার্কের প্রতি স্লটে সেকেন্ডের মতো অন্যান্য সেটিংস। + +আপনার সম্পাদিত প্যারামস ফাইলটি আপনার ইচ্ছামত যেকোনো ডিরেক্টরিতে সংরক্ষণ করুন (নীচের উদাহরণে, এটি ডেস্কটপে সংরক্ষণ করা হয়েছে) এবং তারপর রান করে আপনার Kurtosis প্যাকেজ চালানোর জন্য এটি ব্যবহার করুন: + +```python +kurtosis clean -a && kurtosis run --enclave local-eth-testnet github.com/kurtosis-tech/eth-network-package "$(cat ~/eth-network-params.json)" +``` + +দ্রষ্টব্য: `kurtosis clean -a` কমান্ডটি এখানে Kurtosis-কে নতুন একটি শুরু করার আগে পুরানো টেস্টনেট এবং এর বিষয়বস্তু ধ্বংস করার নির্দেশ দেওয়ার জন্য ব্যবহৃত হয়। + +আবার, Kurtosis কিছুক্ষণ কাজ করবে এবং যে পৃথক পদক্ষেপগুলি ঘটছে তা প্রিন্ট করবে। অবশেষে, আউটপুটটি এইরকম দেখতে হবে: + +```python +Starlark code successfully run. No output was returned. +INFO[2023-04-07T11:43:16-04:00] ========================================================== +INFO[2023-04-07T11:43:16-04:00] || Created enclave: local-eth-testnet || +INFO[2023-04-07T11:43:16-04:00] ========================================================== +Name: local-eth-testnet +UUID: bef8c192008e +Status: RUNNING +Creation Time: Fri, 07 Apr 2023 11:41:58 EDT + +========================================= Files Artifacts ========================================= +UUID Name +cc495a8e364a cl-genesis-data +7033fcdb5471 el-genesis-data +a3aef43fc738 genesis-generation-config-cl +8e968005fc9d genesis-generation-config-el +3182cca9d3cd geth-prefunded-keys +8421166e234f prysm-password +d9e6e8d44d99 validator-keystore-0 +23f5ba517394 validator-keystore-1 +4d28dea40b5c validator-keystore-2 + +========================================== User Services ========================================== +UUID Name Ports Status +485e6fde55ae cl-client-0-beacon http: 4000/tcp -> http://127.0.0.1:65010 RUNNING + metrics: 5054/tcp -> http://127.0.0.1:65011 + tcp-discovery: 9000/tcp -> 127.0.0.1:65012 + udp-discovery: 9000/udp -> 127.0.0.1:54455 +73739bd158b2 cl-client-0-validator http: 5042/tcp -> 127.0.0.1:65016 RUNNING + metrics: 5064/tcp -> http://127.0.0.1:65017 +1b0a233cd011 cl-client-1-beacon http: 4000/tcp -> 127.0.0.1:65021 RUNNING + metrics: 8008/tcp -> 127.0.0.1:65023 + tcp-discovery: 9000/tcp -> 127.0.0.1:65024 + udp-discovery: 9000/udp -> 127.0.0.1:56031 + validator-metrics: 5064/tcp -> 127.0.0.1:65022 +949b8220cd53 cl-client-1-validator http: 4000/tcp -> 127.0.0.1:65028 RUNNING + metrics: 8008/tcp -> 127.0.0.1:65030 + tcp-discovery: 9000/tcp -> 127.0.0.1:65031 + udp-discovery: 9000/udp -> 127.0.0.1:60784 + validator-metrics: 5064/tcp -> 127.0.0.1:65029 +c34417bea5fa cl-client-2 http: 4000/tcp -> 127.0.0.1:65037 RUNNING + metrics: 8008/tcp -> 127.0.0.1:65035 + tcp-discovery: 9000/tcp -> 127.0.0.1:65036 + udp-discovery: 9000/udp -> 127.0.0.1:63581 +e19738e6329d el-client-0 engine-rpc: 8551/tcp -> 127.0.0.1:64986 RUNNING + rpc: 8545/tcp -> 127.0.0.1:64988 + tcp-discovery: 30303/tcp -> 127.0.0.1:64987 + udp-discovery: 30303/udp -> 127.0.0.1:55706 + ws: 8546/tcp -> 127.0.0.1:64989 +e904687449d9 el-client-1 engine-rpc: 8551/tcp -> 127.0.0.1:64993 RUNNING + rpc: 8545/tcp -> 127.0.0.1:64995 + tcp-discovery: 30303/tcp -> 127.0.0.1:64994 + udp-discovery: 30303/udp -> 127.0.0.1:58096 + ws: 8546/tcp -> 127.0.0.1:64996 +ad6f401126fa el-client-2 engine-rpc: 8551/tcp -> 127.0.0.1:65003 RUNNING + rpc: 8545/tcp -> 127.0.0.1:65001 + tcp-discovery: 30303/tcp -> 127.0.0.1:65000 + udp-discovery: 30303/udp -> 127.0.0.1:57269 + ws: 8546/tcp -> 127.0.0.1:65002 +12d04a9dbb69 prelaunch-data-generator-1680882122181135513 STOPPED +5b45f9c0504b prelaunch-data-generator-1680882122192182847 STOPPED +3d4aaa75e218 prelaunch-data-generator-1680882122201668972 STOPPED +``` + +অভিনন্দন! আপনি সফলভাবে আপনার স্থানীয় টেস্টনেটকে 1টির পরিবর্তে 3টি নোড থাকার জন্য কনফিগার করেছেন। আপনার dApp-এর বিরুদ্ধে (স্থাপন ও পরীক্ষা) আপনি আগে যে কর্মপ্রবাহগুলি করেছিলেন তা চালাতে, আপনার `hardhat.config.ts` কনফিগারেশন ফাইলের `localnet` স্ট্রাক্টে `<$YOUR_PORT>`-কে আপনার নতুন, 3-নোড স্থানীয় টেস্টনেটের যেকোনো `el-client-` পরিষেবা থেকে rpc uri আউটপুটের পোর্ট দিয়ে প্রতিস্থাপন করে আমরা আগে যে একই অপারেশনগুলি করেছিলাম তা সম্পাদন করুন। + +## উপসংহার {#conclusion} + +এবং এটাই সব! এই সংক্ষিপ্ত গাইডটি সংক্ষেপে বলতে গেলে, আপনি: + +- Kurtosis ব্যবহার করে Docker-এর উপর একটি স্থানীয় Ethereum টেস্টনেট তৈরি করেছেন +- আপনার স্থানীয় dApp ডেভেলপমেন্ট পরিবেশকে স্থানীয় Ethereum নেটওয়ার্কের সাথে সংযুক্ত করেছেন +- স্থানীয় Ethereum নেটওয়ার্কে একটি dApp স্থাপন করেছেন এবং এর বিরুদ্ধে একটি সহজ পরীক্ষা চালিয়েছেন +- অন্তর্নিহিত Ethereum নেটওয়ার্ককে 3টি নোড থাকার জন্য কনফিগার করেছেন + +আপনার জন্য কী ভালো হয়েছে, কী উন্নত করা যেতে পারে, বা আপনার যেকোনো প্রশ্নের উত্তর দিতে আমরা আপনার কাছ থেকে শুনতে আগ্রহী। [GitHub](https://github.com/kurtosis-tech/kurtosis/issues/new/choose)-এর মাধ্যমে যোগাযোগ করতে বা [আমাদের ইমেল করতে](mailto:feedback@kurtosistech.com) দ্বিধা করবেন না! + +### অন্যান্য উদাহরণ এবং গাইড {#other-examples-guides} + +আমরা আপনাকে আমাদের [কুইকস্টার্ট](https://docs.kurtosis.com/quickstart) (যেখানে আপনি একটি Postgres ডাটাবেস এবং এর উপরে API তৈরি করবেন) এবং আমাদের [awesome-kurtosis রিপোজিটরি](https://github.com/kurtosis-tech/awesome-kurtosis)-তে আমাদের অন্যান্য উদাহরণগুলি দেখতে উৎসাহিত করি যেখানে আপনি প্যাকেজ সহ কিছু দুর্দান্ত উদাহরণ পাবেন: + +- [একই স্থানীয় Ethereum টেস্টনেট চালু করা](https://github.com/kurtosis-tech/eth2-package), কিন্তু অতিরিক্ত পরিষেবা সংযুক্ত সহ যেমন একটি লেনদেন স্প্যামার (লেনদেন সিমুলেট করতে), একটি ফর্ক মনিটর, এবং একটি সংযুক্ত Grafana এবং Prometheus ইনস্ট্যান্স +- একই স্থানীয় Ethereum নেটওয়ার্কের বিরুদ্ধে একটি [সাব-নেটওয়ার্কিং পরীক্ষা](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/ethereum-network-partition-test) সম্পাদন করা diff --git a/public/content/translations/bn/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md b/public/content/translations/bn/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md new file mode 100644 index 00000000000..69e0a5c7c00 --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md @@ -0,0 +1,144 @@ +--- +title: "কন্ট্র্যাক্টের আকারের সীমাবদ্ধতার বিরুদ্ধে লড়তে কন্ট্র্যাক্টের আকার কমানো" +description: "আপনার স্মার্ট কন্ট্র্যাক্টগুলোকে খুব বড় হয়ে যাওয়া থেকে আটকাতে আপনি কি করতে পারেন?" +author: Markus Waas +lang: bn +tags: [ "সলিডিটি", "স্মার্ট কন্ট্র্যাক্ট", "সংগ্রহস্থল" ] +skill: intermediate +published: 2020-06-26 +source: soliditydeveloper.com +sourceUrl: https://soliditydeveloper.com/max-contract-size +--- + +## একটি সীমা কেন আছে? {#why-is-there-a-limit} + +[নভেম্বর 22, 2016](https://blog.ethereum.org/2016/11/18/hard-fork-no-4-spurious-dragon/) তারিখে Spurious Dragon হার্ড-ফর্ক [EIP-170](https://eips.ethereum.org/EIPS/eip-170) চালু করে, যা 24.576 kb এর একটি স্মার্ট কন্ট্র্যাক্ট আকারের সীমা যোগ করে। একজন Solidity ডেভেলপার হিসেবে আপনার জন্য এর মানে হল যে যখন আপনি আপনার কন্ট্র্যাক্টে আরও বেশি করে ফাংশনালিটি যোগ করবেন, কোনো এক সময়ে আপনি সীমাতে পৌঁছে যাবেন এবং ডিপ্লয় করার সময় এই ত্রুটিটি দেখতে পাবেন: + +`সতর্কতা: কন্ট্র্যাক্টের কোডের আকার 24576 বাইট অতিক্রম করেছে (Spurious Dragon-এ একটি সীমা চালু করা হয়েছে)। এই কন্ট্র্যাক্টটি মেইননেটে ডিপ্লয়যোগ্য নাও হতে পারে। অপ্টিমাইজার সক্রিয় করার কথা বিবেচনা করুন (কম "রান" মান সহ!), রিভার্ট স্ট্রিংগুলি বন্ধ করুন, বা লাইব্রেরি ব্যবহার করুন।` + +এই সীমাটি ডিনায়াল-অফ-সার্ভিস (DOS) আক্রমণ প্রতিরোধ করার জন্য চালু করা হয়েছিল। একটি কন্ট্র্যাক্টে যেকোনো কল গ্যাসের দিক থেকে তুলনামূলকভাবে সস্তা। তবে, Ethereum নোডগুলোর জন্য একটি কন্ট্র্যাক্ট কলের প্রভাব কল করা কন্ট্র্যাক্টের কোডের আকারের উপর নির্ভর করে অসামঞ্জস্যপূর্ণভাবে বৃদ্ধি পায় (ডিস্ক থেকে কোড পড়া, কোড প্রি-প্রসেস করা, Merkle প্রুফে ডেটা যোগ করা)। যখনই আপনার এমন পরিস্থিতি থাকে যেখানে আক্রমণকারীকে অন্যদের জন্য অনেক কাজ তৈরি করার জন্য অল্প রিসোর্স প্রয়োজন হয়, তখন DOS আক্রমণের সম্ভাবনা তৈরি হয়। + +মূলত এটি তেমন কোনো সমস্যা ছিল না কারণ একটি স্বাভাবিক কন্ট্র্যাক্টের আকারের সীমা হলো ব্লক গ্যাস লিমিট। স্বাভাবিকভাবেই, একটি কন্ট্র্যাক্টকে এমন একটি ট্রানজ্যাকশনের মধ্যে ডিপ্লয় করতে হবে যা কন্ট্র্যাক্টের সমস্ত বাইটকোড ধারণ করে। আপনি যদি একটি ব্লকে শুধুমাত্র সেই একটি ট্রানজ্যাকশন অন্তর্ভুক্ত করেন, তবে আপনি সেই সমস্ত গ্যাস ব্যবহার করতে পারবেন, কিন্তু এটি অসীম নয়। [লন্ডন আপগ্রেডের](/ethereum-forks/#london) পর থেকে, ব্লক গ্যাস লিমিট নেটওয়ার্কের চাহিদার উপর নির্ভর করে 15M থেকে 30M ইউনিটের মধ্যে পরিবর্তিত হতে সক্ষম হয়েছে। + +এর পরে আমরা তাদের সম্ভাব্য প্রভাব অনুসারে সাজানো কিছু পদ্ধতি দেখব। এটিকে ওজন কমানোর দিক থেকে ভাবুন। কারো জন্য তাদের লক্ষ্য ওজন (আমাদের ক্ষেত্রে 24kb) অর্জনের সেরা কৌশল হল প্রথমে বড় প্রভাবযুক্ত পদ্ধতিগুলিতে মনোযোগ দেওয়া। বেশিরভাগ ক্ষেত্রে শুধুমাত্র আপনার খাদ্যাভ্যাস ঠিক করলেই আপনি সেখানে পৌঁছে যাবেন, কিন্তু কখনও কখনও আপনার আরও কিছু প্রয়োজন হয়। তারপরে আপনি কিছু ব্যায়াম (মাঝারি প্রভাব) বা এমনকি পরিপূরক (ছোট প্রভাব) যোগ করতে পারেন। + +## বড় প্রভাব {#big-impact} + +### আপনার কন্ট্র্যাক্টগুলো আলাদা করুন {#separate-your-contracts} + +এটি সর্বদা আপনার প্রথম পদক্ষেপ হওয়া উচিত। আপনি কীভাবে কন্ট্র্যাক্টটিকে একাধিক ছোট কন্ট্র্যাক্টে বিভক্ত করতে পারেন? এটি সাধারণত আপনাকে আপনার কন্ট্র্যাক্টগুলোর জন্য একটি ভাল আর্কিটেকচার তৈরি করতে বাধ্য করে। কোড পাঠযোগ্যতার দৃষ্টিকোণ থেকে ছোট কন্ট্র্যাক্টগুলো সর্বদা পছন্দনীয়। কন্ট্র্যাক্ট বিভক্ত করার জন্য, নিজেকে জিজ্ঞাসা করুন: + +- কোন ফাংশনগুলো একসাথে সম্পর্কিত? প্রতিটি ফাংশন সেট তার নিজস্ব কন্ট্র্যাক্টে সবচেয়ে ভালো হতে পারে। +- কোন ফাংশনগুলোর জন্য কন্ট্র্যাক্টের স্টেট বা স্টেটের একটি নির্দিষ্ট সাবসেট পড়ার প্রয়োজন নেই? +- আপনি কি সংগ্রহস্থল এবং ফাংশনালিটি বিভক্ত করতে পারেন? + +### লাইব্রেরি {#libraries} + +সংগ্রহস্থল থেকে ফাংশনালিটি কোড সরানোর একটি সহজ উপায় হল একটি [লাইব্রেরি](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#libraries) ব্যবহার করা। লাইব্রেরি ফাংশনগুলিকে ইন্টারনাল হিসাবে ঘোষণা করবেন না কারণ সেগুলি কম্পাইলেশনের সময় সরাসরি [কন্ট্র্যাক্টে যোগ করা হবে](https://ethereum.stackexchange.com/questions/12975/are-internal-functions-in-libraries-not-covered-by-linking)। কিন্তু আপনি যদি পাবলিক ফাংশন ব্যবহার করেন, তবে সেগুলি আসলে একটি পৃথক লাইব্রেরি কন্ট্র্যাক্টে থাকবে। লাইব্রেরির ব্যবহার আরও সুবিধাজনক করতে [using for](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#using-for) ব্যবহার করার কথা বিবেচনা করুন। + +### প্রক্সি {#proxies} + +একটি আরও উন্নত কৌশল হবে একটি প্রক্সি সিস্টেম। লাইব্রেরিগুলো ব্যাকগ্রাউন্ডে `DELEGATECALL` ব্যবহার করে যা কেবল কলিং কন্ট্র্যাক্টের স্টেট দিয়ে অন্য একটি কন্ট্র্যাক্টের ফাংশন কার্যকর করে। প্রক্সি সিস্টেম সম্পর্কে আরও জানতে [এই ব্লগ পোস্টটি](https://hackernoon.com/how-to-make-smart-contracts-upgradable-2612e771d5a2) দেখুন। তারা আপনাকে আরও ফাংশনালিটি দেয়, যেমন, তারা আপগ্রেডযোগ্যতা সক্ষম করে, কিন্তু তারা অনেক জটিলতাও যোগ করে। আমি কেবল কন্ট্র্যাক্টের আকার কমানোর জন্য এগুলি যোগ করব না যদি না এটি যেকোনো কারণেই আপনার একমাত্র বিকল্প হয়। + +## মাঝারি প্রভাব {#medium-impact} + +### ফাংশনগুলি সরান {#remove-functions} + +এটি স্পষ্ট হওয়া উচিত। ফাংশন একটি কন্ট্র্যাক্টের আকার বেশ কিছুটা বাড়িয়ে দেয়। + +- **এক্সটার্নাল**: প্রায়শই আমরা সুবিধার জন্য অনেক ভিউ ফাংশন যোগ করি। যতক্ষণ না আপনি আকারের সীমাতে পৌঁছান ততক্ষণ এটি ঠিক আছে। তখন আপনি একেবারে অপরিহার্য ফাংশনগুলো ছাড়া বাকি সবগুলি সরানোর কথা ভাবতে পারেন। +- **ইন্টারনাল**: আপনি ইন্টারনাল/প্রাইভেট ফাংশনগুলিও সরাতে পারেন এবং যতক্ষণ ফাংশনটি কেবল একবার কল করা হয় ততক্ষণ কোডটি কেবল ইনলাইন করতে পারেন। + +### অতিরিক্ত ভেরিয়েবল এড়িয়ে চলুন {#avoid-additional-variables} + +```solidity +function get(uint id) returns (address,address) { + MyStruct memory myStruct = myStructs[id]; + return (myStruct.addr1, myStruct.addr2); +} +``` + +```solidity +function get(uint id) returns (address,address) { + return (myStructs[id].addr1, myStructs[id].addr2); +} +``` + +এইরকম একটি সাধারণ পরিবর্তনে **0.28kb** এর পার্থক্য তৈরি হয়। সম্ভবত আপনি আপনার কন্ট্র্যাক্টগুলোতে অনেক একই রকম পরিস্থিতি খুঁজে পেতে পারেন এবং সেগুলি সত্যিই একটি উল্লেখযোগ্য পরিমাণে যোগ হতে পারে। + +### ত্রুটির মেসেজ ছোট করুন {#shorten-error-message} + +দীর্ঘ রিভার্ট মেসেজ এবং বিশেষ করে অনেক ভিন্ন রিভার্ট মেসেজ কন্ট্র্যাক্টকে ফুলিয়ে তুলতে পারে। এর পরিবর্তে সংক্ষিপ্ত ত্রুটির কোড ব্যবহার করুন এবং আপনার কন্ট্র্যাক্টে সেগুলি ডিকোড করুন। একটি দীর্ঘ মেসেজ অনেক ছোট হতে পারে: + +```solidity +require(msg.sender == owner, "শুধুমাত্র এই কন্ট্র্যাক্টের মালিকই এই ফাংশনটি কল করতে পারবেন"); +``` + +```solidity +require(msg.sender == owner, "OW1"); +``` + +### ত্রুটির মেসেজের পরিবর্তে কাস্টম ত্রুটি ব্যবহার করুন + +কাস্টম ত্রুটি [Solidity 0.8.4](https://blog.soliditylang.org/2021/04/21/custom-errors/)-এ চালু করা হয়েছে। এগুলি আপনার কন্ট্র্যাক্টের আকার কমানোর একটি দুর্দান্ত উপায়, কারণ এগুলি সিলেক্টর হিসাবে ABI-এনকোডেড (ঠিক যেমন ফাংশনগুলো হয়)। + +```solidity +error Unauthorized(); + +if (msg.sender != owner) { + revert Unauthorized(); +} +``` + +### অপ্টিমাইজারে একটি কম রান ভ্যালু বিবেচনা করুন {#consider-a-low-run-value-in-the-optimizer} + +আপনি অপ্টিমাইজার সেটিংসও পরিবর্তন করতে পারেন। 200 এর ডিফল্ট মানটির অর্থ হল এটি বাইটকোডকে এমনভাবে অপ্টিমাইজ করার চেষ্টা করছে যেন একটি ফাংশন 200 বার কল করা হয়েছে। আপনি যদি এটিকে 1-এ পরিবর্তন করেন, তবে আপনি মূলত অপ্টিমাইজারকে প্রতিটি ফাংশন শুধুমাত্র একবার চালানোর জন্য অপ্টিমাইজ করতে বলছেন। শুধুমাত্র একবার চালানোর জন্য একটি অপ্টিমাইজ করা ফাংশনের অর্থ হল এটি ডিপ্লয়মেন্টের জন্যই অপ্টিমাইজ করা হয়েছে। সচেতন থাকুন যে **এটি ফাংশনগুলি চালানোর জন্য [গ্যাসের খরচ](/developers/docs/gas/) বাড়িয়ে দেয়**, তাই আপনি এটি করতে নাও চাইতে পারেন। + +## ছোট প্রভাব {#small-impact} + +### ফাংশনে স্ট্রাকট পাস করা এড়িয়ে চলুন {#avoid-passing-structs-to-functions} + +আপনি যদি [ABIEncoderV2](https://solidity.readthedocs.io/en/v0.6.10/layout-of-source-files.html#abiencoderv2) ব্যবহার করেন, তাহলে এটি একটি ফাংশনে স্ট্রাকট পাস না করতে সাহায্য করতে পারে। প্যারামিটারটিকে স্ট্রাকট হিসাবে পাস করার পরিবর্তে, প্রয়োজনীয় প্যারামিটারগুলি সরাসরি পাস করুন। এই উদাহরণে আমরা আরও **0.1kb** সাশ্রয় করেছি। + +```solidity +function get(uint id) returns (address,address) { + return _get(myStruct); +} + +function _get(MyStruct memory myStruct) private view returns(address,address) { + return (myStruct.addr1, myStruct.addr2); +} +``` + +```solidity +function get(uint id) returns(address,address) { + return _get(myStructs[id].addr1, myStructs[id].addr2); +} + +function _get(address addr1, address addr2) private view returns(address,address) { + return (addr1, addr2); +} +``` + +### ফাংশন এবং ভেরিয়েবলের জন্য সঠিক ভিজিবিলিটি ঘোষণা করুন {#declare-correct-visibility-for-functions-and-variables} + +- ফাংশন বা ভেরিয়েবল যা শুধুমাত্র বাইরে থেকে কল করা হয়? `public` এর পরিবর্তে সেগুলোকে `external` হিসাবে ঘোষণা করুন। +- ফাংশন বা ভেরিয়েবল যা শুধুমাত্র কন্ট্র্যাক্টের মধ্যে থেকে কল করা হয়? `public` এর পরিবর্তে সেগুলোকে `private` বা `internal` হিসাবে ঘোষণা করুন। + +### মডিফায়ারগুলি সরান {#remove-modifiers} + +মডিফায়ার, বিশেষ করে যখন নিবিড়ভাবে ব্যবহার করা হয়, তখন কন্ট্র্যাক্টের আকারে একটি উল্লেখযোগ্য প্রভাব ফেলতে পারে। সেগুলি সরানোর কথা বিবেচনা করুন এবং পরিবর্তে ফাংশন ব্যবহার করুন। + +```solidity +modifier checkStuff() {} + +function doSomething() checkStuff {} +``` + +```solidity +function checkStuff() private {} + +function doSomething() { checkStuff(); } +``` + +এই টিপসগুলো আপনাকে কন্ট্র্যাক্টের আকার উল্লেখযোগ্যভাবে কমাতে সাহায্য করবে। আবারও, আমি যথেষ্ট জোর দিয়ে বলতে পারি না, সবচেয়ে বড় প্রভাবের জন্য সম্ভব হলে সর্বদা কন্ট্র্যাক্ট বিভক্ত করার দিকে মনোনিবেশ করুন। diff --git a/public/content/translations/bn/developers/tutorials/eip-1271-smart-contract-signatures/index.md b/public/content/translations/bn/developers/tutorials/eip-1271-smart-contract-signatures/index.md new file mode 100644 index 00000000000..1a9da42f184 --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/eip-1271-smart-contract-signatures/index.md @@ -0,0 +1,107 @@ +--- +title: "EIP-1271: স্মার্ট কন্ট্র্যাক্ট স্বাক্ষর করা এবং যাচাই করা" +description: "EIP-1271 সহ স্মার্ট কন্ট্র্যাক্ট স্বাক্ষর তৈরি এবং যাচাইকরণের একটি সংক্ষিপ্ত বিবরণ। আমরা Safe (পূর্বে Gnosis Safe) এ ব্যবহৃত EIP-1271 ইমপ্লিমেন্টেশন নিয়েও আলোচনা করব যাতে স্মার্ট কন্ট্র্যাক্ট ডেভেলপারদের জন্য একটি সুনির্দিষ্ট উদাহরণ দেওয়া যায়।" +author: Nathan H. Leung +lang: bn +tags: + [ + "eip-1271", + "স্মার্ট কন্ট্র্যাক্ট", + "যাচাইকরণ", + "স্বাক্ষর করা" + ] +skill: intermediate +published: 2023-01-12 +--- + +[EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) স্ট্যান্ডার্ড স্মার্ট কন্ট্র্যাক্টকে স্বাক্ষর যাচাই করার অনুমতি দেয়। + +এই টিউটোরিয়ালে, আমরা ডিজিটাল স্বাক্ষর, EIP-1271-এর প্রেক্ষাপট এবং [Safe](https://safe.global/) (পূর্বে Gnosis Safe) দ্বারা ব্যবহৃত EIP-1271-এর নির্দিষ্ট ইমপ্লিমেন্টেশন সম্পর্কে একটি সংক্ষিপ্ত বিবরণ দেব। সব মিলিয়ে, এটি আপনার নিজের কন্ট্র্যাক্টে EIP-1271 প্রয়োগ করার জন্য একটি সূচনা বিন্দু হিসাবে কাজ করতে পারে। + +## স্বাক্ষর কী? + +এই প্রসঙ্গে, একটি স্বাক্ষর (আরও স্পষ্টভাবে, একটি “ডিজিটাল স্বাক্ষর”) হল একটি বার্তা এবং তার সাথে এক ধরনের প্রমাণ যে বার্তাটি একটি নির্দিষ্ট ব্যক্তি/প্রেরক/অ্যাড্রেস থেকে এসেছে। + +উদাহরণস্বরূপ, একটি ডিজিটাল স্বাক্ষর এইরকম দেখতে হতে পারে: + +1. বার্তা: “আমি আমার ইথেরিয়াম ওয়ালেট দিয়ে এই ওয়েবসাইটে লগ ইন করতে চাই।” +2. স্বাক্ষরকারী: আমার অ্যাড্রেস হল `0x000…` +3. প্রমাণ: এখানে কিছু প্রমাণ দেওয়া হল যে আমি, `0x000…`, আসলে এই সম্পূর্ণ বার্তাটি তৈরি করেছি (এটি সাধারণত ক্রিপ্টোগ্রাফিক কিছু)। + +এটা মনে রাখা জরুরি যে একটি ডিজিটাল স্বাক্ষরের মধ্যে একটি “বার্তা” এবং একটি “স্বাক্ষর” দুটোই থাকে। + +কেন? উদাহরণস্বরূপ, যদি আপনি আমাকে স্বাক্ষর করার জন্য একটি চুক্তি দেন, এবং তারপর আমি স্বাক্ষরের পৃষ্ঠাটি কেটে ফেলি এবং চুক্তির বাকি অংশ ছাড়া শুধুমাত্র আমার স্বাক্ষরগুলি আপনাকে ফিরিয়ে দিই, তবে চুক্তিটি বৈধ হবে না। + +একইভাবে, একটি সংশ্লিষ্ট বার্তা ছাড়া একটি ডিজিটাল স্বাক্ষরের কোনো অর্থ হয় না! + +## EIP-1271 কেন বিদ্যমান? + +Ethereum-ভিত্তিক ব্লকচেইনে ব্যবহারের জন্য একটি ডিজিটাল স্বাক্ষর তৈরি করতে, আপনার সাধারণত একটি গোপন প্রাইভেট কী প্রয়োজন যা অন্য কেউ জানে না। এটাই আপনার স্বাক্ষরকে, আপনার করে তোলে (গোপন কী না জেনে অন্য কেউ একই স্বাক্ষর তৈরি করতে পারে না)। + +আপনার ইথেরিয়াম অ্যাকাউন্টের (অর্থাৎ, আপনার এক্সটারনালি-ওনড অ্যাকাউন্ট/EOA) সাথে একটি প্রাইভেট কী যুক্ত আছে এবং এই প্রাইভেট কী সাধারণত তখনই ব্যবহৃত হয় যখন কোনো ওয়েবসাইট বা ডিএ্যাপ আপনাকে স্বাক্ষরের জন্য জিজ্ঞাসা করে (যেমন, “ইথেরিয়াম দিয়ে লগইন করুন”)। + +একটি অ্যাপ ethers.js-এর মতো একটি থার্ড-পার্টি লাইব্রেরি ব্যবহার করে আপনার তৈরি করা [একটি স্বাক্ষর যাচাই করতে পারে](https://www.alchemy.com/docs/how-to-verify-a-message-signature-on-ethereum) [আপনার প্রাইভেট কী না জেনেই](https://en.wikipedia.org/wiki/Public-key_cryptography) এবং নিশ্চিত হতে পারে যে _আপনিই_ সেই স্বাক্ষরটি তৈরি করেছেন। + +> আসলে, যেহেতু EOA ডিজিটাল স্বাক্ষর পাবলিক-কী ক্রিপ্টোগ্রাফি ব্যবহার করে, তাই সেগুলি **অফচেইন** তৈরি এবং যাচাই করা যেতে পারে! এইভাবেই গ্যাস-বিহীন DAO ভোটিং কাজ করে — অনচেইনে ভোট জমা দেওয়ার পরিবর্তে, ক্রিপ্টোগ্রাফিক লাইব্রেরি ব্যবহার করে ডিজিটাল স্বাক্ষর অফচেইনে তৈরি এবং যাচাই করা যেতে পারে। + +যেখানে EOA অ্যাকাউন্টে একটি প্রাইভেট কী থাকে, সেখানে স্মার্ট কন্ট্র্যাক্ট অ্যাকাউন্টে কোনো ধরনের প্রাইভেট বা গোপন কী থাকে না (তাই "ইথেরিয়াম দিয়ে লগইন করুন", ইত্যাদি স্মার্ট কন্ট্র্যাক্ট অ্যাকাউন্টের সাথে স্বাভাবিকভাবে কাজ করতে পারে না)। + +EIP-1271 যে সমস্যার সমাধান করার লক্ষ্য রাখে: আমরা কীভাবে বলতে পারি যে একটি স্মার্ট কন্ট্র্যাক্ট স্বাক্ষর বৈধ যদি স্মার্ট কন্ট্র্যাক্টের এমন কোনো “গোপন” তথ্য না থাকে যা এটি স্বাক্ষরে অন্তর্ভুক্ত করতে পারে? + +## EIP-1271 কীভাবে কাজ করে? + +স্মার্ট কন্ট্র্যাক্টে এমন কোনো প্রাইভেট কী নেই যা বার্তা স্বাক্ষর করতে ব্যবহার করা যেতে পারে। তাহলে আমরা কীভাবে বলতে পারি যে একটি স্বাক্ষর আসল কিনা? + +আচ্ছা, একটি ধারণা হল যে আমরা শুধু স্মার্ট কন্ট্র্যাক্টকেই _জিজ্ঞাসা_ করতে পারি যে একটি স্বাক্ষর আসল কিনা! + +EIP-1271 যা করে তা হল, একটি প্রদত্ত স্বাক্ষর বৈধ কিনা তা একটি স্মার্ট কন্ট্র্যাক্টকে “জিজ্ঞাসা” করার এই ধারণাটিকে এটি মানসম্মত করে। + +EIP-1271 প্রয়োগকারী একটি কন্ট্র্যাক্টে `isValidSignature` নামে একটি ফাংশন থাকতে হবে যা একটি বার্তা এবং একটি স্বাক্ষর ইনপুট হিসেবে নেয়। কন্ট্র্যাক্টটি তখন কিছু যাচাইকরণ লজিক চালাতে পারে (স্পেক এখানে নির্দিষ্ট কিছু প্রয়োগ করে না) এবং তারপর স্বাক্ষরটি বৈধ কিনা তা নির্দেশ করে একটি মান ফিরিয়ে দিতে পারে। + +যদি `isValidSignature` একটি বৈধ ফলাফল প্রদান করে, তবে সেটা অনেকটা কন্ট্র্যাক্টের বলার মতোই, “হ্যাঁ, আমি এই স্বাক্ষর + বার্তা অনুমোদন করছি!” + +### ইন্টারফেস + +EIP-1271 স্পেকে সঠিক ইন্টারফেসটি এখানে দেওয়া হলো (আমরা নীচে `_hash` প্যারামিটার সম্পর্কে কথা বলব, কিন্তু আপাতত, এটিকে সেই বার্তা হিসাবে ভাবুন যা যাচাই করা হচ্ছে): + +```jsx +pragma solidity ^0.5.0;\n\ncontract ERC1271 {\n\n // বাইট4(keccak256(\"isValidSignature(bytes32,bytes)\"))\n bytes4 constant internal MAGICVALUE = 0x1626ba7e;\n\n /**\n * @dev প্রদত্ত হ্যাশের জন্য প্রদত্ত স্বাক্ষরটি বৈধ কিনা তা ফেরানো উচিত\n * @param _hash স্বাক্ষর করার ডেটার হ্যাস\n * @param _signature _hash-এর সাথে যুক্ত স্বাক্ষর বাইট অ্যারে\n *\n * ফাংশন পাস হলে অবশ্যই বাইট4 ম্যাজিক ভ্যালু 0x1626ba7e ফেরাতে হবে।\n * অবশ্যই স্টেট পরিবর্তন করা যাবে না (solc < 0.5 এর জন্য STATICCALL ব্যবহার করে, solc > 0.5 এর জন্য view মডিফায়ার ব্যবহার করে)\n * এক্সটারনাল কলের অনুমতি দিতে হবে\n */\n function isValidSignature(\n bytes32 _hash,\n bytes memory _signature)\n public\n view\n returns (bytes4 magicValue);\n} +``` + +## উদাহরণ EIP-1271 ইমপ্লিমেন্টেশন: Safe + +কন্ট্র্যাক্টগুলি অনেক উপায়ে `isValidSignature` প্রয়োগ করতে পারে — স্পেকটি সঠিক ইমপ্লিমেন্টেশন সম্পর্কে খুব বেশি কিছু বলে না। + +EIP-1271 প্রয়োগকারী একটি উল্লেখযোগ্য কন্ট্র্যাক্ট হল Safe (পূর্বে Gnosis Safe)। + +Safe-এর কোডে, `isValidSignature` [বাস্তবায়ন করা হয়েছে](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol) যাতে [দুটি উপায়ে](https://ethereum.stackexchange.com/questions/122635/signing-messages-as-a-gnosis-safe-eip1271-support) স্বাক্ষর তৈরি এবং যাচাই করা যায়: + +1. অনচেইন বার্তা + 1. তৈরি: একজন সেফ মালিক একটি বার্তাকে “স্বাক্ষর” করার জন্য একটি নতুন সেফ লেনদেন তৈরি করেন, এবং লেনদেনে ডেটা হিসাবে বার্তাটি পাস করেন। মাল্টিসিগ থ্রেশহোল্ডে পৌঁছানোর জন্য পর্যাপ্ত মালিকরা লেনদেনটিতে স্বাক্ষর করার পরে, লেনদেনটি ব্রডকাস্ট এবং রান করা হয়। লেনদেনটিতে, (`signMessage(bytes calldata _data)`) নামে একটি সেফ ফাংশন রয়েছে যা বার্তাটিকে “অনুমোদিত” বার্তার তালিকায় যুক্ত করে। + 2. যাচাইকরণ: Safe কন্ট্র্যাক্টে `isValidSignature` কল করুন, এবং বার্তা প্যারামিটার হিসেবে যাচাই করার জন্য বার্তাটি পাস করুন এবং [স্বাক্ষর প্যারামিটারের জন্য একটি খালি মান](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol#L32) (অর্থাৎ, `0x`) পাস করুন। Safe দেখবে যে স্বাক্ষর প্যারামিটারটি খালি এবং স্বাক্ষরটি ক্রিপ্টোগ্রাফিকভাবে যাচাই করার পরিবর্তে, এটি কেবল এগিয়ে গিয়ে পরীক্ষা করবে যে বার্তাটি “অনুমোদিত” বার্তার তালিকায় আছে কিনা। +2. অফচেইন বার্তা: + 1. তৈরি: একজন সেফ মালিক অফচেইনে একটি বার্তা তৈরি করে, তারপর অন্য সেফ মালিকদের দিয়ে বার্তাটি পৃথকভাবে স্বাক্ষর করায় যতক্ষণ না মাল্টিসিগ অনুমোদনের থ্রেশহোল্ড অতিক্রম করার জন্য পর্যাপ্ত স্বাক্ষর হয়। + 2. যাচাইকরণ: `isValidSignature` কল করুন। বার্তা প্যারামিটারে, যাচাই করার জন্য বার্তাটি পাস করুন। স্বাক্ষর প্যারামিটারে, প্রতিটি সেফ মালিকের স্বতন্ত্র স্বাক্ষরগুলি পরপর একসাথে সংযুক্ত করে পাস করুন। Safe পরীক্ষা করবে যে থ্রেশহোল্ড পূরণ করার জন্য পর্যাপ্ত স্বাক্ষর আছে কিনা **এবং** প্রতিটি স্বাক্ষর বৈধ কিনা। যদি তাই হয়, এটি সফল স্বাক্ষর যাচাইকরণ নির্দেশ করে একটি মান ফিরিয়ে দেবে। + +## `_hash` প্যারামিটারটি আসলে কী? পুরো বার্তাটি কেন পাস করা হয় না? + +আপনি হয়তো লক্ষ্য করেছেন যে [EIP-1271 ইন্টারফেস](https://eips.ethereum.org/EIPS/eip-1271)-এর `isValidSignature` ফাংশনটি সরাসরি বার্তাটি নেয় না, বরং একটি `_hash` প্যারামিটার নেয়। এর মানে হল `isValidSignature`-এ সম্পূর্ণ ইচ্ছামতো দৈর্ঘ্যের বার্তা পাস করার পরিবর্তে, আমরা বার্তার একটি 32-বাইট হ্যাস (সাধারণত keccak256) পাস করি। + +কলডেটার প্রতিটি বাইট — অর্থাৎ, একটি স্মার্ট কন্ট্র্যাক্ট ফাংশনে পাস করা ফাংশন প্যারামিটার ডেটা — [খরচ 16 গ্যাস (শূন্য বাইট হলে 4 গ্যাস)](https://eips.ethereum.org/EIPS/eip-2028), তাই বার্তাটি দীর্ঘ হলে এটি অনেক গ্যাস সাশ্রয় করতে পারে। + +### পূর্ববর্তী EIP-1271 স্পেসিফিকেশন + +এমন কিছু EIP-1271 স্পেসিফিকেশন প্রচলিত আছে যেগুলিতে `isValidSignature` ফাংশনের প্রথম প্যারামিটারের ধরন `bytes` (নির্দিষ্ট দৈর্ঘ্যের `bytes32` এর পরিবর্তে ইচ্ছামতো দৈর্ঘ্য) এবং প্যারামিটারের নাম `message`। এটি EIP-1271 স্ট্যান্ডার্ডের একটি [পুরানো সংস্করণ](https://github.com/safe-global/safe-contracts/issues/391#issuecomment-1075427206)। + +## আমার নিজের কন্ট্র্যাক্টে EIP-1271 কীভাবে প্রয়োগ করা উচিত? + +এই বিষয়ে স্পেকটি খুবই উন্মুক্ত। Safe ইমপ্লিমেন্টেশনে কিছু ভাল ধারণা রয়েছে: + +- আপনি কন্ট্র্যাক্টের "মালিক"-এর কাছ থেকে আসা EOA স্বাক্ষরগুলিকে বৈধ বলে বিবেচনা করতে পারেন। +- আপনি অনুমোদিত বার্তার একটি তালিকা সংরক্ষণ করতে পারেন এবং শুধুমাত্র সেগুলিকে বৈধ বলে বিবেচনা করতে পারেন। + +শেষ পর্যন্ত, কন্ট্র্যাক্ট ডেভেলপার হিসেবে এটি আপনার উপরই নির্ভর করে! + +## উপসংহার + +[EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) একটি বহুমুখী স্ট্যান্ডার্ড যা স্মার্ট কন্ট্র্যাক্টকে স্বাক্ষর যাচাই করার অনুমতি দেয়। এটি স্মার্ট কন্ট্র্যাক্টগুলিকে EOA-এর মতো আরও বেশি কাজ করার সুযোগ করে দেয় — উদাহরণস্বরূপ "ইথেরিয়াম দিয়ে লগইন করুন" পদ্ধতিটিকে স্মার্ট কন্ট্র্যাক্টের সাথে কাজ করার একটি উপায় প্রদান করে — এবং এটি অনেক উপায়ে প্রয়োগ করা যেতে পারে (Safe-এর একটি গুরুত্বপূর্ণ, আকর্ষণীয় ইমপ্লিমেন্টেশন রয়েছে যা বিবেচনা করার মতো)। diff --git a/public/content/translations/bn/developers/tutorials/erc-721-vyper-annotated-code/index.md b/public/content/translations/bn/developers/tutorials/erc-721-vyper-annotated-code/index.md new file mode 100644 index 00000000000..9dcb629aa7f --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/erc-721-vyper-annotated-code/index.md @@ -0,0 +1,640 @@ +--- +title: "Vyper ERC-721 কন্ট্র্যাক্ট ওয়াকথ্রু" +description: "Ryuya Nakamura-এর ERC-721 কন্ট্র্যাক্ট এবং এটি কীভাবে কাজ করে" +author: Ori Pomerantz +lang: bn +tags: [ "vyper", "erc-721", "python" ] +skill: beginner +published: 2021-04-01 +--- + +## ভূমিকা {#introduction} + +[ERC-721](/developers/docs/standards/tokens/erc-721/) স্ট্যান্ডার্ডটি নন-ফাঞ্জিবল টোকেনের (NFT) মালিকানা ধরে রাখতে ব্যবহৃত হয়। +[ERC-20](/developers/docs/standards/tokens/erc-20/) টোকেনগুলি একটি পণ্যের মতো আচরণ করে, কারণ পৃথক টোকেনগুলির মধ্যে কোনও পার্থক্য নেই। +এর বিপরীতে, ERC-721 টোকেনগুলি এমন অ্যাসেটের জন্য ডিজাইন করা হয়েছে যা একই রকম কিন্তু অভিন্ন নয়, যেমন বিভিন্ন বিড়ালের +কার্টুন বা বিভিন্ন রিয়েল এস্টেটের মালিকানা। + +এই আর্টিকেলে আমরা [Ryuya Nakamura-এর ERC-721 কন্ট্র্যাক্ট](https://github.com/vyperlang/vyper/blob/master/examples/tokens/ERC721.vy) বিশ্লেষণ করব। +এই কন্ট্র্যাক্টটি [Vyper](https://vyper.readthedocs.io/en/latest/index.html) ভাষায় লেখা, এটি Python-এর মতো একটি কন্ট্র্যাক্ট ল্যাঙ্গুয়েজ যা Solidity-র চেয়ে নিরাপত্তাহীন কোড লেখাকে আরও কঠিন করে তোলার জন্য ডিজাইন করা হয়েছে। + +## কন্ট্র্যাক্টটি {#contract} + +```python +# @dev ERC-721 নন-ফাঞ্জিবল টোকেন স্ট্যান্ডার্ডের ইমপ্লিমেন্টেশন। +# @author Ryuya Nakamura (@nrryuya) +# থেকে সংশোধিত: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vy +``` + +Vyper-এ, Python-এর মতোই, কমেন্টগুলি একটি হ্যাশ (`#`) দিয়ে শুরু হয় এবং লাইনের শেষ পর্যন্ত চলতে থাকে। যে কমেন্টগুলিতে +`@` থাকে সেগুলি [NatSpec](https://vyper.readthedocs.io/en/latest/natspec.html) দ্বারা মানুষের পাঠযোগ্য ডকুমেন্টেশন তৈরি করতে ব্যবহৃত হয়। + +```python +from vyper.interfaces import ERC721 + +implements: ERC721 +``` + +ERC-721 ইন্টারফেসটি Vyper ল্যাঙ্গুয়েজের মধ্যে অন্তর্নির্মিত। +[আপনি এখানে কোডের সংজ্ঞা দেখতে পারেন](https://github.com/vyperlang/vyper/blob/master/vyper/builtin_interfaces/ERC721.py)। +ইন্টারফেসের সংজ্ঞা Vyper-এর পরিবর্তে Python-এ লেখা হয়েছে, কারণ ইন্টারফেসগুলি শুধুমাত্র ব্লকচেইনের মধ্যেই ব্যবহৃত হয় না, বরং একটি বহিরাগত ক্লায়েন্ট থেকে ব্লকচেইনে লেনদেন পাঠানোর সময়ও ব্যবহৃত হয়, যা Python-এ লেখা হতে পারে। + +প্রথম লাইনটি ইন্টারফেস ইম্পোর্ট করে, এবং দ্বিতীয়টি নির্দিষ্ট করে যে আমরা এখানে এটি ইমপ্লিমেন্ট করছি। + +### ERC721Receiver ইন্টারফেস {#receiver-interface} + +```python +# safeTransferFrom() দ্বারা কল করা কন্ট্র্যাক্টের জন্য ইন্টারফেস +interface ERC721Receiver: + def onERC721Received( +``` + +ERC-721 দুই ধরনের ট্রান্সফার সমর্থন করে: + +- `transferFrom`, যা সেন্ডারকে যেকোনো গন্তব্যের অ্যাড্রেস নির্দিষ্ট করতে দেয় এবং ট্রান্সফারের দায়িত্ব সেন্ডারের উপর রাখে। এর মানে হল আপনি একটি অবৈধ অ্যাড্রেসে ট্রান্সফার করতে পারেন, সেক্ষেত্রে NFT চিরতরে হারিয়ে যাবে। +- `safeTransferFrom`, যা গন্তব্যের অ্যাড্রেসটি একটি কন্ট্র্যাক্ট কিনা তা পরীক্ষা করে। যদি তাই হয়, ERC-721 কন্ট্র্যাক্টটি গ্রহণকারী কন্ট্র্যাক্টকে জিজ্ঞাসা করে যে এটি NFT গ্রহণ করতে চায় কিনা। + +`safeTransferFrom` অনুরোধের উত্তর দিতে একটি গ্রহণকারী কন্ট্র্যাক্টকে `ERC721Receiver` ইমপ্লিমেন্ট করতে হবে। + +```python + _operator: address, + _from: address, +``` + +`_from` অ্যাড্রেসটি টোকেনের বর্তমান মালিক। `_operator` অ্যাড্রেসটি হল সেই ব্যক্তি যে ট্রান্সফারের জন্য অনুরোধ করেছে (allowances-এর কারণে এই দুটি এক নাও হতে পারে)। + +```python + _tokenId: uint256, +``` + +ERC-721 টোকেন ID গুলি 256 বিটের হয়। সাধারণত টোকেনটি যা উপস্থাপন করে তার একটি বিবরণের হ্যাশিং করে এগুলি তৈরি করা হয়। + +```python + _data: Bytes[1024] +``` + +অনুরোধে 1024 বাইট পর্যন্ত ব্যবহারকারীর ডেটা থাকতে পারে। + +```python + ) -> bytes32: view +``` + +একটি কন্ট্র্যাক্ট যাতে ভুলবশত কোনো ট্রান্সফার গ্রহণ না করে, তা প্রতিরোধ করার জন্য রিটার্ন ভ্যালুটি বুলিয়ান নয়, বরং একটি নির্দিষ্ট মান সহ 256 বিটের হয়। + +এই ফাংশনটি একটি `view`, যার মানে এটি ব্লকচেইনের স্টেট পড়তে পারে, কিন্তু এটি পরিবর্তন করতে পারে না। + +### ইভেন্ট {#events} + +[ইভেন্টগুলি](https://media.consensys.net/technical-introduction-to-events-and-logs-in-ethereum-a074d65dd61e) ব্লকচেইনের বাইরের ব্যবহারকারী এবং সার্ভারদের ইভেন্ট সম্পর্কে জানাতে নির্গত হয়। মনে রাখবেন যে ইভেন্টের বিষয়বস্তু ব্লকচেইনের কন্ট্র্যাক্টগুলির জন্য উপলব্ধ নয়। + +```python +# @dev যেকোনো মেকানিজমের মাধ্যমে কোনো NFT-এর মালিকানা পরিবর্তন হলে এটি নির্গত হয়। এই ইভেন্টটি তখন নির্গত হয় যখন NFT +# তৈরি হয় (`from` == 0) এবং ধ্বংস হয় (`to` == 0)। ব্যতিক্রম: কন্ট্র্যাক্ট তৈরির সময়, যেকোনো +# সংখ্যক NFT ট্রান্সফার নির্গত না করে তৈরি এবং অ্যাসাইন করা যেতে পারে। যেকোনো +# ট্রান্সফারের সময়, সেই NFT-এর জন্য অনুমোদিত অ্যাড্রেস (যদি থাকে) রিসেট করে কিছুই থাকে না। +# @param _from NFT-এর সেন্ডার (যদি অ্যাড্রেসটি জিরো অ্যাড্রেস হয় তবে এটি টোকেন তৈরি নির্দেশ করে)। +# @param _to NFT-এর প্রাপক (যদি অ্যাড্রেসটি জিরো অ্যাড্রেস হয় তবে এটি টোকেন ধ্বংস নির্দেশ করে)। +# @param _tokenId যে NFTটি ট্রান্সফার করা হয়েছে। +event Transfer: + sender: indexed(address) + receiver: indexed(address) + tokenId: indexed(uint256) +``` + +এটি ERC-20 ট্রান্সফার ইভেন্টের মতো, তবে এখানে আমরা একটি পরিমাণের পরিবর্তে একটি `tokenId` রিপোর্ট করি। +জিরো অ্যাড্রেসের মালিক কেউ নয়, তাই প্রথা অনুযায়ী আমরা এটি টোকেন তৈরি এবং ধ্বংস রিপোর্ট করতে ব্যবহার করি। + +```python +# @dev এটি নির্গত হয় যখন একটি NFT-এর জন্য অনুমোদিত অ্যাড্রেস পরিবর্তন বা পুনরায় নিশ্চিত করা হয়। জিরো +# অ্যাড্রেস নির্দেশ করে যে কোনো অনুমোদিত অ্যাড্রেস নেই। যখন একটি ট্রান্সফার ইভেন্ট নির্গত হয়, এটিও +# নির্দেশ করে যে সেই NFT-এর জন্য অনুমোদিত অ্যাড্রেস (যদি থাকে) রিসেট করে কিছুই থাকে না। +# @param _owner NFT-এর মালিক। +# @param _approved যে অ্যাড্রেসটি আমরা অনুমোদন করছি। +# @param _tokenId যে NFT-টি আমরা অনুমোদন করছি। +event Approval: + owner: indexed(address) + approved: indexed(address) + tokenId: indexed(uint256) +``` + +একটি ERC-721 অনুমোদন একটি ERC-20 অ্যালাওয়েন্সের মতো। একটি নির্দিষ্ট অ্যাড্রেস একটি নির্দিষ্ট টোকেন ট্রান্সফার করার অনুমতিপ্রাপ্ত। এটি কন্ট্র্যাক্টগুলিকে একটি টোকেন গ্রহণ করার সময় প্রতিক্রিয়া জানানোর জন্য একটি মেকানিজম দেয়। কন্ট্র্যাক্টগুলি ইভেন্টের জন্য অপেক্ষা করতে পারে না, তাই যদি আপনি কেবল তাদের কাছে টোকেন ট্রান্সফার করেন তবে তারা এটি সম্পর্কে "জানতে" পারে না। এইভাবে মালিক প্রথমে একটি অনুমোদন জমা দেয় এবং তারপরে কন্ট্র্যাক্টে একটি অনুরোধ পাঠায়: "আমি আপনাকে টোকেন +X ট্রান্সফার করার জন্য অনুমোদন দিয়েছি, অনুগ্রহ করে করুন..."। + +এটি ERC-721 স্ট্যান্ডার্ডকে ERC-20 স্ট্যান্ডার্ডের মতো করার জন্য একটি ডিজাইনের সিদ্ধান্ত। যেহেতু ERC-721 টোকেনগুলি নন-ফাঞ্জিবল, তাই একটি কন্ট্র্যাক্ট টোকেনের মালিকানা দেখেও শনাক্ত করতে পারে যে এটি একটি নির্দিষ্ট টোকেন পেয়েছে। + +```python +# @dev এটি তখন নির্গত হয় যখন একজন মালিকের জন্য একজন অপারেটর সক্রিয় বা নিষ্ক্রিয় করা হয়। অপারেটর পরিচালনা করতে পারে +# মালিকের সমস্ত NFT। +# @param _owner NFT-এর মালিক। +# @param _operator যে অ্যাড্রেসে আমরা অপারেটরের অধিকার সেট করছি। +# @param _approved অপারেটরের অধিকারের স্থিতি (সত্য যদি অপারেটরের অধিকার দেওয়া হয় এবং মিথ্যা যদি +# প্রত্যাহার করা হয়)। +event ApprovalForAll: + owner: indexed(address) + operator: indexed(address) + approved: bool +``` + +কখনো কখনো একজন _অপারেটর_ থাকা দরকারী, যে একটি নির্দিষ্ট ধরনের একটি অ্যাকাউন্টের সমস্ত টোকেন পরিচালনা করতে পারে (যেগুলো একটি নির্দিষ্ট কন্ট্র্যাক্ট দ্বারা পরিচালিত হয়), যা পাওয়ার অফ অ্যাটর্নির মতো। উদাহরণস্বরূপ, আমি এমন একটি কন্ট্র্যাক্টকে এই ক্ষমতা দিতে চাইতে পারি যা পরীক্ষা করে দেখবে যে আমি ছয় মাস ধরে এর সাথে যোগাযোগ করেছি কিনা, এবং যদি না করে থাকি, তাহলে আমার সম্পদ আমার উত্তরাধিকারীদের মধ্যে বিতরণ করবে (যদি তাদের মধ্যে কেউ এর জন্য জিজ্ঞাসা করে, কন্ট্র্যাক্টগুলো কোনো লেনদেন দ্বারা কল না করা পর্যন্ত কিছুই করতে পারে না)। ERC-20-তে আমরা উত্তরাধিকার কন্ট্র্যাক্টকে একটি উচ্চ অ্যালাওয়েন্স দিতে পারি, কিন্তু এটি ERC-721-এর জন্য কাজ করে না কারণ টোকেনগুলি নন-ফাঞ্জিবল। এটি তার সমতুল্য। + +`approved` মানটি আমাদের বলে যে ইভেন্টটি একটি অনুমোদনের জন্য, নাকি একটি অনুমোদন প্রত্যাহারের জন্য। + +### স্টেট ভ্যারিয়েবল {#state-vars} + +এই ভ্যারিয়েবলগুলিতে টোকেনগুলির বর্তমান স্টেট থাকে: কোনটি উপলব্ধ এবং কে তাদের মালিক। এগুলির বেশিরভাগই `HashMap` অবজেক্ট, [দুটি টাইপের মধ্যে বিদ্যমান একমুখী ম্যাপিং](https://vyper.readthedocs.io/en/latest/types.html#mappings)। + +```python +# @dev NFT ID থেকে সেটির মালিকের অ্যাড্রেসের ম্যাপিং। +idToOwner: HashMap[uint256, address] + +# @dev NFT ID থেকে অনুমোদিত অ্যাড্রেসের ম্যাপিং। +idToApprovals: HashMap[uint256, address] +``` + +Ethereum-এ ব্যবহারকারী এবং কন্ট্র্যাক্টের পরিচয় 160-বিটের অ্যাড্রেস দ্বারা উপস্থাপিত হয়। এই দুটি ভ্যারিয়েবল টোকেন ID থেকে তাদের মালিক এবং যারা তাদের ট্রান্সফার করার জন্য অনুমোদিত (প্রতিটির জন্য সর্বাধিক একজন) তাদের ম্যাপ করে। Ethereum-এ, ইনিশিয়ালাইজ না করা ডেটা সবসময় শূন্য থাকে, তাই যদি কোনো মালিক বা অনুমোদিত ট্রান্সফারকারী না থাকে তবে সেই টোকেনের মান শূন্য হয়। + +```python +# @dev মালিকের অ্যাড্রেস থেকে তার টোকেন সংখ্যার ম্যাপিং। +ownerToNFTokenCount: HashMap[address, uint256] +``` + +এই ভ্যারিয়েবলটি প্রতিটি মালিকের টোকেনের সংখ্যা ধরে রাখে। মালিকদের থেকে টোকেনের কোনো ম্যাপিং নেই, তাই কোনো নির্দিষ্ট মালিকের মালিকানাধীন টোকেনগুলি শনাক্ত করার একমাত্র উপায় হলো ব্লকচেইনের ইভেন্ট ইতিহাসে ফিরে দেখা এবং উপযুক্ত `Transfer` ইভেন্টগুলি দেখা। আমরা এই ভ্যারিয়েবলটি ব্যবহার করে জানতে পারি যে কখন আমাদের কাছে সমস্ত NFT আছে এবং সময়ের আরও পিছনে দেখার প্রয়োজন নেই। + +মনে রাখবেন যে এই অ্যালগরিদমটি শুধুমাত্র ব্যবহারকারী ইন্টারফেস এবং বহিরাগত সার্ভারের জন্য কাজ করে। ব্লকচেইনে চলমান কোড নিজে অতীতের ইভেন্টগুলি পড়তে পারে না। + +```python +# @dev মালিকের অ্যাড্রেস থেকে অপারেটর অ্যাড্রেসের ম্যাপিং-এর ম্যাপিং। +ownerToOperators: HashMap[address, HashMap[address, bool]] +``` + +একটি অ্যাকাউন্টে একাধিক অপারেটর থাকতে পারে। একটি সাধারণ `HashMap` তাদের ট্র্যাক রাখার জন্য অপর্যাপ্ত, কারণ প্রতিটি কী একটি একক মানের দিকে নিয়ে যায়। এর পরিবর্তে, আপনি মান হিসাবে `HashMap[address, bool]` ব্যবহার করতে পারেন। ডিফল্টরূপে প্রতিটি অ্যাড্রেসের জন্য মান `False` থাকে, যার মানে এটি একজন অপারেটর নয়। প্রয়োজন অনুযায়ী আপনি মানগুলিকে `True` তে সেট করতে পারেন। + +```python +# @dev মিন্টারের অ্যাড্রেস, যে একটি টোকেন মিন্ট করতে পারে +minter: address +``` + +নতুন টোকেন কোনো না কোনোভাবে তৈরি করতে হবে। এই কন্ট্র্যাক্টে একটি একক সত্তা রয়েছে যা এটি করার অনুমতিপ্রাপ্ত, সেটি হলো `minter`। উদাহরণস্বরূপ, একটি গেমের জন্য এটি যথেষ্ট হতে পারে। অন্যান্য উদ্দেশ্যে, একটি আরও জটিল ব্যবসায়িক যুক্তি তৈরি করা প্রয়োজন হতে পারে। + +```python +# @dev ইন্টারফেস আইডি থেকে বুলিয়ানের ম্যাপিং, এটি সমর্থিত কিনা। +supportedInterfaces: HashMap[bytes32, bool] + +# @dev ERC165 এর ERC165 ইন্টারফেস আইডি +ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a7 + +# @dev ERC721 এর ERC165 ইন্টারফেস আইডি +ERC721_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000080ac58cd +``` + +[ERC-165](https://eips.ethereum.org/EIPS/eip-165) একটি কন্ট্র্যাক্টের জন্য একটি মেকানিজম নির্দিষ্ট করে যা প্রকাশ করে যে অ্যাপ্লিকেশনগুলি কীভাবে এর সাথে যোগাযোগ করতে পারে, এবং এটি কোন ERC গুলি মেনে চলে। এই ক্ষেত্রে, কন্ট্র্যাক্টটি ERC-165 এবং ERC-721 মেনে চলে। + +### ফাংশন {#functions} + +এগুলি হলো সেই ফাংশন যা আসলে ERC-721 ইমপ্লিমেন্ট করে। + +#### কনস্ট্রাক্টর {#constructor} + +```python +@external +def __init__(): +``` + +Vyper-এ, Python-এর মতোই, কনস্ট্রাক্টর ফাংশনকে `__init__` বলা হয়। + +```python + """ + @dev কন্ট্র্যাক্ট কনস্ট্রাক্টর। + """ +``` + +Python এবং Vyper-এ, আপনি একটি মাল্টি-লাইন স্ট্রিং (যা `\"\"\"` দিয়ে শুরু এবং শেষ হয়) নির্দিষ্ট করে এবং এটিকে কোনোভাবে ব্যবহার না করেও একটি মন্তব্য তৈরি করতে পারেন। এই কমেন্টগুলিতে [NatSpec](https://vyper.readthedocs.io/en/latest/natspec.html) অন্তর্ভুক্ত থাকতে পারে। + +```python + self.supportedInterfaces[ERC165_INTERFACE_ID] = True + self.supportedInterfaces[ERC721_INTERFACE_ID] = True + self.minter = msg.sender +``` + +স্টেট ভ্যারিয়েবল অ্যাক্সেস করতে আপনি `self.` ব্যবহার করেন` (আবার, Python-এর মতোই)। + +#### ভিউ ফাংশন {#views} + +এগুলি এমন ফাংশন যা ব্লকচেইনের স্টেট পরিবর্তন করে না, এবং তাই যদি বাইরে থেকে কল করা হয় তবে বিনামূল্যে কার্যকর করা যেতে পারে। যদি ভিউ ফাংশনগুলি একটি কন্ট্র্যাক্ট দ্বারা কল করা হয় তবে সেগুলি এখনও প্রতিটি নোডে কার্যকর করতে হবে এবং তাই গ্যাসের খরচ হয়। + +```python +@view +@external +``` + +একটি ফাংশন সংজ্ঞার আগে এই কীওয়ার্ডগুলি যা একটি অ্যাট সাইন (`@`) দিয়ে শুরু হয়, তাদের _ডেকোরেশন_ বলা হয়। তারা নির্দিষ্ট করে যে কোন পরিস্থিতিতে একটি ফাংশন কল করা যেতে পারে। + +- `@view` নির্দিষ্ট করে যে এই ফাংশনটি একটি ভিউ। +- `@external` নির্দিষ্ট করে যে এই নির্দিষ্ট ফাংশনটি লেনদেন এবং অন্যান্য কন্ট্র্যাক্ট দ্বারা কল করা যেতে পারে। + +```python +def supportsInterface(_interfaceID: bytes32) -> bool: +``` + +Python-এর বিপরীতে, Vyper একটি [স্ট্যাটিক টাইপড ল্যাঙ্গুয়েজ](https://wikipedia.org/wiki/Type_system#Static_type_checking)। +[ডেটা টাইপ](https://vyper.readthedocs.io/en/latest/types.html) শনাক্ত না করে আপনি কোনো ভ্যারিয়েবল বা ফাংশন প্যারামিটার ঘোষণা করতে পারবেন না। এই ক্ষেত্রে ইনপুট প্যারামিটার হল `bytes32`, একটি 256-বিটের মান (256 বিট হল [Ethereum Virtual Machine](/developers/docs/evm/)-এর নেটিভ ওয়ার্ড সাইজ)। আউটপুটটি একটি বুলিয়ান মান। প্রথা অনুযায়ী, ফাংশন প্যারামিটারের নাম একটি আন্ডারস্কোর (`_`) দিয়ে শুরু হয়। + +```python + """ + @dev ইন্টারফেস শনাক্তকরণ ERC-165-এ নির্দিষ্ট করা আছে। + @param _interfaceID ইন্টারফেসের আইডি + """ + return self.supportedInterfaces[_interfaceID] +``` + +`self.supportedInterfaces` HashMap থেকে মানটি রিটার্ন করুন, যা কনস্ট্রাক্টরে (`__init__`) সেট করা হয়েছে। + +```python +### VIEW FUNCTIONS ### + +``` + +এগুলি এমন ভিউ ফাংশন যা ব্যবহারকারী এবং অন্যান্য কন্ট্র্যাক্টের জন্য টোকেন সম্পর্কে তথ্য উপলব্ধ করে। + +```python +@view +@external +def balanceOf(_owner: address) -> uint256: + """ + @dev `_owner`-এর মালিকানাধীন NFT-এর সংখ্যা ফেরত দেয়। + `_owner` যদি শূন্য অ্যাড্রেস হয় তবে থ্রো করে। শূন্য অ্যাড্রেসে অ্যাসাইন করা NFT গুলিকে অবৈধ বলে মনে করা হয়। + @param _owner যে অ্যাড্রেসের জন্য ব্যালেন্স জিজ্ঞাসা করতে হবে। + """ + assert _owner != ZERO_ADDRESS +``` + +এই লাইনটি [অ্যাসার্ট করে](https://vyper.readthedocs.io/en/latest/statements.html#assert) যে `_owner` শূন্য নয়। যদি এটি হয়, তাহলে একটি ত্রুটি হয় এবং অপারেশনটি প্রত্যাবর্তন করা হয়। + +```python + return self.ownerToNFTokenCount[_owner] + +@view +@external +def ownerOf(_tokenId: uint256) -> address: + """ + @dev NFT-এর মালিকের অ্যাড্রেস ফেরত দেয়। + `_tokenId` যদি একটি বৈধ NFT না হয় তবে থ্রো করে। + @param _tokenId একটি NFT-এর জন্য আইডেন্টিফায়ার। + """ + owner: address = self.idToOwner[_tokenId] + # `_tokenId` যদি একটি বৈধ NFT না হয় তবে থ্রো করে + assert owner != ZERO_ADDRESS + return owner +``` + +Ethereum Virtual Machine (evm)-এ যে কোনও স্টোরেজ যেখানে কোনও মান সংরক্ষণ করা হয়নি তা শূন্য থাকে। +যদি `_tokenId`-তে কোনো টোকেন না থাকে তাহলে `self.idToOwner[_tokenId]`-এর মান শূন্য। সেক্ষেত্রে ফাংশনটি রিভার্ট করে। + +```python +@view +@external +def getApproved(_tokenId: uint256) -> address: + """ + @dev একটি একক NFT-এর জন্য অনুমোদিত অ্যাড্রেস পান। + `_tokenId` যদি একটি বৈধ NFT না হয় তবে থ্রো করে। + @param _tokenId যে NFT-এর অনুমোদন জিজ্ঞাসা করতে হবে তার আইডি। + """ + # `_tokenId` যদি একটি বৈধ NFT না হয় তবে থ্রো করে + assert self.idToOwner[_tokenId] != ZERO_ADDRESS + return self.idToApprovals[_tokenId] +``` + +মনে রাখবেন যে `getApproved` শূন্য ফেরত দিতে _পারে_। যদি টোকেনটি বৈধ হয় তবে এটি `self.idToApprovals[_tokenId]` ফেরত দেয়। +যদি কোনো অনুমোদনকারী না থাকে তবে সেই মান শূন্য। + +```python +@view +@external +def isApprovedForAll(_owner: address, _operator: address) -> bool: + """ + @dev পরীক্ষা করে যে `_operator` `_owner`-এর জন্য একজন অনুমোদিত অপারেটর কিনা। + @param _owner যে অ্যাড্রেসটি NFT-এর মালিক। + @param _operator যে অ্যাড্রেসটি মালিকের পক্ষে কাজ করে। + """ + return (self.ownerToOperators[_owner])[_operator] +``` + +এই ফাংশনটি পরীক্ষা করে যে `_operator`-কে এই কন্ট্র্যাক্টে `_owner`-এর সমস্ত টোকেন পরিচালনা করার অনুমতি আছে কিনা। +যেহেতু একাধিক অপারেটর থাকতে পারে, এটি একটি দুই-স্তরের HashMap। + +#### ট্রান্সফার হেল্পার ফাংশন {#transfer-helpers} + +এই ফাংশনগুলি টোকেন ট্রান্সফার বা পরিচালনার অংশ এমন অপারেশনগুলি ইমপ্লিমেন্ট করে। + +```python + +### TRANSFER FUNCTION HELPERS ### + +@view +@internal +``` + +এই ডেকোরেশন, `@internal` এর মানে হল যে ফাংশনটি শুধুমাত্র একই কন্ট্র্যাক্টের মধ্যে অন্যান্য ফাংশন থেকে অ্যাক্সেসযোগ্য। প্রথা অনুযায়ী, এই ফাংশনের নামগুলিও একটি আন্ডারস্কোর (`_`) দিয়ে শুরু হয়। + +```python +def _isApprovedOrOwner(_spender: address, _tokenId: uint256) -> bool: + """ + @dev প্রদত্ত স্পেন্ডার একটি প্রদত্ত টোকেন আইডি ট্রান্সফার করতে পারে কিনা তা ফেরত দেয় + @param spender যে স্পেন্ডারের অ্যাড্রেসটি জিজ্ঞাসা করা হবে + @param tokenId uint256 ট্রান্সফার করা হবে এমন টোকেনের আইডি + @return bool msg.sender প্রদত্ত টোকেন আইডির জন্য অনুমোদিত কিনা, + মালিকের একজন অপারেটর, বা টোকেনের মালিক কিনা + """ + owner: address = self.idToOwner[_tokenId] + spenderIsOwner: bool = owner == _spender + spenderIsApproved: bool = _spender == self.idToApprovals[_tokenId] + spenderIsApprovedForAll: bool = (self.ownerToOperators[owner])[_spender] + return (spenderIsOwner or spenderIsApproved) or spenderIsApprovedForAll +``` + +একটি অ্যাড্রেসকে টোকেন ট্রান্সফার করার অনুমতি দেওয়ার তিনটি উপায় আছে: + +1. অ্যাড্রেসটি টোকেনের মালিক +2. অ্যাড্রেসটি সেই টোকেনটি খরচ করার জন্য অনুমোদিত +3. অ্যাড্রেসটি টোকেনের মালিকের জন্য একজন অপারেটর + +উপরের ফাংশনটি একটি ভিউ হতে পারে কারণ এটি স্টেট পরিবর্তন করে না। অপারেটিং খরচ কমাতে, যেকোনো ফাংশন যা ভিউ হতে পারে তা ভিউ _হওয়া উচিত_। + +```python +@internal +def _addTokenTo(_to: address, _tokenId: uint256): + """ + @dev একটি প্রদত্ত অ্যাড্রেসে একটি NFT যোগ করুন + যদি `_tokenId` কারও মালিকানাধীন থাকে তবে থ্রো করে। + """ + # যদি `_tokenId` কারও মালিকানাধীন থাকে তবে থ্রো করে + assert self.idToOwner[_tokenId] == ZERO_ADDRESS + # মালিক পরিবর্তন করুন + self.idToOwner[_tokenId] = _to + # গণনা ট্র্যাকিং পরিবর্তন করুন + self.ownerToNFTokenCount[_to] += 1 + + +@internal +def _removeTokenFrom(_from: address, _tokenId: uint256): + """ + @dev একটি প্রদত্ত অ্যাড্রেস থেকে একটি NFT সরান + যদি `_from` বর্তমান মালিক না হয় তবে থ্রো করে। + """ + # যদি `_from` বর্তমান মালিক না হয় তবে থ্রো করে + assert self.idToOwner[_tokenId] == _from + # মালিক পরিবর্তন করুন + self.idToOwner[_tokenId] = ZERO_ADDRESS + # গণনা ট্র্যাকিং পরিবর্তন করুন + self.ownerToNFTokenCount[_from] -= 1 +``` + +ট্রান্সফারে সমস্যা হলে আমরা কলটি রিভার্ট করি। + +```python +@internal +def _clearApproval(_owner: address, _tokenId: uint256): + """ + @dev একটি প্রদত্ত অ্যাড্রেসের একটি অনুমোদন পরিষ্কার করুন + যদি `_owner` বর্তমান মালিক না হয় তবে থ্রো করে। + """ + # যদি `_owner` বর্তমান মালিক না হয় তবে থ্রো করে + assert self.idToOwner[_tokenId] == _owner + if self.idToApprovals[_tokenId] != ZERO_ADDRESS: + # অনুমোদন রিসেট করুন + self.idToApprovals[_tokenId] = ZERO_ADDRESS +``` + +প্রয়োজন হলেই কেবল মান পরিবর্তন করুন। স্টেট ভ্যারিয়েবলগুলি স্টোরেজে থাকে। স্টোরেজে লেখা EVM (Ethereum Virtual Machine) এর অন্যতম ব্যয়বহুল অপারেশন ([গ্যাস](/developers/docs/gas/) এর পরিপ্রেক্ষিতে)। সুতরাং, এটি কমানো একটি ভাল ধারণা, এমনকি বিদ্যমান মান লেখারও একটি উচ্চ খরচ আছে। + +```python +@internal +def _transferFrom(_from: address, _to: address, _tokenId: uint256, _sender: address): + """ + @dev একটি NFT-এর ট্রান্সফার কার্যকর করুন। + `msg.sender` যদি বর্তমান মালিক, একজন অনুমোদিত অপারেটর, বা এই NFT-এর জন্য অনুমোদিত + অ্যাড্রেস না হয় তবে থ্রো করে। (দ্রষ্টব্য: `msg.sender` ব্যক্তিগত ফাংশনে অনুমোদিত নয় তাই `_sender` পাস করুন।) + যদি `_to` শূন্য অ্যাড্রেস হয় তবে থ্রো করে। + যদি `_from` বর্তমান মালিক না হয় তবে থ্রো করে। + যদি `_tokenId` একটি বৈধ NFT না হয় তবে থ্রো করে। + """ +``` + +আমাদের এই অভ্যন্তরীণ ফাংশনটি আছে কারণ টোকেন ট্রান্সফার করার দুটি উপায় আছে (নিয়মিত এবং নিরাপদ), কিন্তু অডিটিং সহজ করার জন্য আমরা কোডের শুধুমাত্র একটি স্থানে এটি করতে চাই। + +```python + # প্রয়োজনীয়তা পরীক্ষা করুন + assert self._isApprovedOrOwner(_sender, _tokenId) + # `_to` যদি শূন্য ঠিকানা হয় তবে থ্রো করে + assert _to != ZERO_ADDRESS + # অনুমোদন সাফ করুন। `_from` যদি বর্তমান মালিক না হয় তবে থ্রো করে + self._clearApproval(_from, _tokenId) + # NFT সরান। `_tokenId` যদি একটি বৈধ NFT না হয় তবে থ্রো করে + self._removeTokenFrom(_from, _tokenId) + # NFT যোগ করুন + self._addTokenTo(_to, _tokenId) + # স্থানান্তরটি লগ করুন + log Transfer(_from, _to, _tokenId) +``` + +Vyper-এ একটি ইভেন্ট নির্গত করতে আপনি একটি `log` স্টেটমেন্ট ব্যবহার করেন ([আরো বিস্তারিত জানার জন্য এখানে দেখুন](https://vyper.readthedocs.io/en/latest/event-logging.html#event-logging))। + +#### ট্রান্সফার ফাংশন {#transfer-funs} + +```python + +### TRANSFER FUNCTIONS ### + +@external +def transferFrom(_from: address, _to: address, _tokenId: uint256): + """ + @dev `msg.sender` যদি বর্তমান মালিক, একজন অনুমোদিত অপারেটর বা এই NFT-এর জন্য অনুমোদিত ঠিকানা না হয় তবে থ্রো করে। + যদি `_from` বর্তমান মালিক না হয় তবে থ্রো করে। + যদি `_to` শূন্য ঠিকানা হয় তবে থ্রো করে। + যদি `_tokenId` একটি বৈধ NFT না হয় তবে থ্রো করে। + @notice `_to` NFT গ্রহণ করতে সক্ষম কিনা তা নিশ্চিত করার জন্য কলার দায়ী, অন্যথায় সেগুলি স্থায়ীভাবে হারিয়ে যেতে পারে। + @param _from NFT-এর বর্তমান মালিক। + @param _to নতুন মালিক। + @param _tokenId যে NFTটি স্থানান্তর করতে হবে। + """ + self._transferFrom(_from, _to, _tokenId, msg.sender) +``` + +এই ফাংশনটি আপনাকে একটি নির্বিচারী ঠিকানায় স্থানান্তর করতে দেয়। যদি না ঠিকানাটি একজন ব্যবহারকারী, বা একটি চুক্তি যা টোকেন স্থানান্তর করতে জানে, তবে আপনি যে কোনও টোকেন স্থানান্তর করবেন তা সেই ঠিকানায় আটকে যাবে এবং অকেজো হয়ে যাবে। + +```python +@external +def safeTransferFrom( + _from: address, + _to: address, + _tokenId: uint256, + _data: Bytes[1024]=b"" + ): + """ + @dev একটি NFT-এর মালিকানা এক ঠিকানা থেকে অন্য ঠিকানায় স্থানান্তর করে। + `msg.sender` যদি বর্তমান মালিক, একজন অনুমোদিত অপারেটর, বা এই NFT-এর জন্য অনুমোদিত ঠিকানা না হয় তবে থ্রো করে। + যদি `_from` বর্তমান মালিক না হয় তবে থ্রো করে। + যদি `_to` শূন্য ঠিকানা হয় তবে থ্রো করে। + যদি `_tokenId` একটি বৈধ NFT না হয় তবে থ্রো করে। + যদি `_to` একটি স্মার্ট চুক্তি হয়, তবে এটি `_to`-তে `onERC721Received` কল করে এবং রিটার্ন মান `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` না হলে থ্রো করে। + দ্রষ্টব্য: bytes4 প্যাডিং সহ bytes32 দ্বারা প্রতিনিধিত্ব করা হয় + @param _from NFT-এর বর্তমান মালিক। + @param _to নতুন মালিক। + @param _tokenId যে NFTটি স্থানান্তর করতে হবে। + @param _data কোনও নির্দিষ্ট বিন্যাস ছাড়াই অতিরিক্ত ডেটা, যা `_to`-তে কলে পাঠানো হয়। + """ + self._transferFrom(_from, _to, _tokenId, msg.sender) +``` + +প্রথমে স্থানান্তর করা ঠিক আছে কারণ যদি কোনও সমস্যা হয় তবে আমরা যাইহোক রিভার্ট করব, তাই কলে করা সবকিছু বাতিল হয়ে যাবে। + +```python + if _to.is_contract: # `_to` একটি চুক্তি ঠিকানা কিনা তা পরীক্ষা করুন +``` + +প্রথমে ঠিকানাটি একটি চুক্তি কিনা তা পরীক্ষা করুন (যদি এতে কোড থাকে)। যদি না হয়, তবে ধরে নিন এটি একটি ব্যবহারকারীর ঠিকানা এবং ব্যবহারকারী টোকেনটি ব্যবহার করতে বা স্থানান্তর করতে পারবে। কিন্তু এটি আপনাকে একটি মিথ্যা নিরাপত্তার অনুভূতিতে ঘুম পাড়িয়ে দেবেন না। আপনি `safeTransferFrom` দিয়েও টোকেন হারাতে পারেন, যদি আপনি সেগুলি এমন একটি ঠিকানায় স্থানান্তর করেন যার ব্যক্তিগত কী কেউ জানে না। + +```python + returnValue: bytes32 = ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) +``` + +লক্ষ্য চুক্তিটি ERC-721 টোকেন গ্রহণ করতে পারে কিনা তা দেখতে কল করুন। + +```python + # যদি স্থানান্তর গন্তব্য এমন একটি চুক্তি হয় যা 'onERC721Received' প্রয়োগ করে না তবে থ্রো করে + assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes32) +``` + +যদি গন্তব্য একটি চুক্তি হয়, কিন্তু এমন একটি যা ERC-721 টোকেন গ্রহণ করে না (বা যা এই বিশেষ স্থানান্তরটি গ্রহণ না করার সিদ্ধান্ত নিয়েছে), তবে রিভার্ট করুন। + +```python +@external +def approve(_approved: address, _tokenId: uint256): + """ + @dev একটি NFT-এর জন্য অনুমোদিত ঠিকানা সেট করুন বা পুনরায় নিশ্চিত করুন। শূন্য ঠিকানা নির্দেশ করে যে কোনও অনুমোদিত ঠিকানা নেই। + `msg.sender` যদি বর্তমান NFT মালিক বা বর্তমান মালিকের একজন অনুমোদিত অপারেটর না হয় তবে থ্রো করে। + যদি `_tokenId` একটি বৈধ NFT না হয় তবে থ্রো করে। (দ্রষ্টব্য: এটি EIP-তে লেখা নেই) + যদি `_approved` বর্তমান মালিক হয় তবে থ্রো করে। (দ্রষ্টব্য: এটি EIP-তে লেখা নেই) + @param _approved প্রদত্ত NFT আইডির জন্য অনুমোদিত হতে যাওয়া ঠিকানা। + @param _tokenId অনুমোদিত হতে যাওয়া টোকেনের আইডি। + """ + owner: address = self.idToOwner[_tokenId] + # `_tokenId` যদি একটি বৈধ NFT না হয় তবে থ্রো করে + assert owner != ZERO_ADDRESS + # `_approved` যদি বর্তমান মালিক হয় তবে থ্রো করে + assert _approved != owner +``` + +প্রথা অনুযায়ী যদি আপনি কোনও অনুমোদনকারী না রাখতে চান তবে আপনি শূন্য ঠিকানা নিয়োগ করেন, নিজেকে নয়। + +```python + # প্রয়োজনীয়তা পরীক্ষা করুন + senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender + senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender] + assert (senderIsOwner or senderIsApprovedForAll) +``` + +একটি অনুমোদন সেট করতে আপনি হয় মালিক হতে পারেন, অথবা মালিক দ্বারা অনুমোদিত একজন অপারেটর হতে পারেন। + +```python + # অনুমোদন সেট করুন + self.idToApprovals[_tokenId] = _approved + log Approval(owner, _approved, _tokenId) + + +@external +def setApprovalForAll(_operator: address, _approved: bool): + """ + @dev `msg.sender`-এর সমস্ত সম্পদ পরিচালনার জন্য একটি তৃতীয় পক্ষের ("অপারেটর") জন্য অনুমোদন সক্ষম বা অক্ষম করে। এটি ApprovalForAll ইভেন্টও নির্গত করে। + যদি `_operator` `msg.sender` হয় তবে থ্রো করে। (দ্রষ্টব্য: এটি EIP-তে লেখা নেই) + @notice এটি কাজ করে এমনকি যদি প্রেরক সেই সময়ে কোনও টোকেনের মালিক নাও হন। + @param _operator অনুমোদিত অপারেটরদের সেটে যোগ করার জন্য ঠিকানা। + @param _approved অপারেটর অনুমোদিত হলে সত্য, অনুমোদন প্রত্যাহার করতে মিথ্যা। + """ + # `_operator` যদি `msg.sender` হয় তবে থ্রো করে + assert _operator != msg.sender + self.ownerToOperators[msg.sender][_operator] = _approved + log ApprovalForAll(msg.sender, _operator, _approved) +``` + +#### নতুন টোকেন মিন্ট করুন এবং বিদ্যমানগুলি ধ্বংস করুন {#mint-burn} + +যে অ্যাকাউন্টটি চুক্তিটি তৈরি করেছে সেটি হল `মিন্টার`, সুপার ব্যবহারকারী যা নতুন এনএফটি মিন্ট করার জন্য অনুমোদিত। তবে, এমনকি এটিও বিদ্যমান টোকেনগুলি পোড়াতে অনুমোদিত নয়। কেবল মালিক, বা মালিক দ্বারা অনুমোদিত একটি সত্তা এটি করতে পারে। + +```python +### MINT & BURN FUNCTIONS ### + +@external +def mint(_to: address, _tokenId: uint256) -> bool: +``` + +এই ফাংশনটি সর্বদা `সত্য` প্রদান করে, কারণ অপারেশন ব্যর্থ হলে এটি প্রত্যাবর্তন করা হয়। + +```python + """ + @dev টোকেন মিন্ট করার ফাংশন + `msg.sender` যদি মিন্টার না হয় তবে থ্রো করে। + যদি `_to` শূন্য ঠিকানা হয় তবে থ্রো করে। + যদি `_tokenId` কারও মালিকানাধীন থাকে তবে থ্রো করে। + @param _to যে ঠিকানাটি মিন্ট করা টোকেনগুলি গ্রহণ করবে। + @param _tokenId মিন্ট করার জন্য টোকেন আইডি। + @return একটি বুলিয়ান যা নির্দেশ করে যে অপারেশনটি সফল হয়েছে কিনা। + """ + # `msg.sender` যদি মিন্টার না হয় তবে থ্রো করে + assert msg.sender == self.minter +``` + +শুধুমাত্র মিন্টার (যে অ্যাকাউন্টটি ERC-721 চুক্তিটি তৈরি করেছে) নতুন টোকেন মিন্ট করতে পারে। ভবিষ্যতে যদি আমরা মিন্টারের পরিচয় পরিবর্তন করতে চাই তবে এটি একটি সমস্যা হতে পারে। একটি প্রোডাকশন চুক্তিতে আপনি সম্ভবত এমন একটি ফাংশন চাইবেন যা মিন্টারকে মিন্টার সুবিধা অন্য কাউকে স্থানান্তর করতে দেয়। + +```python + # `_to` যদি শূন্য ঠিকানা হয় তবে থ্রো করে + assert _to != ZERO_ADDRESS + # NFT যোগ করুন। `_tokenId` যদি কারও মালিকানাধীন থাকে তবে থ্রো করে + self._addTokenTo(_to, _tokenId) + log Transfer(ZERO_ADDRESS, _to, _tokenId) + return True +``` + +প্রথা অনুযায়ী, নতুন টোকেন মিন্ট করা শূন্য ঠিকানা থেকে একটি স্থানান্তর হিসাবে গণ্য হয়। + +```python + +@external +def burn(_tokenId: uint256): + """ + @dev একটি নির্দিষ্ট ERC721 টোকেন পোড়ায়। + `msg.sender` যদি বর্তমান মালিক, একজন অনুমোদিত অপারেটর, বা এই NFT-এর জন্য অনুমোদিত ঠিকানা না হয় তবে থ্রো করে। + যদি `_tokenId` একটি বৈধ NFT না হয় তবে থ্রো করে। + @param _tokenId পোড়ানোর জন্য ERC721 টোকেনের uint256 আইডি। + """ + # প্রয়োজনীয়তা পরীক্ষা করুন + assert self._isApprovedOrOwner(msg.sender, _tokenId) + owner: address = self.idToOwner[_tokenId] + # `_tokenId` যদি একটি বৈধ NFT না হয় তবে থ্রো করে + assert owner != ZERO_ADDRESS + self._clearApproval(owner, _tokenId) + self._removeTokenFrom(owner, _tokenId) + log Transfer(owner, ZERO_ADDRESS, _tokenId) +``` + +যেকেউ যে একটি টোকেন স্থানান্তর করতে অনুমোদিত, সে এটি পোড়াতেও অনুমোদিত। যদিও একটি পোড়ানো শূন্য ঠিকানায় স্থানান্তরের সমতুল্য বলে মনে হয়, শূন্য ঠিকানা আসলে টোকেনটি গ্রহণ করে না। এটি আমাদের টোকেনের জন্য ব্যবহৃত সমস্ত স্টোরেজ মুক্ত করতে দেয়, যা লেনদেনের গ্যাস খরচ কমাতে পারে। + +## এই চুক্তিটি ব্যবহার করা {#using-contract} + +Solidity-এর বিপরীতে, Vyper-এর ইনহেরিটেন্স নেই। এটি একটি ইচ্ছাকৃত ডিজাইনের সিদ্ধান্ত যা কোডকে আরও স্পষ্ট এবং সুরক্ষিত করা সহজ করে তোলে। তাই আপনার নিজের Vyper ERC-721 চুক্তি তৈরি করতে আপনি [এই চুক্তিটি](https://github.com/vyperlang/vyper/blob/master/examples/tokens/ERC721.vy) নিন এবং আপনার কাঙ্খিত ব্যবসায়িক যুক্তি প্রয়োগ করতে এটি পরিবর্তন করুন। + +## উপসংহার {#conclusion} + +পর্যালোচনার জন্য, এই চুক্তির কয়েকটি সবচেয়ে গুরুত্বপূর্ণ ধারণা এখানে দেওয়া হল: + +- নিরাপদ ট্রান্সফারের মাধ্যমে ERC-721 টোকেন পেতে, কন্ট্র্যাক্টগুলিকে `ERC721Receiver` ইন্টারফেসটি ইমপ্লিমেন্ট করতে হবে। +- এমনকি যদি আপনি নিরাপদ ট্রান্সফার ব্যবহার করেন, তবুও টোকেনগুলি আটকে যেতে পারে যদি আপনি সেগুলিকে এমন একটি অ্যাড্রেসে পাঠান যার প্রাইভেট কি অজানা। +- যখন কোনও অপারেশনে সমস্যা হয় তখন কেবল একটি ব্যর্থতার মান ফেরত দেওয়ার পরিবর্তে কলটি `রিভার্ট` করা একটি ভাল ধারণা। +- ERC-721 টোকেনগুলির অস্তিত্ব থাকে যখন তাদের একজন মালিক থাকে। +- একটি NFT স্থানান্তর করার জন্য অনুমোদিত হওয়ার তিনটি উপায় আছে। আপনি মালিক হতে পারেন, একটি নির্দিষ্ট টোকেনের জন্য অনুমোদিত হতে পারেন, অথবা মালিকের সমস্ত টোকেনের জন্য একজন অপারেটর হতে পারেন। +- অতীতের ইভেন্টগুলি কেবল ব্লকচেইনের বাইরে দৃশ্যমান। ব্লকচেইনের ভিতরে চলমান কোড সেগুলি দেখতে পারে না। + +এখন যান এবং সুরক্ষিত Vyper চুক্তিগুলি প্রয়োগ করুন। + +[আমার আরও কাজের জন্য এখানে দেখুন](https://cryptodocguy.pro/)। + diff --git a/public/content/translations/bn/developers/tutorials/erc20-annotated-code/index.md b/public/content/translations/bn/developers/tutorials/erc20-annotated-code/index.md new file mode 100644 index 00000000000..b68cf3f471c --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/erc20-annotated-code/index.md @@ -0,0 +1,814 @@ +--- +title: "ERC-20 কন্ট্র্যাক্ট ওয়াক-থ্রু" +description: "OpenZeppelin ERC-20 কন্ট্র্যাক্টের ভিতরে কী আছে এবং কেন এটি সেখানে আছে?" +author: Ori Pomerantz +lang: bn +tags: [ "সলিডিটি", "erc-20" ] +skill: beginner +published: 2021-03-09 +--- + +## ভূমিকা {#introduction} + +Ethereum-এর অন্যতম সাধারণ ব্যবহার হল একটি গোষ্ঠীর জন্য একটি ট্রেডযোগ্য টোকেন তৈরি করা, এক অর্থে তাদের নিজস্ব মুদ্রা। এই টোকেনগুলি সাধারণত একটি স্ট্যান্ডার্ড অনুসরণ করে, +[ERC-20](/developers/docs/standards/tokens/erc-20/)। এই স্ট্যান্ডার্ডটি লিকুইডিটি পুল এবং ওয়ালেটের মতো টুলস লেখা সম্ভব করে, যা সমস্ত ERC-20 টোকেনের সাথে কাজ করে। এই নিবন্ধে আমরা +[OpenZeppelin সলিডিটি ERC20 ইমপ্লিমেন্টেশন](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol), এবং +[ইন্টারফেস সংজ্ঞা](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol) বিশ্লেষণ করব। + +এটি একটি টীকাযুক্ত সোর্স কোড। আপনি যদি ERC-20 ইমপ্লিমেন্ট করতে চান, +[এই টিউটোরিয়ালটি পড়ুন](https://docs.openzeppelin.com/contracts/2.x/erc20-supply)। + +## ইন্টারফেস {#the-interface} + +ERC-20-এর মতো একটি স্ট্যান্ডার্ডের উদ্দেশ্য হল অনেক টোকেন ইমপ্লিমেন্টেশনকে অনুমতি দেওয়া যা ওয়ালেট এবং ডিসেন্ট্রালাইজড এক্সচেঞ্জের মতো অ্যাপ্লিকেশন জুড়ে ইন্টারঅপারেবল। এটি অর্জন করার জন্য, আমরা একটি +[ইন্টারফেস](https://www.geeksforgeeks.org/solidity/solidity-basics-of-interface/) তৈরি করি। যেকোনো কোড যা টোকেন কন্ট্র্যাক্ট ব্যবহার করতে চায়, তা ইন্টারফেসের একই সংজ্ঞা ব্যবহার করতে পারে এবং এটি ব্যবহার করে এমন সমস্ত টোকেন কন্ট্র্যাক্টের সাথে সামঞ্জস্যপূর্ণ হতে পারে, তা MetaMask-এর মতো একটি ওয়ালেট হোক, etherscan.io-এর মতো একটি ডিএ্যাপ হোক বা লিকুইডিটি পুলের মতো ভিন্ন কোনো কন্ট্র্যাক্ট হোক। + +![ERC-20 ইন্টারফেসের চিত্রণ](erc20_interface.png) + +আপনি যদি একজন অভিজ্ঞ প্রোগ্রামার হন, আপনার সম্ভবত [Java](https://www.w3schools.com/java/java_interface.asp) বা এমনকি [C হেডার ফাইলগুলিতে](https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html)-ও একই ধরনের গঠন দেখতে পাওয়ার কথা মনে আছে। + +এটি OpenZeppelin থেকে প্রাপ্ত [ERC-20 ইন্টারফেস](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol)-এর একটি সংজ্ঞা। এটি [মানুষের পাঠযোগ্য স্ট্যান্ডার্ড](https://eips.ethereum.org/EIPS/eip-20)-কে সলিডিটি কোডে অনুবাদ। অবশ্যই, +ইন্টারফেসটি নিজে থেকে কিছু _কীভাবে_ করতে হয় তা নির্ধারণ করে না। সেটি নিচের কন্ট্র্যাক্ট সোর্স কোডে ব্যাখ্যা করা হয়েছে। + +  + +```solidity +// SPDX-License-Identifier: MIT +``` + +সলিডিটি ফাইলগুলিতে একটি লাইসেন্স আইডেন্টিফায়ার অন্তর্ভুক্ত থাকার কথা। [আপনি এখানে লাইসেন্সের তালিকা দেখতে পারেন](https://spdx.org/licenses/)। আপনার যদি একটি ভিন্ন +লাইসেন্সের প্রয়োজন হয়, তবে কেবল কমেন্টে তা ব্যাখ্যা করুন। + +  + +```solidity +pragma solidity >=0.6.0 <0.8.0; +``` + +সলিডিটি ভাষা এখনও দ্রুত বিকশিত হচ্ছে এবং নতুন সংস্করণগুলি পুরানো কোডের সাথে সামঞ্জস্যপূর্ণ নাও হতে পারে +([এখানে দেখুন](https://docs.soliditylang.org/en/v0.7.0/070-breaking-changes.html))। অতএব, শুধুমাত্র ভাষার একটি ন্যূনতম সংস্করণই নয়, বরং একটি সর্বোচ্চ সংস্করণও উল্লেখ করা একটি ভাল ধারণা, যেটির সাথে আপনি কোডটি পরীক্ষা করেছেন। + +  + +```solidity +/** + * @dev EIP-তে সংজ্ঞায়িত ERC20 স্ট্যান্ডার্ডের ইন্টারফেস। + */ +``` + +কমেন্টের মধ্যে `@dev` হল [NatSpec ফরম্যাটের](https://docs.soliditylang.org/en/develop/natspec-format.html) অংশ, যা সোর্স কোড থেকে +ডকুমেন্টেশন তৈরি করতে ব্যবহৃত হয়। + +  + +```solidity +interface IERC20 { +``` + +প্রচলিত নিয়ম অনুযায়ী, ইন্টারফেসের নাম `I` দিয়ে শুরু হয়। + +  + +```solidity + /** + * @dev অস্তিত্বে থাকা টোকেনের পরিমাণ ফেরত দেয়। + */ + function totalSupply() external view returns (uint256); +``` + +এই ফাংশনটি `external`, যার অর্থ [এটি শুধুমাত্র কন্ট্র্যাক্টের বাইরে থেকে কল করা যেতে পারে](https://docs.soliditylang.org/en/v0.7.0/cheatsheet.html#index-2)। +এটি কন্ট্র্যাক্টে টোকেনের মোট সরবরাহ ফেরত দেয়। এই মানটি ইথেরিয়ামের সবচেয়ে সাধারণ টাইপ, আনসাইন্ড 256 বিট ব্যবহার করে ফেরত দেওয়া হয় (256 বিট হল EVM-এর +নেটিভ ওয়ার্ড সাইজ)। এই ফাংশনটি একটি `view`ও, যার অর্থ এটি স্টেট পরিবর্তন করে না, তাই এটি ব্লকচেইনের প্রতিটি নোডে চালানোর পরিবর্তে একটি একক নোডে এক্সিকিউট করা যেতে পারে। এই ধরনের ফাংশন কোনো লেনদেন তৈরি করে না এবং কোনো [গ্যাস](/developers/docs/gas/) খরচ করে না। + +**দ্রষ্টব্য:** তত্ত্বগতভাবে এমনটা মনে হতে পারে যে কোনও কন্ট্র্যাক্টের নির্মাতা প্রকৃত মানের চেয়ে কম মোট সরবরাহ ফেরত দিয়ে প্রতারণা করতে পারে, যার ফলে প্রতিটি টোকেন তার প্রকৃত মূল্যের চেয়ে বেশি মূল্যবান বলে মনে হবে। যাইহোক, এই ভয় ব্লকচেইনের আসল প্রকৃতিকে উপেক্ষা করে। ব্লকচেইনে যা কিছু ঘটে তা প্রতিটি নোড দ্বারা যাচাই করা যেতে পারে। এটি অর্জন করার জন্য, প্রতিটি কন্ট্র্যাক্টের মেশিন ল্যাঙ্গুয়েজ কোড এবং স্টোরেজ প্রতিটি নোডে উপলব্ধ। যদিও আপনার কন্ট্র্যাক্টের জন্য সলিডিটি +কোড প্রকাশ করা আপনার জন্য আবশ্যক নয়, আপনি যদি সোর্স কোড এবং যে সলিডিটি সংস্করণ দিয়ে এটি কম্পাইল করা হয়েছিল তা প্রকাশ না করেন তবে কেউ আপনাকে গুরুত্ব সহকারে নেবে না, যাতে এটি আপনার দেওয়া মেশিন ল্যাঙ্গুয়েজ কোডের সাথে যাচাই করা যায়। +উদাহরণস্বরূপ, [এই কন্ট্র্যাক্টটি দেখুন](https://eth.blockscout.com/address/0xa530F85085C6FE2f866E7FdB716849714a89f4CD?tab=contract)। + +  + +```solidity + /** + * @dev `account`-এর মালিকানাধীন টোকেনের পরিমাণ ফেরত দেয়। + */ + function balanceOf(address account) external view returns (uint256); +``` + +নাম শুনেই বোঝা যাচ্ছে, `balanceOf` একটি অ্যাকাউন্টের ব্যালেন্স ফেরত দেয়। ইথেরিয়াম অ্যাকাউন্টগুলি সলিডিটিতে `address` টাইপ ব্যবহার করে চিহ্নিত করা হয়, যা 160 বিট ধারণ করে। +এটি `external` এবং `view`ও বটে। + +  + +```solidity + /** + * @dev কলারের অ্যাকাউন্ট থেকে `recipient`-কে `amount` পরিমাণ টোকেন পাঠায়। + * + * অপারেশনটি সফল হয়েছে কিনা তা নির্দেশ করে একটি বুলিয়ান মান ফেরত দেয়। + * + * একটি {Transfer} ইভেন্ট নির্গত করে। + */ + function transfer(address recipient, uint256 amount) external returns (bool); +``` + +`transfer` ফাংশন কলার থেকে একটি ভিন্ন অ্যাড্রেসে টোকেন স্থানান্তর করে। এতে স্টেটের পরিবর্তন জড়িত, তাই এটি একটি `view` নয়। +যখন একজন ব্যবহারকারী এই ফাংশনটি কল করেন, তখন এটি একটি লেনদেন তৈরি করে এবং গ্যাস খরচ হয়। এটি একটি ইভেন্ট, `Transfer`ও নির্গত করে, যাতে ব্লকচেইনের সবাইকে এই ইভেন্ট সম্পর্কে জানানো যায়। + +ফাংশনটির দুটি ভিন্ন ধরনের কলারদের জন্য দুই ধরনের আউটপুট রয়েছে: + +- যে ব্যবহারকারীরা সরাসরি একটি ইউজার ইন্টারফেস থেকে ফাংশনটি কল করে। সাধারণত ব্যবহারকারী একটি লেনদেন জমা দেয় + এবং প্রতিক্রিয়ার জন্য অপেক্ষা করে না, যা একটি অনির্দিষ্ট পরিমাণ সময় নিতে পারে। ব্যবহারকারী লেনদেনের রসিদ (যা লেনদেনের হ্যাস দ্বারা চিহ্নিত করা হয়) বা + `Transfer` ইভেন্টটি খুঁজে দেখতে পারে যে কী ঘটেছে। +- অন্যান্য কন্ট্র্যাক্ট, যা একটি সামগ্রিক লেনদেনের অংশ হিসাবে ফাংশনটি কল করে। সেই কন্ট্র্যাক্টগুলো অবিলম্বে ফলাফল পায়, + কারণ তারা একই লেনদেনে চলে, তাই তারা ফাংশনের রিটার্ন মান ব্যবহার করতে পারে। + +একই ধরনের আউটপুট অন্যান্য ফাংশন দ্বারা তৈরি করা হয় যা কন্ট্র্যাক্টের স্টেট পরিবর্তন করে। + +  + +অ্যালাওয়েন্স একটি অ্যাকাউন্টকে ভিন্ন মালিকের কিছু টোকেন খরচ করার অনুমতি দেয়। +এটি দরকারী, উদাহরণস্বরূপ, বিক্রেতা হিসাবে কাজ করে এমন কন্ট্র্যাক্টের জন্য। কন্ট্র্যাক্টগুলো ইভেন্টের জন্য +পর্যবেক্ষণ করতে পারে না, তাই যদি কোনো ক্রেতা সরাসরি বিক্রেতা কন্ট্র্যাক্টে টোকেন স্থানান্তর করে তবে সেই কন্ট্র্যাক্ট জানবে না যে তাকে অর্থ প্রদান করা হয়েছে। পরিবর্তে, ক্রেতা বিক্রেতার +কন্ট্র্যাক্টকে একটি নির্দিষ্ট পরিমাণ খরচ করার অনুমতি দেয় এবং বিক্রেতা সেই পরিমাণ স্থানান্তর করে। +এটি বিক্রেতা কন্ট্র্যাক্টের কল করা একটি ফাংশনের মাধ্যমে করা হয়, যাতে বিক্রেতা কন্ট্র্যাক্ট জানতে পারে যে এটি সফল হয়েছে কিনা। + +```solidity + /** + * @dev {transferFrom}-এর মাধ্যমে `spender` `owner`-এর পক্ষ থেকে যে পরিমাণ টোকেন খরচ করার অনুমতি পাবে তা ফেরত দেয়। এটি ডিফল্টরূপে শূন্য থাকে। + * + * এই মানটি {approve} বা {transferFrom} কল করা হলে পরিবর্তিত হয়। + */ + function allowance(address owner, address spender) external view returns (uint256); +``` + +`allowance` ফাংশনটি যে কাউকে একটি +অ্যাড্রেস (`owner`) অন্য অ্যাড্রেসকে (`spender`) কতটা খরচ করতে দেয় তা দেখার জন্য কোয়েরি করার অনুমতি দেয়। + +  + +```solidity + /** + * @dev কলারের টোকেনের উপর `spender`-এর জন্য `amount`-কে অ্যালাওয়েন্স হিসাবে সেট করে। + * + * অপারেশনটি সফল হয়েছে কিনা তা নির্দেশ করে একটি বুলিয়ান মান ফেরত দেয়। + * + * গুরুত্বপূর্ণ: সতর্ক থাকুন যে এই পদ্ধতি ব্যবহার করে একটি অ্যালাওয়েন্স পরিবর্তন করার ফলে একটি ঝুঁকি থাকে যে কেউ দুর্ভাগ্যজনক + * লেনদেনের ক্রম অনুসারে পুরানো এবং নতুন উভয় অ্যালাওয়েন্স ব্যবহার করতে পারে। এই রেস কন্ডিশনটি + * কমানোর একটি সম্ভাব্য সমাধান হল প্রথমে স্পেন্ডারের অ্যালাওয়েন্স 0-তে নামিয়ে আনা এবং তারপরে + * কাঙ্খিত মান সেট করা: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * একটি {Approval} ইভেন্ট নির্গত করে। + */ + function approve(address spender, uint256 amount) external returns (bool); +``` + +`approve` ফাংশনটি একটি অ্যালাওয়েন্স তৈরি করে। কীভাবে এটির অপব্যবহার করা যেতে পারে সে সম্পর্কে বার্তাটি পড়তে ভুলবেন না। ইথেরিয়ামে আপনি আপনার নিজের লেনদেনের ক্রম নিয়ন্ত্রণ করেন, +কিন্তু আপনি অন্য লোকেদের লেনদেন কোন ক্রমে কার্যকর হবে তা নিয়ন্ত্রণ করতে পারবেন না, +যদি না আপনি অন্য পক্ষের লেনদেন সম্পন্ন হয়েছে না দেখা পর্যন্ত আপনার নিজের লেনদেন জমা না দেন। + +  + +```solidity + /** + * @dev অ্যালাওয়েন্স মেকানিজম ব্যবহার করে `sender` থেকে `recipient`-এর কাছে `amount` পরিমাণ টোকেন পাঠায়। + * এরপর কলারের অ্যালাওয়েন্স থেকে `amount` কেটে নেওয়া হয়। + * + * অপারেশনটি সফল হয়েছে কিনা তা নির্দেশ করে একটি বুলিয়ান মান ফেরত দেয়। + * + * একটি {Transfer} ইভেন্ট নির্গত করে। + */ + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); +``` + +অবশেষে, `transferFrom` স্পেন্ডার দ্বারা অ্যালাওয়েন্সটি খরচ করার জন্য ব্যবহৃত হয়। + +  + +```solidity + + /** + * @dev যখন `value` টোকেন এক অ্যাকাউন্ট (`from`) থেকে + * অন্য অ্যাকাউন্টে (`to`) সরানো হয়, তখন নির্গত হয়। + * + * মনে রাখবেন `value` শূন্য হতে পারে। + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev যখন {approve}-এ একটি কলের মাধ্যমে `owner`-এর জন্য একটি `spender`-এর অ্যালাওয়েন্স সেট করা হয় তখন নির্গত হয়। `value` হল নতুন অ্যালাওয়েন্স। + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} +``` + +ERC-20 কন্ট্র্যাক্টের স্টেট পরিবর্তন হলে এই ইভেন্টগুলি নির্গত হয়। + +## আসল কন্ট্র্যাক্ট {#the-actual-contract} + +এটি হল আসল কন্ট্র্যাক্ট যা ERC-20 স্ট্যান্ডার্ড ইমপ্লিমেন্ট করে, +[এখান থেকে নেওয়া হয়েছে](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)। +এটি যেমন আছে তেমন ব্যবহারের জন্য নয়, তবে আপনি এটিকে ব্যবহারযোগ্য কিছুতে প্রসারিত করতে এটির থেকে +[ইনহেরিট](https://www.tutorialspoint.com/solidity/solidity_inheritance.htm) করতে পারেন। + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.0 <0.8.0; +``` + +  + +### ইম্পোর্ট স্টেটমেন্ট {#import-statements} + +উপরের ইন্টারফেস সংজ্ঞা ছাড়াও, কন্ট্র্যাক্ট সংজ্ঞাটি অন্য দুটি ফাইল ইম্পোর্ট করে: + +```solidity + +import "../../GSN/Context.sol"; +import "./IERC20.sol"; +import "../../math/SafeMath.sol"; +``` + +- `GSN/Context.sol` হল [OpenGSN](https://www.opengsn.org/) ব্যবহার করার জন্য প্রয়োজনীয় সংজ্ঞা, এটি এমন একটি সিস্টেম যা ইথার ছাড়া ব্যবহারকারীদের ব্লকচেইন ব্যবহার করার অনুমতি দেয়। মনে রাখবেন এটি একটি পুরানো সংস্করণ, আপনি যদি OpenGSN-এর সাথে ইন্টিগ্রেট করতে চান তবে [এই টিউটোরিয়ালটি ব্যবহার করুন](https://docs.opengsn.org/javascript-client/tutorial.html)। +- [SafeMath লাইব্রেরি](https://ethereumdev.io/using-safe-math-library-to-prevent-from-overflows/), যা সলিডিটি সংস্করণ **<0.8.0**-এর জন্য অ্যারিথমেটিক ওভারফ্লো/আন্ডারফ্লো প্রতিরোধ করে। সলিডিটি ≥0.8.0-তে, অ্যারিথমেটিক অপারেশনগুলি ওভারফ্লো/আন্ডারফ্লোতে স্বয়ংক্রিয়ভাবে রিভার্ট হয়, যা SafeMath-কে অপ্রয়োজনীয় করে তোলে। এই কন্ট্র্যাক্টটি পুরানো কম্পাইলার সংস্করণগুলির সাথে ব্যাকওয়ার্ড সামঞ্জস্যের জন্য SafeMath ব্যবহার করে। + +  + +এই মন্তব্যটি কন্ট্র্যাক্টের উদ্দেশ্য ব্যাখ্যা করে। + +```solidity +/** + * @dev {IERC20} ইন্টারফেসের ইমপ্লিমেন্টেশন। + * + * এই ইমপ্লিমেন্টেশনটি টোকেনগুলি কীভাবে তৈরি হয় সে সম্পর্কে অ্যাগনস্টিক। এর মানে হল + * যে একটি সাপ্লাই মেকানিজম একটি ডিরাইভড কন্ট্র্যাক্টে {_mint} ব্যবহার করে যোগ করতে হবে। + * একটি জেনেরিক মেকানিজমের জন্য {ERC20PresetMinterPauser} দেখুন। + * + * টিপ: একটি বিস্তারিত লেখার জন্য আমাদের গাইড দেখুন + * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[কীভাবে + * সাপ্লাই মেকানিজম ইমপ্লিমেন্ট করবেন]। + * + * আমরা সাধারণ OpenZeppelin নির্দেশিকা অনুসরণ করেছি: ফাংশনগুলি ব্যর্থ হলে `false` ফেরত দেওয়ার পরিবর্তে রিভার্ট করে। + * এই আচরণটি প্রচলিত এবং ERC20 অ্যাপ্লিকেশনগুলির প্রত্যাশার সাথে বিরোধ করে না। + * + * এছাড়াও, {transferFrom}-এ কল করার সময় একটি {Approval} ইভেন্ট নির্গত হয়। + * এটি অ্যাপ্লিকেশনগুলিকে শুধুমাত্র উক্ত ইভেন্টগুলি শুনে সমস্ত অ্যাকাউন্টের জন্য অ্যালাওয়েন্স পুনর্গঠন করার অনুমতি দেয়। + * EIP-এর অন্যান্য ইমপ্লিমেন্টেশন এই ইভেন্টগুলি নির্গত নাও করতে পারে, কারণ এটি স্পেসিফিকেশন দ্বারা প্রয়োজন হয় না। + * + * অবশেষে, অ্যালাওয়েন্স সেট করার আশেপাশে সুপরিচিত সমস্যাগুলি প্রশমিত করার জন্য নন-স্ট্যান্ডার্ড {decreaseAllowance} এবং {increaseAllowance} ফাংশনগুলি যোগ করা হয়েছে। + * {IERC20-approve} দেখুন। + */ + +``` + +### কন্ট্র্যাক্ট সংজ্ঞা {#contract-definition} + +```solidity +contract ERC20 is Context, IERC20 { +``` + +এই লাইনটি ইনহেরিট্যান্স নির্দিষ্ট করে, এই ক্ষেত্রে উপরের `IERC20` থেকে এবং OpenGSN-এর জন্য `Context` থেকে। + +  + +```solidity + + using SafeMath for uint256; + +``` + +এই লাইনটি `uint256` টাইপের সাথে `SafeMath` লাইব্রেরি সংযুক্ত করে। আপনি এই লাইব্রেরিটি +[এখানে](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol) খুঁজে পেতে পারেন। + +### ভেরিয়েবল সংজ্ঞা {#variable-definitions} + +এই সংজ্ঞাগুলি কন্ট্র্যাক্টের স্টেট ভেরিয়েবলগুলি নির্দিষ্ট করে। এই ভেরিয়েবলগুলিকে `private` হিসাবে ঘোষণা করা হয়েছে, তবে এর মানে হল যে ব্লকচেইনের অন্যান্য কন্ট্র্যাক্টগুলি সেগুলি পড়তে পারে না। _ব্লকচেইনে কোনো গোপনীয়তা নেই_, প্রতিটি নোডের সফটওয়্যার প্রতিটি ব্লকে প্রতিটি কন্ট্র্যাক্টের স্টেট ধারণ করে। প্রচলিত নিয়ম অনুযায়ী, স্টেট ভেরিয়েবলগুলির নামকরণ করা হয় `_`। + +প্রথম দুটি ভেরিয়েবল হল [ম্যাপিং](https://www.tutorialspoint.com/solidity/solidity_mappings.htm), যার অর্থ তারা প্রায় [অ্যাসোসিয়েটিভ অ্যারে](https://wikipedia.org/wiki/Associative_array)-এর মতো আচরণ করে, তবে এখানে কীগুলো নিউমেরিক ভ্যালু। স্টোরেজ শুধুমাত্র সেই এন্ট্রিগুলির জন্য বরাদ্দ করা হয় যেগুলির মান ডিফল্ট (শূন্য) থেকে ভিন্ন। + +```solidity + mapping (address => uint256) private _balances; +``` + +প্রথম ম্যাপিং, `_balances`, হল অ্যাড্রেস এবং এই টোকেনে তাদের নিজ নিজ ব্যালেন্স। ব্যালেন্স অ্যাক্সেস করতে, এই সিনট্যাক্সটি ব্যবহার করুন: `_balances[
]`। + +  + +```solidity + mapping (address => mapping (address => uint256)) private _allowances; +``` + +এই ভেরিয়েবল, `_allowances`, আগে ব্যাখ্যা করা অ্যালাওয়েন্সগুলো সংরক্ষণ করে। প্রথম ইন্ডেক্স হল টোকেনের মালিক, এবং দ্বিতীয়টি হল অ্যালাওয়েন্স সহ কন্ট্র্যাক্ট। অ্যাড্রেস B-এর অ্যাকাউন্ট থেকে অ্যাড্রেস A কত খরচ করতে পারে তা অ্যাক্সেস করতে, `_allowances[B][A]` ব্যবহার করুন। + +  + +```solidity + uint256 private _totalSupply; +``` + +নাম থেকেই বোঝা যাচ্ছে, এই ভেরিয়েবলটি টোকেনের মোট সরবরাহের হিসাব রাখে। + +  + +```solidity + string private _name; + string private _symbol; + uint8 private _decimals; +``` + +এই তিনটি ভেরিয়েবল পঠনযোগ্যতা উন্নত করতে ব্যবহৃত হয়। প্রথম দুটি স্ব-ব্যাখ্যামূলক, কিন্তু `_decimals` নয়। + +একদিকে, ইথেরিয়ামের কোনো ফ্লোটিং পয়েন্ট বা ভগ্নাংশ ভেরিয়েবল নেই। অন্যদিকে, মানুষ টোকেন ভাগ করতে পছন্দ করে। লোকেরা মুদ্রার জন্য সোনা বেছে নেওয়ার একটি কারণ হল, যখন কেউ একটি গরুর মূল্যের সমপরিমাণ হাঁস কিনতে চায়, তখন পরিবর্তন করা কঠিন ছিল। + +সমাধান হল পূর্ণসংখ্যার হিসাব রাখা, কিন্তু আসল টোকেনের পরিবর্তে এমন একটি ভগ্নাংশ টোকেন গণনা করা যা প্রায় মূল্যহীন। ইথারের ক্ষেত্রে, ভগ্নাংশ টোকেনটিকে wei বলা হয়, এবং 10^18 wei এক ETH-এর সমান। লেখার সময়, 10,000,000,000,000 wei প্রায় এক মার্কিন বা ইউরো সেন্টের সমান। + +অ্যাপ্লিকেশনগুলিকে জানতে হবে কীভাবে টোকেন ব্যালেন্স প্রদর্শন করতে হয়। যদি একজন ব্যবহারকারীর কাছে 3,141,000,000,000,000,000 wei থাকে, তাহলে সেটা কি 3.14 ETH? 31.41 ETH? 3,141 ETH? ইথারের ক্ষেত্রে এটি 10^18 wei প্রতি ETH হিসাবে সংজ্ঞায়িত, কিন্তু আপনার টোকেনের জন্য আপনি একটি ভিন্ন মান নির্বাচন করতে পারেন। যদি টোকেন ভাগ করার কোনো মানে না হয়, আপনি শূন্যের একটি `_decimals` মান ব্যবহার করতে পারেন। আপনি যদি ETH-এর মতো একই স্ট্যান্ডার্ড ব্যবহার করতে চান, তাহলে **18** মানটি ব্যবহার করুন। + +### কনস্ট্রাক্টর {#the-constructor} + +```solidity + /** + * @dev {name} এবং {symbol}-এর জন্য মান সেট করে, {decimals}-কে 18-এর একটি ডিফল্ট মান দিয়ে ইনিশিয়ালাইজ করে। + * + * {decimals}-এর জন্য একটি ভিন্ন মান নির্বাচন করতে, {_setupDecimals} ব্যবহার করুন। + * + * এই তিনটি মানই অপরিবর্তনীয়: এগুলি শুধুমাত্র নির্মাণের সময় একবার সেট করা যেতে পারে। + */ + constructor (string memory name_, string memory symbol_) public { + // সলিডিটি ≥0.7.0-তে, 'public' অন্তর্নিহিত এবং বাদ দেওয়া যেতে পারে। + + _name = name_; + _symbol = symbol_; + _decimals = 18; + } +``` + +কনস্ট্রাক্টরটি প্রথমবার কন্ট্র্যাক্ট তৈরি করার সময় কল করা হয়। প্রচলিত নিয়ম অনুযায়ী, ফাংশন প্যারামিটারগুলির নামকরণ করা হয় `_`। + +### ইউজার ইন্টারফেস ফাংশন {#user-interface-functions} + +```solidity + /** + * @dev টোকেনের নাম ফেরত দেয়। + */ + function name() public view returns (string memory) { + return _name; + } + + /** + * @dev টোকেনের প্রতীক ফেরত দেয়, সাধারণত নামের একটি সংক্ষিপ্ত সংস্করণ। + */ + function symbol() public view returns (string memory) { + return _symbol; + } + + /** + * @dev ব্যবহারকারীর কাছে এর উপস্থাপনা পেতে ব্যবহৃত দশমিকের সংখ্যা ফেরত দেয়। + * উদাহরণস্বরূপ, যদি `decimals` `2` এর সমান হয়, তাহলে `505` টোকেনের একটি ব্যালেন্স + * একজন ব্যবহারকারীকে `5,05` (`505 / 10 ** 2`) হিসাবে প্রদর্শন করা উচিত। + * + * টোকেনগুলি সাধারণত 18-এর একটি মান বেছে নেয়, যা ইথার এবং wei-এর মধ্যেকার সম্পর্কের অনুকরণ করে। + * এটিই সেই মান যা {ERC20} ব্যবহার করে, যদি না {_setupDecimals} কল করা হয়। + * + * দ্রষ্টব্য: এই তথ্যটি শুধুমাত্র _প্রদর্শনের_ উদ্দেশ্যে ব্যবহৃত হয়: এটি কোনোভাবেই + * কন্ট্র্যাক্টের কোনো গাণিতিক ক্রিয়াকলাপকে প্রভাবিত করে না, যার মধ্যে + * {IERC20-balanceOf} এবং {IERC20-transfer} অন্তর্ভুক্ত। + */ + function decimals() public view returns (uint8) { + return _decimals; + } +``` + +এই ফাংশনগুলি, `name`, `symbol`, এবং `decimals` ইউজার ইন্টারফেসগুলিকে আপনার কন্ট্র্যাক্ট সম্পর্কে জানতে সাহায্য করে যাতে তারা এটি সঠিকভাবে প্রদর্শন করতে পারে। + +রিটার্ন টাইপ হল `string memory`, যার অর্থ মেমরিতে সংরক্ষিত একটি স্ট্রিং ফেরত দেওয়া। ভেরিয়েবল, যেমন স্ট্রিং, তিনটি স্থানে সংরক্ষণ করা যেতে পারে: + +| | জীবনকাল | কন্ট্র্যাক্ট অ্যাক্সেস | গ্যাস খরচ | +| ---------- | ------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------ | +| মেমরি | ফাংশন কল | পড়া/লেখা | দশ বা শত (উচ্চতর অবস্থানের জন্য উচ্চতর) | +| Calldata | ফাংশন কল | শুধুমাত্র পঠনযোগ্য | রিটার্ন টাইপ হিসাবে ব্যবহার করা যাবে না, শুধুমাত্র একটি ফাংশন প্যারামিটার টাইপ হিসাবে ব্যবহার করা যাবে | +| সংগ্রহস্থল | পরিবর্তন না হওয়া পর্যন্ত | পড়া/লেখা | উচ্চ (পড়ার জন্য 800, লেখার জন্য 20k) | + +এই ক্ষেত্রে, `memory` হল সেরা পছন্দ। + +### টোকেন তথ্য পড়ুন {#read-token-information} + +এগুলি হল এমন ফাংশন যা টোকেন সম্পর্কে তথ্য প্রদান করে, হয় মোট সরবরাহ বা একটি অ্যাকাউন্টের ব্যালেন্স। + +```solidity + /** + * @dev {IERC20-totalSupply} দেখুন। + */ + function totalSupply() public view override returns (uint256) { + return _totalSupply; + } +``` + +`totalSupply` ফাংশনটি টোকেনের মোট সরবরাহ ফেরত দেয়। + +  + +```solidity + /** + * @dev {IERC20-balanceOf} দেখুন। + */ + function balanceOf(address account) public view override returns (uint256) { + return _balances[account]; + } +``` + +একটি অ্যাকাউন্টের ব্যালেন্স পড়ুন। মনে রাখবেন যে যে কেউ অন্য কারো অ্যাকাউন্টের ব্যালেন্স পেতে পারে। এই তথ্যটি লুকানোর চেষ্টা করার কোনো মানে নেই, কারণ এটি যাইহোক প্রতিটি নোডে উপলব্ধ। _ব্লকচেইনে কোনো গোপনীয়তা নেই।_ + +### টোকেন স্থানান্তর করুন {#transfer-tokens} + +```solidity + /** + * @dev {IERC20-transfer} দেখুন। + * + * প্রয়োজনীয়তা: + * + * - `recipient` শূন্য অ্যাড্রেস হতে পারবে না। + * - কলারের কাছে কমপক্ষে `amount` পরিমাণ ব্যালেন্স থাকতে হবে। + */ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { +``` + +`transfer` ফাংশনটি প্রেরকের অ্যাকাউন্ট থেকে একটি ভিন্ন অ্যাকাউন্টে টোকেন স্থানান্তর করার জন্য কল করা হয়। মনে রাখবেন যে যদিও এটি একটি বুলিয়ান মান ফেরত দেয়, সেই মানটি সবসময় **সত্য**। যদি স্থানান্তর ব্যর্থ হয় তবে কন্ট্র্যাক্ট কলটি রিভার্ট করে। + +  + +```solidity + _transfer(_msgSender(), recipient, amount); + return true; + } +``` + +`_transfer` ফাংশনটি আসল কাজটি করে। এটি একটি প্রাইভেট ফাংশন যা শুধুমাত্র অন্যান্য কন্ট্র্যাক্ট ফাংশন দ্বারা কল করা যেতে পারে। প্রচলিত নিয়ম অনুযায়ী প্রাইভেট ফাংশনগুলির নামকরণ করা হয় `_`, স্টেট ভেরিয়েবলগুলির মতোই। + +সাধারণত সলিডিটিতে আমরা মেসেজ প্রেরকের জন্য `msg.sender` ব্যবহার করি। যাইহোক, এটি [OpenGSN](http://opengsn.org/)-কে ব্রেক করে। যদি আমরা আমাদের টোকেন দিয়ে ইথারবিহীন লেনদেনের অনুমতি দিতে চাই, তাহলে আমাদের `_msgSender()` ব্যবহার করতে হবে। এটি সাধারণ লেনদেনের জন্য `msg.sender` ফেরত দেয়, কিন্তু ইথারবিহীন লেনদেনের জন্য আসল সাইনারকে ফেরত দেয় এবং মেসেজ রিলে করা কন্ট্র্যাক্টকে নয়। + +### অ্যালাওয়েন্স ফাংশন {#allowance-functions} + +এগুলি হল সেই ফাংশন যা অ্যালাওয়েন্স কার্যকারিতা ইমপ্লিমেন্ট করে: `allowance`, `approve`, `transferFrom`, এবং `_approve`। এছাড়াও, OpenZeppelin ইমপ্লিমেন্টেশন মৌলিক স্ট্যান্ডার্ডের বাইরে গিয়ে কিছু বৈশিষ্ট্য অন্তর্ভুক্ত করে যা নিরাপত্তা উন্নত করে: `increaseAllowance`, এবং `decreaseAllowance`। + +#### অ্যালাওয়েন্স ফাংশন {#allowance} + +```solidity + /** + * @dev {IERC20-allowance} দেখুন। + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } +``` + +`allowance` ফাংশনটি প্রত্যেককে যেকোনো অ্যালাওয়েন্স পরীক্ষা করার অনুমতি দেয়। + +#### অ্যাপ্রুভ ফাংশন {#approve} + +```solidity + /** + * @dev {IERC20-approve} দেখুন। + * + * প্রয়োজনীয়তা: + * + * - `spender` শূন্য অ্যাড্রেস হতে পারবে না। + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { +``` + +এই ফাংশনটি একটি অ্যালাওয়েন্স তৈরি করার জন্য কল করা হয়। এটি উপরের `transfer` ফাংশনের অনুরূপ: + +- ফাংশনটি শুধু একটি অভ্যন্তরীণ ফাংশন কল করে (এই ক্ষেত্রে, `_approve`) যা আসল কাজটি করে। +- ফাংশনটি হয় `true` ফেরত দেয় (যদি সফল হয়) অথবা রিভার্ট করে (যদি না হয়)। + +  + +```solidity + _approve(_msgSender(), spender, amount); + return true; + } +``` + +আমরা অভ্যন্তরীণ ফাংশন ব্যবহার করি যাতে স্টেট পরিবর্তনের স্থানগুলির সংখ্যা কমানো যায়। _যেকোনো_ ফাংশন যা স্টেট পরিবর্তন করে তা একটি সম্ভাব্য নিরাপত্তা ঝুঁকি যা নিরাপত্তার জন্য অডিট করা প্রয়োজন। এইভাবে আমাদের ভুল করার সম্ভাবনা কম থাকে। + +#### transferFrom ফাংশন {#transferFrom} + +এটি হল সেই ফাংশন যা একজন স্পেন্ডার একটি অ্যালাওয়েন্স খরচ করার জন্য কল করে। এর জন্য দুটি অপারেশন প্রয়োজন: খরচ করা পরিমাণ স্থানান্তর করা এবং সেই পরিমাণ দ্বারা অ্যালাওয়েন্স কমানো। + +```solidity + /** + * @dev {IERC20-transferFrom} দেখুন। + * + * আপডেট করা অ্যালাওয়েন্স নির্দেশ করে একটি {Approval} ইভেন্ট নির্গত করে। এটি + * EIP দ্বারা প্রয়োজন হয় না। {ERC20}-এর শুরুতে নোটটি দেখুন। + * + * প্রয়োজনীয়তা: + * + * - `sender` এবং `recipient` শূন্য অ্যাড্রেস হতে পারবে না। + * - `sender`-এর কাছে কমপক্ষে `amount` পরিমাণ ব্যালেন্স থাকতে হবে। + * - কলারের কাছে ``sender``-এর টোকেনের জন্য কমপক্ষে `amount` পরিমাণ + * অ্যালাওয়েন্স থাকতে হবে। + */ + function transferFrom(address sender, address recipient, uint256 amount) public virtual + override returns (bool) { + _transfer(sender, recipient, amount); +``` + +  + +`a.sub(b, "message")` ফাংশন কল দুটি কাজ করে। প্রথমত, এটি `a-b` গণনা করে, যা নতুন অ্যালাওয়েন্স। +দ্বিতীয়ত, এটি পরীক্ষা করে যে এই ফলাফলটি নেতিবাচক নয়। যদি এটি নেতিবাচক হয় তবে কলটি প্রদত্ত বার্তা সহ রিভার্ট করে। মনে রাখবেন যে যখন একটি কল রিভার্ট করে তখন সেই কলের সময় আগে করা যেকোনো প্রক্রিয়াকরণ উপেক্ষা করা হয় তাই আমাদের `_transfer` আনডু করার প্রয়োজন নেই। + +```solidity + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, + "ERC20: transfer amount exceeds allowance")); + return true; + } +``` + +#### OpenZeppelin নিরাপত্তা সংযোজন {#openzeppelin-safety-additions} + +একটি নন-জিরো অ্যালাওয়েন্সকে অন্য একটি নন-জিরো মানে সেট করা বিপজ্জনক, কারণ আপনি শুধুমাত্র আপনার নিজের লেনদেনের ক্রম নিয়ন্ত্রণ করেন, অন্য কারো নয়। কল্পনা করুন আপনার দুজন ব্যবহারকারী আছে, এলিস যে সাদাসিধে এবং বিল যে অসৎ। এলিস বিলের কাছ থেকে কিছু পরিষেবা চায়, যা তার মতে পাঁচ টোকেন মূল্যের - তাই সে বিলকে পাঁচ টোকেনের অ্যালাওয়েন্স দেয়। + +তারপর কিছু পরিবর্তন হয় এবং বিলের দাম দশ টোকেনে বেড়ে যায়। এলিস, যে এখনও পরিষেবাটি চায়, একটি লেনদেন পাঠায় যা বিলের অ্যালাওয়েন্স দশ-এ সেট করে। বিল লেনদেন পুলে এই নতুন লেনদেনটি দেখার সাথে সাথে একটি লেনদেন পাঠায় যা এলিসের পাঁচ টোকেন খরচ করে এবং যার গ্যাস মূল্য অনেক বেশি যাতে এটি দ্রুত মাইনিং করা হয়। এইভাবে বিল প্রথমে পাঁচ টোকেন খরচ করতে পারে এবং তারপর, এলিসের নতুন অ্যালাওয়েন্স মাইনিং হয়ে গেলে, আরও দশ টোকেন খরচ করতে পারে, মোট পনেরো টোকেনের জন্য, যা এলিস অনুমোদন করতে চেয়েছিল তার চেয়ে বেশি। এই কৌশলটিকে +[ফ্রন্ট-রানিং](https://consensysdiligence.github.io/smart-contract-best-practices/attacks/#front-running) বলা হয়। + +| এলিস লেনদেন | এলিস নন্স | বিল লেনদেন | বিল নন্স | বিলের অ্যালাওয়েন্স | এলিসের কাছ থেকে বিলের মোট আয় | +| ------------------------------------ | --------- | ------------------------------------------------ | -------- | ------------------ | ----------------------------- | +| approve(Bill, 5) | 10 | | | 5 | 0 | +| | | transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | +| approve(Bill, 10) | 11 | | | 10 | 5 | +| | | transferFrom(Alice, Bill, 10) | 10,124 | 0 | 15 | + +এই সমস্যাটি এড়াতে, এই দুটি ফাংশন (`increaseAllowance` এবং `decreaseAllowance`) আপনাকে একটি নির্দিষ্ট পরিমাণ দ্বারা অ্যালাওয়েন্স পরিবর্তন করার অনুমতি দেয়। সুতরাং যদি বিল ইতিমধ্যে পাঁচ টোকেন খরচ করে ফেলে, সে কেবল আরও পাঁচ টোকেন খরচ করতে পারবে। সময়ের উপর নির্ভর করে, এটি দুটি উপায়ে কাজ করতে পারে, যার উভয়টিতেই বিল শুধুমাত্র দশটি টোকেন পায়: + +A: + +| এলিস লেনদেন | এলিস নন্স | বিল লেনদেন | বিল নন্স | বিলের অ্যালাওয়েন্স | এলিসের কাছ থেকে বিলের মোট আয় | +| --------------------------------------------- | --------: | ----------------------------------------------- | -------: | -----------------: | ----------------------------- | +| approve(Bill, 5) | 10 | | | 5 | 0 | +| | | transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | +| increaseAllowance(Bill, 5) | 11 | | | 0+5 = 5 | 5 | +| | | transferFrom(Alice, Bill, 5) | 10,124 | 0 | 10 | + +B: + +| এলিস লেনদেন | এলিস নন্স | বিল লেনদেন | বিল নন্স | বিলের অ্যালাওয়েন্স | এলিসের কাছ থেকে বিলের মোট আয় | +| --------------------------------------------- | --------: | ------------------------------------------------ | -------: | -----------------: | ----------------------------: | +| approve(Bill, 5) | 10 | | | 5 | 0 | +| increaseAllowance(Bill, 5) | 11 | | | 5+5 = 10 | 0 | +| | | transferFrom(Alice, Bill, 10) | 10,124 | 0 | 10 | + +```solidity + /** + * @dev কলার দ্বারা স্পেন্ডারকে প্রদত্ত অ্যালাওয়েন্স অ্যাটমিকভাবে বৃদ্ধি করে। + * + * এটি {approve}-এর একটি বিকল্প যা {IERC20-approve}-এ বর্ণিত সমস্যাগুলির জন্য একটি প্রশমন হিসাবে ব্যবহার করা যেতে পারে। + * + * আপডেট করা অ্যালাওয়েন্স নির্দেশ করে একটি {Approval} ইভেন্ট নির্গত করে। + * + * প্রয়োজনীয়তা: + * + * - `spender` শূন্য অ্যাড্রেস হতে পারবে না। + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); + return true; + } +``` + +`a.add(b)` ফাংশনটি একটি নিরাপদ যোগ। অসম্ভাব্য ক্ষেত্রে যে `a`+`b`>=`2^256` হয়, এটি সাধারণ যোগের মতো মোড়ানো হয় না। + +```solidity + + /** + * @dev কলার দ্বারা স্পেন্ডারকে প্রদত্ত অ্যালাওয়েন্স অ্যাটমিকভাবে হ্রাস করে। + * + * এটি {approve}-এর একটি বিকল্প যা {IERC20-approve}-এ বর্ণিত সমস্যাগুলির জন্য একটি প্রশমন হিসাবে ব্যবহার করা যেতে পারে। + * + * আপডেট করা অ্যালাওয়েন্স নির্দেশ করে একটি {Approval} ইভেন্ট নির্গত করে। + * + * প্রয়োজনীয়তা: + * + * - `spender` শূন্য অ্যাড্রেস হতে পারবে না। + * - `spender`-এর কাছে কলারের জন্য কমপক্ষে + * `subtractedValue` পরিমাণ অ্যালাওয়েন্স থাকতে হবে। + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, + "ERC20: decreased allowance below zero")); + return true; + } +``` + +### টোকেন তথ্য পরিবর্তনকারী ফাংশন {#functions-that-modify-token-information} + +এগুলি হল সেই চারটি ফাংশন যা আসল কাজটি করে: `_transfer`, `_mint`, `_burn`, এবং `_approve`। + +#### _transfer ফাংশন {#_transfer} + +```solidity + /** + * @dev `sender` থেকে `recipient`-এর কাছে `amount` পরিমাণ টোকেন পাঠায়। + * + * এই ইন্টারনাল ফাংশনটি {transfer}-এর সমতুল্য, এবং এটি ব্যবহার করা যেতে পারে, উদাহরণস্বরূপ, + * স্বয়ংক্রিয় টোকেন ফি, স্ল্যাশিং মেকানিজম ইত্যাদি ইমপ্লিমেন্ট করার জন্য। + * + * একটি {Transfer} ইভেন্ট নির্গত করে। + * + * প্রয়োজনীয়তা: + * + * - `sender` শূন্য অ্যাড্রেস হতে পারবে না। + * - `recipient` শূন্য অ্যাড্রেস হতে পারবে না। + * - `sender`-এর কাছে কমপক্ষে `amount` পরিমাণ ব্যালেন্স থাকতে হবে। + */ + function _transfer(address sender, address recipient, uint256 amount) internal virtual { +``` + +এই ফাংশন, `_transfer`, এক অ্যাকাউন্ট থেকে অন্য অ্যাকাউন্টে টোকেন স্থানান্তর করে। এটি `transfer` (প্রেরকের নিজের অ্যাকাউন্ট থেকে স্থানান্তরের জন্য) এবং `transferFrom` (অন্য কারো অ্যাকাউন্ট থেকে স্থানান্তর করতে অ্যালাওয়েন্স ব্যবহার করার জন্য) উভয় দ্বারাই কল করা হয়। + +  + +```solidity + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); +``` + +ইথেরিয়ামে আসলে কেউ শূন্য অ্যাড্রেসের মালিক নয় (অর্থাৎ, কেউ এমন একটি ব্যক্তিগত কী জানে না যার ম্যাচিং পাবলিক কী শূন্য অ্যাড্রেসে রূপান্তরিত হয়)। লোকেরা যখন সেই অ্যাড্রেস ব্যবহার করে, তখন এটি সাধারণত একটি সফটওয়্যার বাগ - তাই প্রেরক বা প্রাপক হিসাবে শূন্য অ্যাড্রেস ব্যবহার করা হলে আমরা ব্যর্থ হই। + +  + +```solidity + _beforeTokenTransfer(sender, recipient, amount); + +``` + +এই কন্ট্র্যাক্টটি ব্যবহার করার দুটি উপায় আছে: + +1. আপনার নিজের কোডের জন্য এটি একটি টেমপ্লেট হিসাবে ব্যবহার করুন +2. [এটির থেকে ইনহেরিট করুন](https://www.bitdegree.org/learn/solidity-inheritance), এবং শুধুমাত্র সেই ফাংশনগুলি ওভাররাইড করুন যা আপনাকে পরিবর্তন করতে হবে + +দ্বিতীয় পদ্ধতিটি অনেক ভালো কারণ OpenZeppelin ERC-20 কোডটি ইতিমধ্যে অডিট করা হয়েছে এবং নিরাপদ হিসাবে দেখানো হয়েছে। আপনি যখন ইনহেরিট্যান্স ব্যবহার করেন তখন এটি স্পষ্ট হয় যে আপনি কোন ফাংশনগুলি পরিবর্তন করছেন, এবং আপনার কন্ট্র্যাক্টকে বিশ্বাস করতে লোকেদের শুধুমাত্র সেই নির্দিষ্ট ফাংশনগুলি অডিট করতে হবে। + +প্রতিবার টোকেন হাত বদলানোর সময় একটি ফাংশন সম্পাদন করা প্রায়শই দরকারী। যাইহোক, `_transfer` একটি অত্যন্ত গুরুত্বপূর্ণ ফাংশন এবং এটিকে অনিরাপদভাবে লেখা সম্ভব (নীচে দেখুন), তাই এটি ওভাররাইড না করাই ভাল। সমাধান হল `_beforeTokenTransfer`, একটি [হুক ফাংশন](https://wikipedia.org/wiki/Hooking)। আপনি এই ফাংশনটি ওভাররাইড করতে পারেন, এবং এটি প্রতিটি স্থানান্তরে কল করা হবে। + +  + +```solidity + _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); + _balances[recipient] = _balances[recipient].add(amount); +``` + +এগুলি হল সেই লাইন যা আসলে স্থানান্তর করে। মনে রাখবেন যে তাদের মধ্যে **কিছুই** নেই, এবং আমরা প্রাপকের কাছে যোগ করার আগে প্রেরকের কাছ থেকে স্থানান্তরিত পরিমাণ বিয়োগ করি। এটি গুরুত্বপূর্ণ কারণ যদি মাঝখানে একটি ভিন্ন কন্ট্র্যাক্টে একটি কল থাকত, তবে এটি এই কন্ট্র্যাক্টকে প্রতারণা করতে ব্যবহার করা যেতে পারত। এইভাবে স্থানান্তরটি অ্যাটমিক, এর মাঝখানে কিছুই ঘটতে পারে না। + +  + +```solidity + emit Transfer(sender, recipient, amount); + } +``` + +অবশেষে, একটি `Transfer` ইভেন্ট নির্গত করুন। ইভেন্টগুলি স্মার্ট কন্ট্র্যাক্টগুলিতে অ্যাক্সেসযোগ্য নয়, কিন্তু ব্লকচেইনের বাইরে চলমান কোড ইভেন্টগুলি শুনতে পারে এবং সেগুলিতে প্রতিক্রিয়া জানাতে পারে। উদাহরণস্বরূপ, একটি ওয়ালেট যখন মালিক আরও টোকেন পায় তখন তার হিসাব রাখতে পারে। + +#### _mint এবং _burn ফাংশন {#_mint-and-_burn} + +এই দুটি ফাংশন (`_mint` এবং `_burn`) টোকেনের মোট সরবরাহ পরিবর্তন করে। +এগুলি অভ্যন্তরীণ এবং এই কন্ট্র্যাক্টে তাদের কল করার জন্য কোনো ফাংশন নেই, তাই এগুলি শুধুমাত্র তখনই কার্যকর যখন আপনি কন্ট্র্যাক্ট থেকে ইনহেরিট করেন এবং নতুন টোকেন মিন্ট করার বা বিদ্যমান টোকেনগুলি বার্ন করার শর্তগুলি নির্ধারণের জন্য আপনার নিজস্ব লজিক যুক্ত করেন। + +**দ্রষ্টব্য:** প্রতিটি ERC-20 টোকেনের নিজস্ব ব্যবসায়িক যুক্তি রয়েছে যা টোকেন ব্যবস্থাপনাকে নির্দেশ করে। +উদাহরণস্বরূপ, একটি নির্দিষ্ট সরবরাহ কন্ট্র্যাক্ট শুধুমাত্র কনস্ট্রাক্টরে `_mint` কল করতে পারে এবং `_burn` কল নাও করতে পারে। একটি কন্ট্র্যাক্ট যা টোকেন বিক্রি করে, তা `_mint` কল করবে যখন এটি অর্থ প্রদান করা হয়, এবং সম্ভবত `_burn` কল করবে কিছু সময়ে অনিয়ন্ত্রিত মুদ্রাস্ফীতি এড়াতে। + +```solidity + /** @dev `amount` পরিমাণ টোকেন তৈরি করে এবং সেগুলোকে `account`-কে অ্যাসাইন করে, যা মোট সরবরাহ বাড়ায়। + * + * `from`-কে শূন্য অ্যাড্রেসে সেট করে একটি {Transfer} ইভেন্ট নির্গত করে। + * + * প্রয়োজনীয়তা: + * + * - `to` শূন্য অ্যাড্রেস হতে পারবে না। + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + _beforeTokenTransfer(address(0), account, amount); + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + emit Transfer(address(0), account, amount); + } +``` + +টোকেনের মোট সংখ্যা পরিবর্তন হলে `_totalSupply` আপডেট করতে ভুলবেন না। + +  + +```solidity + /** + * @dev `account` থেকে `amount` পরিমাণ টোকেন নষ্ট করে, যা মোট সরবরাহ কমায়। + * + * `to`-কে শূন্য অ্যাড্রেসে সেট করে একটি {Transfer} ইভেন্ট নির্গত করে। + * + * প্রয়োজনীয়তা: + * + * - `account` শূন্য অ্যাড্রেস হতে পারবে না। + * - `account`-এর কাছে কমপক্ষে `amount` পরিমাণ টোকেন থাকতে হবে। + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); + _totalSupply = _totalSupply.sub(amount); + emit Transfer(account, address(0), amount); + } +``` + +`_burn` ফাংশনটি প্রায় `_mint`-এর মতোই, তবে এটি বিপরীত দিকে যায়। + +#### _approve ফাংশন {#_approve} + +এটি হল সেই ফাংশন যা আসলে অ্যালাওয়েন্স নির্দিষ্ট করে। মনে রাখবেন যে এটি একজন মালিককে এমন একটি অ্যালাওয়েন্স নির্দিষ্ট করার অনুমতি দেয় যা মালিকের বর্তমান ব্যালেন্সের চেয়ে বেশি। এটি ঠিক আছে কারণ ব্যালেন্সটি স্থানান্তরের সময় পরীক্ষা করা হয়, যখন এটি অ্যালাওয়েন্স তৈরি করার সময়কার ব্যালেন্স থেকে ভিন্ন হতে পারে। + +```solidity + /** + * @dev `spender`-এর `owner` টোকেনের উপর `amount`-কে অ্যালাওয়েন্স হিসাবে সেট করে। + * + * এই অভ্যন্তরীণ ফাংশনটি `approve`-এর সমতুল্য, এবং এটি ব্যবহার করা যেতে পারে, + * যেমন, নির্দিষ্ট সাবসিস্টেমের জন্য স্বয়ংক্রিয় অ্যালাওয়েন্স সেট করতে ইত্যাদি। + * + * একটি {Approval} ইভেন্ট নির্গত করে। + * + * প্রয়োজনীয়তা: + * + * - `owner` শূন্য অ্যাড্রেস হতে পারবে না। + * - `spender` শূন্য অ্যাড্রেস হতে পারবে না। + */ + function _approve(address owner, address spender, uint256 amount) internal virtual { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; +``` + +  + +একটি `Approval` ইভেন্ট নির্গত করুন। অ্যাপ্লিকেশনটি কীভাবে লেখা হয়েছে তার উপর নির্ভর করে, স্পেন্ডার কন্ট্র্যাক্টকে মালিক দ্বারা বা এই ইভেন্টগুলি শুনে এমন একটি সার্ভার দ্বারা অনুমোদনের বিষয়ে জানানো যেতে পারে। + +```solidity + emit Approval(owner, spender, amount); + } + +``` + +### ডেসিমাল ভেরিয়েবল পরিবর্তন করুন {#modify-the-decimals-variable} + +```solidity + + + /** + * @dev {decimals}-কে 18-এর ডিফল্ট মানের চেয়ে অন্য একটি মানে সেট করে। + * + * সতর্কতা: এই ফাংশনটি শুধুমাত্র কনস্ট্রাক্টর থেকে কল করা উচিত। টোকেন কন্ট্র্যাক্টগুলির সাথে ইন্টারঅ্যাক্ট করা বেশিরভাগ + * অ্যাপ্লিকেশন আশা করবে না যে {decimals} কখনো পরিবর্তন হবে, এবং যদি তা হয় তবে ভুলভাবে কাজ করতে পারে। + */ + function _setupDecimals(uint8 decimals_) internal { + _decimals = decimals_; + } +``` + +এই ফাংশনটি `_decimals` ভেরিয়েবলটি পরিবর্তন করে যা ইউজার ইন্টারফেসগুলিকে পরিমাণটি কীভাবে ব্যাখ্যা করতে হবে তা জানাতে ব্যবহৃত হয়। +আপনার এটি কনস্ট্রাক্টর থেকে কল করা উচিত। পরবর্তী কোনো সময়ে এটি কল করা অসততা হবে এবং অ্যাপ্লিকেশনগুলি এটি পরিচালনা করার জন্য ডিজাইন করা হয়নি। + +### হুকস {#hooks} + +```solidity + + /** + * @dev টোকেনের যেকোনো স্থানান্তরের আগে যে হুক কল করা হয়। এর মধ্যে মিন্টিং এবং বার্নিং অন্তর্ভুক্ত রয়েছে। + * + * কল করার শর্ত: + * + * - যখন `from` এবং `to` উভয়ই নন-জিরো হয়, তখন `from`-এর টোকেনের `amount` পরিমাণ `to`-কে স্থানান্তর করা হবে। + * - যখন `from` শূন্য হয়, তখন `to`-এর জন্য `amount` পরিমাণ টোকেন মিন্ট করা হবে। + * - যখন `to` শূন্য হয়, তখন `from`-এর টোকেনের `amount` পরিমাণ বার্ন করা হবে। + * - `from` এবং `to` কখনোই উভয়ই শূন্য হয় না। + * + * হুক সম্পর্কে আরও জানতে, xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]-এ যান। + */ + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } +``` + +এটি স্থানান্তরের সময় কল করার জন্য হুক ফাংশন। এটি এখানে খালি, কিন্তু যদি আপনার এটি কিছু করার প্রয়োজন হয় তবে আপনি শুধু এটি ওভাররাইড করুন। + +## উপসংহার {#conclusion} + +পর্যালোচনা করার জন্য, এখানে এই কন্ট্র্যাক্টের কিছু সবচেয়ে গুরুত্বপূর্ণ ধারণা দেওয়া হল (আমার মতে, আপনার মতামত ভিন্ন হতে পারে): + +- _ব্লকচেইনে কোনো গোপনীয়তা নেই_। যেকোনো তথ্য যা একটি স্মার্ট কন্ট্র্যাক্ট অ্যাক্সেস করতে পারে তা সমগ্র বিশ্বের জন্য উপলব্ধ। +- আপনি আপনার নিজের লেনদেনের ক্রম নিয়ন্ত্রণ করতে পারেন, কিন্তু অন্য লোকের লেনদেন কখন ঘটবে তা নয়। এই কারণেই একটি অ্যালাওয়েন্স পরিবর্তন করা বিপজ্জনক হতে পারে, কারণ এটি স্পেন্ডারকে উভয় অ্যালাওয়েন্সের যোগফল খরচ করার অনুমতি দেয়। +- `uint256` টাইপের মানগুলি মোড়ানো হয়। অন্য কথায়, _0-1=2^256-1_। যদি এটি কাঙ্ক্ষিত আচরণ না হয়, তাহলে আপনাকে এটি পরীক্ষা করতে হবে (অথবা SafeMath লাইব্রেরি ব্যবহার করতে হবে যা আপনার জন্য এটি করে)। মনে রাখবেন যে এটি [সলিডিটি 0.8.0](https://docs.soliditylang.org/en/breaking/080-breaking-changes.html)-তে পরিবর্তিত হয়েছে। +- একটি নির্দিষ্ট ধরণের সমস্ত স্টেট পরিবর্তন একটি নির্দিষ্ট স্থানে করুন, কারণ এটি অডিটিং সহজ করে। + এই কারণেই আমাদের কাছে, উদাহরণস্বরূপ, `_approve` রয়েছে, যা `approve`, `transferFrom`, `increaseAllowance`, এবং `decreaseAllowance` দ্বারা কল করা হয়। +- স্টেট পরিবর্তনগুলি অ্যাটমিক হওয়া উচিত, তাদের মাঝখানে অন্য কোনো অ্যাকশন ছাড়াই (যেমন আপনি `_transfer`-এ দেখতে পারেন)। এর কারণ হল স্টেট পরিবর্তনের সময় আপনার একটি অসামঞ্জস্যপূর্ণ স্টেট থাকে। উদাহরণস্বরূপ, প্রেরকের ব্যালেন্স থেকে কর্তন করার সময় এবং প্রাপকের ব্যালেন্সে যোগ করার সময়ের মধ্যে যত টোকেন থাকা উচিত তার চেয়ে কম টোকেন存在 করে। যদি তাদের মধ্যে অপারেশন থাকে, বিশেষ করে একটি ভিন্ন কন্ট্র্যাক্টে কল, তবে এর সম্ভাব্য অপব্যবহার করা যেতে পারে। + +এখন যেহেতু আপনি দেখেছেন OpenZeppelin ERC-20 কন্ট্র্যাক্টটি কীভাবে লেখা হয়েছে, এবং বিশেষ করে কীভাবে এটি আরও নিরাপদ করা হয়েছে, যান এবং আপনার নিজের নিরাপদ কন্ট্র্যাক্ট এবং অ্যাপ্লিকেশনগুলি লিখুন। + +[আমার আরও কাজের জন্য এখানে দেখুন](https://cryptodocguy.pro/)। diff --git a/public/content/translations/bn/developers/tutorials/erc20-with-safety-rails/index.md b/public/content/translations/bn/developers/tutorials/erc20-with-safety-rails/index.md new file mode 100644 index 00000000000..590afab0898 --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/erc20-with-safety-rails/index.md @@ -0,0 +1,217 @@ +--- +title: "সেফটি রেলস সহ ERC-20" +description: "লোকেদের বোকার মতো ভুল এড়াতে কীভাবে সাহায্য করবেন" +author: Ori Pomerantz +lang: bn +tags: [ "erc-20" ] +skill: beginner +published: 2022-08-15 +--- + +## ভূমিকা {#introduction} + +ইথেরিয়াম সম্পর্কে একটি দুর্দান্ত জিনিস হলো যে কোনও কেন্দ্রীয় কর্তৃপক্ষ নেই যা আপনার লেনদেন পরিবর্তন বা বাতিল করতে পারে। ইথেরিয়ামের একটি বড় সমস্যা হলো যে কোনও কেন্দ্রীয় কর্তৃপক্ষ নেই যার ব্যবহারকারীর ভুল বা অবৈধ লেনদেন বাতিল করার ক্ষমতা রয়েছে। এই নিবন্ধে আপনি [ERC-20](/developers/docs/standards/tokens/erc-20/) টোকেনগুলির সাথে ব্যবহারকারীরা যে সাধারণ ভুলগুলি করে সে সম্পর্কে জানতে পারবেন, সেইসাথে কীভাবে ERC-20 কন্ট্র্যাক্ট তৈরি করতে হয় যা ব্যবহারকারীদের সেই ভুলগুলি এড়াতে সাহায্য করে, বা যা একটি কেন্দ্রীয় কর্তৃপক্ষকে কিছু ক্ষমতা দেয় (উদাহরণস্বরূপ অ্যাকাউন্ট ফ্রিজ করার জন্য)। + +মনে রাখবেন যে আমরা [OpenZeppelin ERC-20 টোকেন কন্ট্র্যাক্ট](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20) ব্যবহার করব, এই নিবন্ধটি এটিকে খুব বিশদভাবে ব্যাখ্যা করে না। আপনি এই তথ্যটি [এখানে](/developers/tutorials/erc20-annotated-code) খুঁজে পেতে পারেন। + +আপনি যদি সম্পূর্ণ সোর্স কোড দেখতে চান: + +1. [Remix IDE](https://remix.ethereum.org/) খুলুন। +2. ক্লোন গিটহাব আইকনে ক্লিক করুন (![clone github icon](icon-clone.png))। +3. গিটহাব রিপোজিটরিটি `https://github.com/qbzzt/20220815-erc20-safety-rails` ক্লোন করুন। +4. **contracts > erc20-safety-rails.sol** খুলুন। + +## একটি ERC-20 কন্ট্র্যাক্ট তৈরি করা {#creating-an-erc-20-contract} + +সেফটি রেল ফাংশনালিটি যোগ করার আগে আমাদের একটি ERC-20 কন্ট্র্যাক্ট প্রয়োজন। এই নিবন্ধে আমরা [OpenZeppelin Contracts Wizard](https://docs.openzeppelin.com/contracts/5.x/wizard) ব্যবহার করব। এটি অন্য ব্রাউজারে খুলুন এবং এই নির্দেশাবলী অনুসরণ করুন: + +1. **ERC20** নির্বাচন করুন। + +2. এই সেটিংসগুলি লিখুন: + + | প্যারামিটার | মান | + | ------------------ | -------------------------------------------------------------- | + | নাম | SafetyRailsToken | + | প্রতীক | SAFE | + | Premint | আমরা প্রতি জোড়া টোকেনের জন্য একটির বেশি লিকুইডিটি পুল চাই না। | + | বৈশিষ্ট্য | কোনটি না | + | অ্যাক্সেস কন্ট্রোল | Ownable | + | আপগ্রেডিবিলিটি | কোনটি না | + +3. উপরে স্ক্রোল করুন এবং **Remix-এ খুলুন** (Remix-এর জন্য) অথবা একটি ভিন্ন এনভায়রনমেন্ট ব্যবহার করার জন্য **ডাউনলোড করুন**-এ ক্লিক করুন। আমি ধরে নিচ্ছি আপনি Remix ব্যবহার করছেন, যদি আপনি অন্য কিছু ব্যবহার করেন তবে শুধু উপযুক্ত পরিবর্তনগুলি করুন। + +4. এখন আমাদের একটি সম্পূর্ণরূপে কার্যকরী ERC-20 কন্ট্র্যাক্ট আছে। আপনি ইম্পোর্ট করা কোড দেখতে `.deps` > `npm` விரிৃত করতে পারেন। + +5. কন্ট্র্যাক্টটি একটি ERC-20 কন্ট্র্যাক্ট হিসাবে কাজ করে কিনা তা দেখার জন্য কম্পাইল করুন, ডিপ্লয় করুন এবং এর সাথে প্লে করুন। যদি আপনার Remix কীভাবে ব্যবহার করতে হয় তা শেখার প্রয়োজন হয়, [এই টিউটোরিয়ালটি ব্যবহার করুন](https://remix.ethereum.org/?#activate=udapp,solidity,LearnEth)। + +## সাধারণ ভুল {#common-mistakes} + +### ভুলগুলি {#the-mistakes} + +ব্যবহারকারীরা কখনও কখনও ভুল ঠিকানায় টোকেন পাঠান। যদিও আমরা তাদের মন পড়ে জানতে পারি না যে তারা কী করতে চেয়েছিল, তবে দুই ধরণের ভুল রয়েছে যা অনেক ঘটে এবং সহজে সনাক্ত করা যায়: + +1. কন্ট্র্যাক্টের নিজের ঠিকানায় টোকেন পাঠানো। উদাহরণস্বরূপ, [Optimism-এর OP টোকেন](https://optimism.mirror.xyz/qvd0WfuLKnePm1Gxb9dpGchPf5uDz5NSMEFdgirDS4c) দুই মাসেরও কম সময়ে [120,000 এর বেশি](https://optimism.blockscout.com/address/0x4200000000000000000000000000000000000042) OP টোকেন জমা করতে সক্ষম হয়েছে। এটি একটি উল্লেখযোগ্য পরিমাণ সম্পদকে প্রতিনিধিত্ব করে যা সম্ভবত মানুষ হারিয়েছে। + +2. টোকেনগুলিকে একটি খালি ঠিকানায় পাঠানো, যা একটি [এক্সটার্নালি ওনড অ্যাকাউন্ট](/developers/docs/accounts/#externally-owned-accounts-and-key-pairs) বা একটি [স্মার্ট কন্ট্র্যাক্টের](/developers/docs/smart-contracts) সাথে সঙ্গতিপূর্ণ নয়। যদিও আমার কাছে এটি কত ঘন ঘন ঘটে তার পরিসংখ্যান নেই, [একটি ঘটনায় 20,000,000 টোকেনের ক্ষতি হতে পারত](https://gov.optimism.io/t/message-to-optimism-community-from-wintermute/2595)। + +### ট্রান্সফার প্রতিরোধ করা {#preventing-transfers} + +OpenZeppelin ERC-20 কন্ট্র্যাক্টে [একটি হুক, `_beforeTokenTransfer`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol#L364-L368) অন্তর্ভুক্ত রয়েছে, যা একটি টোকেন ট্রান্সফার করার আগে কল করা হয়। ডিফল্টরূপে এই হুকটি কিছুই করে না, কিন্তু আমরা এতে আমাদের নিজস্ব ফাংশনালিটি যোগ করতে পারি, যেমন চেক যা সমস্যা হলে রিভার্ট করে। + +হুকটি ব্যবহার করতে, কনস্ট্রাক্টরের পরে এই ফাংশনটি যোগ করুন: + +```solidity + function _beforeTokenTransfer(address from, address to, uint256 amount) + internal virtual + override(ERC20) + { + super._beforeTokenTransfer(from, to, amount); + } +``` + +আপনি যদি Solidity-এর সাথে খুব পরিচিত না হন তবে এই ফাংশনের কিছু অংশ নতুন হতে পারে: + +```solidity + internal virtual +``` + +`virtual` কীওয়ার্ডটির অর্থ হলো যে আমরা যেমন `ERC20` থেকে ফাংশনালিটি ইনহেরিট করেছি এবং এই ফাংশনটিকে ওভাররাইড করেছি, তেমনি অন্যান্য কন্ট্র্যাক্ট আমাদের কাছ থেকে ইনহেরিট করতে এবং এই ফাংশনটিকে ওভাররাইড করতে পারে। + +```solidity + override(ERC20) +``` + +আমাদেরকে স্পষ্টভাবে নির্দিষ্ট করতে হবে যে আমরা `_beforeTokenTransfer`-এর ERC20 টোকেন সংজ্ঞাটি [ওভাররাইড](https://docs.soliditylang.org/en/v0.8.15/contracts.html#function-overriding) করছি। সাধারণভাবে, নিরাপত্তার দৃষ্টিকোণ থেকে, স্পষ্ট সংজ্ঞাগুলি অন্তর্নিহিতগুলির চেয়ে অনেক ভালো - আপনি যদি কিছু করে থাকেন তবে তা আপনার সামনে থাকলে আপনি তা ভুলতে পারবেন না। এই কারণেই আমাদের নির্দিষ্ট করতে হবে যে আমরা কোন সুপারক্লাসের `_beforeTokenTransfer` ওভাররাইড করছি। + +```solidity + super._beforeTokenTransfer(from, to, amount); +``` + +এই লাইনটি সেই কন্ট্র্যাক্ট বা কন্ট্র্যাক্টগুলির `_beforeTokenTransfer` ফাংশনটিকে কল করে যা থেকে আমরা ইনহেরিট করেছি এবং যার মধ্যে এটি রয়েছে। এই ক্ষেত্রে, এটি শুধুমাত্র `ERC20`, `Ownable`-এর এই হুকটি নেই। যদিও বর্তমানে `ERC20._beforeTokenTransfer` কিছুই করে না, আমরা এটিকে কল করি যদি ভবিষ্যতে ফাংশনালিটি যোগ করা হয় (এবং আমরা তখন কন্ট্র্যাক্টটি পুনরায় ডিপ্লয় করার সিদ্ধান্ত নিই, কারণ ডিপ্লয়মেন্টের পরে কন্ট্র্যাক্ট পরিবর্তন হয় না)। + +### রিকোয়ারমেন্টগুলি কোডিং করা {#coding-the-requirements} + +আমরা ফাংশনে এই রিকোয়ারমেন্টগুলি যোগ করতে চাই: + +- `to` ঠিকানাটি `address(this)`-এর সমান হতে পারে না, যা হলো ERC-20 কন্ট্র্যাক্টের নিজের ঠিকানা। +- `to` ঠিকানাটি খালি হতে পারে না, এটি অবশ্যই হবে: + - একটি এক্সটার্নালি ওনড অ্যাকাউন্ট (EOA)। আমরা সরাসরি একটি ঠিকানা EOA কিনা তা পরীক্ষা করতে পারি না, তবে আমরা একটি ঠিকানার ETH ব্যালেন্স পরীক্ষা করতে পারি। EOA-গুলির প্রায় সবসময়ই একটি ব্যালেন্স থাকে, এমনকি যদি সেগুলি আর ব্যবহার না করা হয় - শেষ wei পর্যন্ত সেগুলি পরিষ্কার করা কঠিন। + - একটি স্মার্ট কন্ট্র্যাক্ট। একটি ঠিকানা স্মার্ট কন্ট্র্যাক্ট কিনা তা পরীক্ষা করা একটু কঠিন। একটি অপকোড আছে যা এক্সটার্নাল কোডের দৈর্ঘ্য পরীক্ষা করে, যাকে [`EXTCODESIZE`](https://www.evm.codes/#3b) বলা হয়, কিন্তু এটি সরাসরি Solidity-তে উপলব্ধ নয়। এর জন্য আমাদের [Yul](https://docs.soliditylang.org/en/v0.8.15/yul.html) ব্যবহার করতে হবে, যা হলো EVM অ্যাসেম্বলি। Solidity থেকে আমরা ব্যবহার করতে পারতাম এমন অন্যান্য মানও আছে ([`
.code` এবং `
.codehash`](https://docs.soliditylang.org/en/v0.8.15/units-and-global-variables.html#members-of-address-types)), কিন্তু সেগুলোর খরচ বেশি। + +আসুন নতুন কোডটি লাইন বাই লাইন দেখি: + +```solidity + require(to != address(this), "কন্ট্র্যাক্ট ঠিকানায় টোকেন পাঠানো যাবে না"); +``` + +এটি হলো প্রথম রিকোয়ারমেন্ট, `to` এবং `this(address)` একই জিনিস নয় তা পরীক্ষা করুন। + +```solidity + bool isToContract; + assembly { + isToContract := gt(extcodesize(to), 0) + } +``` + +এভাবে আমরা একটি ঠিকানা কন্ট্র্যাক্ট কিনা তা পরীক্ষা করি। আমরা সরাসরি Yul থেকে আউটপুট পেতে পারি না, তাই পরিবর্তে আমরা ফলাফল ধরে রাখার জন্য একটি ভেরিয়েবল সংজ্ঞায়িত করি (`isToContract` এই ক্ষেত্রে)। Yul যেভাবে কাজ করে তা হলো, প্রতিটি অপকোডকে একটি ফাংশন হিসাবে বিবেচনা করা হয়। তাই প্রথমে আমরা কন্ট্র্যাক্টের আকার পেতে [`EXTCODESIZE`](https://www.evm.codes/#3b) কল করি, এবং তারপর এটি শূন্য নয় তা পরীক্ষা করতে [`GT`](https://www.evm.codes/#11) ব্যবহার করি (আমরা আনসাইন্ড পূর্ণসংখ্যার সাথে কাজ করছি, তাই অবশ্যই এটি ঋণাত্মক হতে পারে না)। এরপর আমরা ফলাফলটি `isToContract`-এ লিখি। + +```solidity + require(to.balance != 0 || isToContract, "একটি খালি ঠিকানায় টোকেন পাঠানো যাবে না"); +``` + +এবং অবশেষে, আমাদের কাছে খালি ঠিকানাগুলির জন্য আসল চেকটি রয়েছে। + +## প্রশাসনিক অ্যাক্সেস {#admin-access} + +কখনও কখনও একজন প্রশাসক থাকা দরকারি যিনি ভুল সংশোধন করতে পারেন। অপব্যবহারের সম্ভাবনা কমাতে, এই প্রশাসক একটি [মাল্টিসিগ](https://blog.logrocket.com/security-choices-multi-signature-wallets/) হতে পারে যাতে একাধিক ব্যক্তিকে একটি কাজের জন্য একমত হতে হয়। এই নিবন্ধে আমাদের দুটি প্রশাসনিক বৈশিষ্ট্য থাকবে: + +1. অ্যাকাউন্ট ফ্রিজ এবং আনফ্রিজ করা। উদাহরণস্বরূপ, যখন একটি অ্যাকাউন্টের নিরাপত্তা বিঘ্নিত হতে পারে তখন এটি কার্যকর হতে পারে। +2. অ্যাসেট ক্লিনআপ। + + কখনও কখনও প্রতারকরা বৈধতা অর্জনের জন্য আসল টোকেনের কন্ট্র্যাক্টে জাল টোকেন পাঠায়। উদাহরণস্বরূপ, [এখানে দেখুন](https://optimism.blockscout.com/token/0x2348B1a1228DDCd2dB668c3d30207c3E1852fBbe?tab=holders)। বৈধ ERC-20 কন্ট্র্যাক্টটি হলো [0x4200....0042](https://optimism.blockscout.com/token/0x4200000000000000000000000000000000000042)। যে স্ক্যামটি এটি হওয়ার ভান করে তা হলো [0x234....bbe](https://optimism.blockscout.com/token/0x2348B1a1228DDCd2dB668c3d30207c3E1852fBbe)। + + এটাও সম্ভব যে লোকেরা ভুল করে আমাদের কন্ট্র্যাক্টে বৈধ ERC-20 টোকেন পাঠাতে পারে, যা সেগুলিকে বের করার একটি উপায় থাকার আরেকটি কারণ। + +OpenZeppelin প্রশাসনিক অ্যাক্সেস সক্ষম করার জন্য দুটি প্রক্রিয়া প্রদান করে: + +- [`Ownable`](https://docs.openzeppelin.com/contracts/5.x/access-control#ownership-and-ownable) কন্ট্র্যাক্টগুলির একজন একক মালিক থাকে। যেসব ফাংশনে `onlyOwner` [মডিফায়ার](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm) থাকে, সেগুলি শুধুমাত্র সেই মালিক দ্বারা কল করা যেতে পারে। মালিকরা অন্য কাউকে মালিকানা হস্তান্তর করতে বা এটি সম্পূর্ণরূপে ত্যাগ করতে পারে। অন্যান্য সমস্ত অ্যাকাউন্টের অধিকার সাধারণত অভিন্ন। +- [`AccessControl`](https://docs.openzeppelin.com/contracts/5.x/access-control#role-based-access-control) কন্ট্র্যাক্টগুলিতে [রোল বেসড অ্যাক্সেস কন্ট্রোল (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control) থাকে। + +সরলতার জন্য, এই নিবন্ধে আমরা `Ownable` ব্যবহার করি। + +### কন্ট্র্যাক্ট ফ্রিজ এবং আনফ্রিজ করা {#freezing-and-thawing-contracts} + +কন্ট্র্যাক্ট ফ্রিজ এবং আনফ্রিজ করার জন্য কয়েকটি পরিবর্তন প্রয়োজন: + +- কোন ঠিকানাগুলি ফ্রিজ করা হয়েছে তার হিসাব রাখার জন্য ঠিকানা থেকে [বুলিয়ানে](https://en.wikipedia.org/wiki/Boolean_data_type) একটি [ম্যাপিং](https://www.tutorialspoint.com/solidity/solidity_mappings.htm)। সমস্ত মান প্রাথমিকভাবে শূন্য থাকে, যা বুলিয়ান মানের জন্য মিথ্যা হিসাবে ব্যাখ্যা করা হয়। এটাই আমরা চাই কারণ ডিফল্টরূপে অ্যাকাউন্টগুলি ফ্রিজ করা হয় না। + + ```solidity + mapping(address => bool) public frozenAccounts; + ``` + +- যখন একটি অ্যাকাউন্ট ফ্রিজ বা আনফ্রিজ করা হয় তখন আগ্রহী যে কাউকে জানানোর জন্য [ইভেন্ট](https://www.tutorialspoint.com/solidity/solidity_events.htm)। টেকনিক্যালি বলতে গেলে, এই কাজগুলির জন্য ইভেন্টগুলির প্রয়োজন নেই, তবে এটি অফচেইন কোডকে এই ইভেন্টগুলি শুনতে এবং কী ঘটছে তা জানতে সাহায্য করে। যখন অন্য কারও জন্য প্রাসঙ্গিক কিছু ঘটে তখন একটি স্মার্ট কন্ট্র্যাক্টের জন্য সেগুলি নির্গত করাকে ভালো আচরণ বলে মনে করা হয়। + + ইভেন্টগুলি ইনডেক্স করা হয় তাই একটি অ্যাকাউন্ট কতবার ফ্রিজ বা আনফ্রিজ করা হয়েছে তা অনুসন্ধান করা সম্ভব হবে। + + ```solidity + // যখন অ্যাকাউন্টগুলি ফ্রিজ বা আনফ্রিজ করা হয় + event AccountFrozen(address indexed _addr); + event AccountThawed(address indexed _addr); + ``` + +- অ্যাকাউন্ট ফ্রিজ এবং আনফ্রিজ করার জন্য ফাংশন। এই দুটি ফাংশন প্রায় অভিন্ন, তাই আমরা শুধুমাত্র ফ্রিজ ফাংশনটি দেখব। + + ```solidity + function freezeAccount(address addr) + public + onlyOwner + ``` + + [`public`](https://www.tutorialspoint.com/solidity/solidity_contracts.htm) হিসাবে চিহ্নিত ফাংশনগুলি অন্যান্য স্মার্ট কন্ট্র্যাক্ট থেকে বা সরাসরি একটি লেনদেনের মাধ্যমে কল করা যেতে পারে। + + ```solidity + { + require(!frozenAccounts[addr], "অ্যাকাউন্ট ইতিমধ্যে ফ্রিজ করা আছে"); + frozenAccounts[addr] = true; + emit AccountFrozen(addr); + } // freezeAccount + ``` + + যদি অ্যাকাউন্টটি ইতিমধ্যে ফ্রিজ করা থাকে, তবে রিভার্ট করুন। অন্যথায়, এটি ফ্রিজ করুন এবং একটি ইভেন্ট `emit` করুন। + +- একটি ফ্রিজ করা অ্যাকাউন্ট থেকে অর্থ সরানো প্রতিরোধ করতে `_beforeTokenTransfer` পরিবর্তন করুন। মনে রাখবেন যে ফ্রিজ করা অ্যাকাউন্টে এখনও অর্থ ট্রান্সফার করা যেতে পারে। + + ```solidity + require(!frozenAccounts[from], "অ্যাকাউন্টটি ফ্রিজ করা আছে"); + ``` + +### অ্যাসেট ক্লিনআপ {#asset-cleanup} + +এই কন্ট্র্যাক্ট দ্বারা ধারণ করা ERC-20 টোকেন রিলিজ করার জন্য আমাদের সেই টোকেন কন্ট্র্যাক্টে একটি ফাংশন কল করতে হবে, হয় [`transfer`](https://eips.ethereum.org/EIPS/eip-20#transfer) অথবা [`approve`](https://eips.ethereum.org/EIPS/eip-20#approve)। এই ক্ষেত্রে অ্যালাওয়েন্সের ওপর গ্যাস নষ্ট করার কোনো মানে নেই, আমরা সরাসরি ট্রান্সফার করতে পারি। + +```solidity + function cleanupERC20( + address erc20, + address dest + ) + public + onlyOwner + { + IERC20 token = IERC20(erc20); +``` + +যখন আমরা ঠিকানাটি পাই তখন একটি কন্ট্র্যাক্টের জন্য একটি অবজেক্ট তৈরি করার সিনট্যাক্স এটি। আমরা এটি করতে পারি কারণ আমাদের সোর্স কোডের অংশ হিসাবে ERC20 টোকেনের জন্য সংজ্ঞা রয়েছে (লাইন 4 দেখুন), এবং সেই ফাইলে [IERC20-এর জন্য সংজ্ঞা](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol) অন্তর্ভুক্ত রয়েছে, যা একটি OpenZeppelin ERC-20 কন্ট্র্যাক্টের ইন্টারফেস। + +```solidity + uint balance = token.balanceOf(address(this)); + token.transfer(dest, balance); + } +``` + +এটি একটি ক্লিনআপ ফাংশন, তাই সম্ভবত আমরা কোনো টোকেন রেখে যেতে চাই না। ব্যবহারকারীর কাছ থেকে ম্যানুয়ালি ব্যালেন্স পাওয়ার পরিবর্তে, আমরা প্রক্রিয়াটি স্বয়ংক্রিয় করতে পারি। + +## উপসংহার {#conclusion} + +এটি একটি নিখুঁত সমাধান নয় - "ব্যবহারকারী ভুল করেছে" সমস্যার কোনো নিখুঁত সমাধান নেই। তবে, এই ধরনের চেক ব্যবহার করে অন্তত কিছু ভুল প্রতিরোধ করা যেতে পারে। অ্যাকাউন্ট ফ্রিজ করার ক্ষমতা, যদিও বিপজ্জনক, হ্যাকারকে চুরি করা তহবিল থেকে বঞ্চিত করে নির্দিষ্ট হ্যাকের ক্ষতি সীমিত করতে ব্যবহার করা যেতে পারে। + +[আমার আরও কাজের জন্য এখানে দেখুন](https://cryptodocguy.pro/)। diff --git a/public/content/translations/bn/developers/tutorials/ethereum-for-web2-auth/index.md b/public/content/translations/bn/developers/tutorials/ethereum-for-web2-auth/index.md new file mode 100644 index 00000000000..0361234926a --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/ethereum-for-web2-auth/index.md @@ -0,0 +1,886 @@ +--- +title: "web2 প্রমাণীকরণের জন্য ইথেরিয়াম ব্যবহার করা" +description: "এই টিউটোরিয়ালটি পড়ার পরে, একজন ডেভেলপার SAML লগইনের সাথে ইথেরিয়াম লগইন (web3) একীভূত করতে সক্ষম হবেন, যা web2-তে একক সাইন-অন এবং অন্যান্য সম্পর্কিত পরিষেবা প্রদানের জন্য ব্যবহৃত একটি স্ট্যান্ডার্ড। এটি ইথেরিয়াম স্বাক্ষরগুলির মাধ্যমে web2 রিসোর্সগুলিকে প্রমাণীকৃত করার অনুমতি দেয়, যার ব্যবহারকারী বৈশিষ্ট্যগুলি অ্যাটেস্টেশনগুলি থেকে আসে।" +author: Ori Pomerantz +tags: [ "web2", "প্রমাণীকরণ", "eas" ] +skill: beginner +lang: bn +published: 2025-04-30 +--- + +## ভূমিকা + +[SAML](https://www.onelogin.com/learn/saml) হল web2-এ ব্যবহৃত একটি স্ট্যান্ডার্ড যা একজন [পরিচয় প্রদানকারী (IdP)](https://en.wikipedia.org/wiki/Identity_provider#SAML_identity_provider)-কে [পরিষেবা প্রদানকারী (SP)](https://en.wikipedia.org/wiki/Service_provider_\(SAML\))-দের জন্য ব্যবহারকারীর তথ্য প্রদান করার অনুমতি দেয়। + +এই টিউটোরিয়ালে আপনি শিখবেন কিভাবে ইথেরিয়াম স্বাক্ষরকে SAML-এর সাথে একীভূত করতে হয়, যাতে ব্যবহারকারীরা তাদের ইথেরিয়াম ওয়ালেট ব্যবহার করে সেইসব web2 পরিষেবাগুলিতে নিজেদের প্রমাণীকরণ করতে পারে যা এখনও স্থানীয়ভাবে ইথেরিয়াম সমর্থন করে না। + +মনে রাখবেন যে এই টিউটোরিয়ালটি দুটি পৃথক দর্শকের জন্য লেখা হয়েছে: + +- ইথেরিয়াম ব্যবহারকারী যারা ইথেরিয়াম বোঝেন এবং তাদের SAML শিখতে হবে +- Web2 ব্যবহারকারী যারা SAML এবং web2 প্রমাণীকরণ বোঝেন এবং তাদের ইথেরিয়াম শিখতে হবে + +ফলস্বরূপ, এতে এমন অনেক প্রাথমিক উপাদান থাকবে যা আপনি ইতিমধ্যেই জানেন। আপনি নির্দ্বিধায় এটি এড়িয়ে যেতে পারেন। + +### ইথেরিয়াম ব্যবহারকারীদের জন্য SAML + +SAML একটি কেন্দ্রীভূত প্রোটোকল। একজন পরিষেবা প্রদানকারী (SP) একজন পরিচয় প্রদানকারী (IdP) থেকে শুধুমাত্র তখনই দাবি (যেমন, "এটি আমার ব্যবহারকারী জন, তার A, B, এবং C করার অনুমতি থাকা উচিত") গ্রহণ করে, যদি তার সাথে এটির একটি পূর্ব-বিদ্যমান বিশ্বাস সম্পর্ক থাকে, অথবা সেই [সার্টিফিকেট কর্তৃপক্ষ](https://www.ssl.com/article/what-is-a-certificate-authority-ca/)-এর সাথে যা সেই IdP-র সার্টিফিকেট স্বাক্ষর করেছে। + +উদাহরণস্বরূপ, SP হতে পারে একটি ট্রাভেল এজেন্সি যা কোম্পানিগুলিকে ভ্রমণ পরিষেবা প্রদান করে, এবং IdP হতে পারে একটি কোম্পানির অভ্যন্তরীণ ওয়েব সাইট। যখন কর্মচারীদের ব্যবসায়িক ভ্রমণের জন্য বুকিং করতে হয়, তখন ট্রাভেল এজেন্সি তাদের আসল ভ্রমণের বুকিং করার আগে কোম্পানির দ্বারা প্রমাণীকরণের জন্য পাঠায়। + +![ধাপে ধাপে SAML প্রক্রিয়া](./fig-01-saml.png) + +এইভাবেই তিনটি সত্তা, ব্রাউজার, SP, এবং IdP, অ্যাক্সেসের জন্য আলোচনা করে। SP-কে আগে থেকে ব্রাউজার ব্যবহারকারী সম্পর্কে কিছু জানার প্রয়োজন নেই, শুধু IdP-কে বিশ্বাস করলেই চলে। + +### SAML ব্যবহারকারীদের জন্য ইথেরিয়াম + +ইথেরিয়াম একটি বিকেন্দ্রীভূত সিস্টেম। + +![ইথেরিয়াম লগঅন](./fig-02-eth-logon.png) + +ব্যবহারকারীদের একটি প্রাইভেট কী থাকে (সাধারণত একটি ব্রাউজার এক্সটেনশনে রাখা হয়)। প্রাইভেট কী থেকে আপনি একটি পাবলিক কী এবং তা থেকে একটি 20-বাইটের অ্যাড্রেস তৈরি করতে পারেন। যখন ব্যবহারকারীদের একটি সিস্টেমে লগ ইন করতে হয়, তখন তাদের একটি নন্স (একবার ব্যবহারযোগ্য মান) সহ একটি বার্তায় স্বাক্ষর করতে বলা হয়। সার্ভার যাচাই করতে পারে যে স্বাক্ষরটি সেই অ্যাড্রেস দ্বারা তৈরি করা হয়েছে। + +![অ্যাটেস্টেশন থেকে অতিরিক্ত ডেটা পাওয়া](./fig-03-eas-data.png) + +স্বাক্ষরটি শুধুমাত্র ইথেরিয়াম অ্যাড্রেস যাচাই করে। অন্যান্য ব্যবহারকারীর বৈশিষ্ট্যগুলি পেতে, আপনি সাধারণত [অ্যাটেস্টেশন](https://attest.org/) ব্যবহার করেন। একটি অ্যাটেস্টেশনে সাধারণত এই ফিল্ডগুলি থাকে: + +- **অ্যাটেস্টর**, যে অ্যাড্রেসটি অ্যাটেস্টেশনটি তৈরি করেছে +- **প্রাপক**, যে অ্যাড্রেসের জন্য অ্যাটেস্টেশনটি প্রযোজ্য +- **ডেটা**, যে ডেটা প্রত্যয়িত করা হচ্ছে, যেমন নাম, অনুমতি, ইত্যাদি। +- **স্কিমা**, ডেটা ব্যাখ্যা করার জন্য ব্যবহৃত স্কিমার ID। + +ইথেরিয়ামের বিকেন্দ্রীভূত প্রকৃতির কারণে, যেকোনো ব্যবহারকারী অ্যাটেস্টেশন তৈরি করতে পারেন। আমরা কোন অ্যাটেস্টেশনগুলিকে নির্ভরযোগ্য বলে মনে করি তা শনাক্ত করার জন্য অ্যাটেস্টরের পরিচয় গুরুত্বপূর্ণ। + +## সেটআপ + +প্রথম ধাপ হল একটি SAML SP এবং একটি SAML IdP-র মধ্যে যোগাযোগ স্থাপন করা। + +1. সফ্টওয়্যারটি ডাউনলোড করুন। এই নিবন্ধটির জন্য নমুনা সফ্টওয়্যারটি [গিটহাবে](https://github.com/qbzzt/250420-saml-ethereum) রয়েছে। বিভিন্ন পর্যায়গুলি বিভিন্ন ব্রাঞ্চে সংরক্ষণ করা হয়, এই পর্যায়ের জন্য আপনার `saml-only` প্রয়োজন + + ```sh + git clone https://github.com/qbzzt/250420-saml-ethereum -b saml-only + cd 250420-saml-ethereum + pnpm install + ``` + +2. স্ব-স্বাক্ষরিত সার্টিফিকেটসহ কী তৈরি করুন। এর মানে হল যে কী নিজেই তার সার্টিফিকেট কর্তৃপক্ষ, এবং এটি পরিষেবা প্রদানকারীর কাছে ম্যানুয়ালি আমদানি করতে হবে। আরও তথ্যের জন্য [OpenSSL ডক্স](https://docs.openssl.org/master/man1/openssl-req/) দেখুন। + + ```sh + mkdir keys + cd keys + openssl req -new -x509 -days 365 -nodes -sha256 -out saml-sp.crt -keyout saml-sp.pem -subj /CN=sp/ + openssl req -new -x509 -days 365 -nodes -sha256 -out saml-idp.crt -keyout saml-idp.pem -subj /CN=idp/ + cd .. + ``` + +3. সার্ভারগুলি শুরু করুন (SP এবং IdP উভয়ই) + + ```sh + pnpm start + ``` + +4. URL [http://localhost:3000/](http://localhost:3000/) এ SP ব্রাউজ করুন এবং IdP-তে (পোর্ট 3001) পুনঃনির্দেশিত হতে বোতামে ক্লিক করুন। + +5. IdP-কে আপনার ইমেল অ্যাড্রেস দিন এবং **পরিষেবা প্রদানকারীতে লগইন করুন**-এ ক্লিক করুন। দেখুন যে আপনাকে পরিষেবা প্রদানকারীতে (পোর্ট 3000) আবার পুনঃনির্দেশিত করা হয়েছে এবং এটি আপনাকে আপনার ইমেল অ্যাড্রেস দ্বারা চেনে। + +### বিস্তারিত ব্যাখ্যা + +ধাপে ধাপে যা ঘটে তা এখানে দেওয়া হল: + +![ইথেরিয়াম ছাড়া সাধারণ SAML লগঅন](./fig-04-saml-no-eth.png) + +#### src/config.mts + +এই ফাইলটিতে আইডেন্টিটি প্রোভাইডার এবং সার্ভিস প্রোভাইডার উভয়ের কনফিগারেশন রয়েছে। সাধারণত এই দুটি ভিন্ন সত্তা হবে, কিন্তু এখানে আমরা সরলতার জন্য কোড শেয়ার করতে পারি। + +```typescript +const fs = await import("fs") + +const protocol="http" +``` + +আপাতত আমরা শুধু পরীক্ষা করছি, তাই HTTP ব্যবহার করা ঠিক আছে। + +```typescript +export const spCert = fs.readFileSync("keys/saml-sp.crt").toString() +export const idpCert = fs.readFileSync("keys/saml-idp.crt").toString() +``` + +পাবলিক কীগুলি পড়ুন, যা সাধারণত উভয় উপাদানের জন্য উপলব্ধ থাকে (এবং হয় সরাসরি বিশ্বস্ত, অথবা একটি বিশ্বস্ত সার্টিফিকেট কর্তৃপক্ষ দ্বারা স্বাক্ষরিত)। + +```typescript +export const spPort = 3000 +export const spHostname = "localhost" +export const spDir = "sp" + +export const idpPort = 3001 +export const idpHostname = "localhost" +export const idpDir = "idp" + +export const spUrl = `${protocol}://${spHostname}:${spPort}/${spDir}` +export const idpUrl = `${protocol}://${idpHostname}:${idpPort}/${idpDir}` +``` + +উভয় উপাদানের জন্য URL। + +```typescript +export const spPublicData = { +``` + +পরিষেবা প্রদানকারীর জন্য পাবলিক ডেটা। + +```typescript + entityID: `${spUrl}/metadata`, +``` + +প্রচলিত নিয়ম অনুযায়ী, SAML-এ `entityID` হল সেই URL যেখানে সত্তার মেটাডেটা পাওয়া যায়। এই মেটাডেটা এখানকার পাবলিক ডেটার সাথে সামঞ্জস্যপূর্ণ, তবে এটি XML ফর্ম্যাটে থাকে। + +```typescript + wantAssertionsSigned: true, + authnRequestsSigned: false, + signingCert: spCert, + allowCreate: true, + assertionConsumerService: [{ + Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + Location: `${spUrl}/assertion`, + }] + } +``` + +আমাদের উদ্দেশ্যের জন্য সবচেয়ে গুরুত্বপূর্ণ সংজ্ঞা হল `assertionConsumerServer`। এর মানে হল যে পরিষেবা প্রদানকারীর কাছে কিছু দাবি করতে (উদাহরণস্বরূপ, "যে ব্যবহারকারী আপনাকে এই তথ্য পাঠাচ্ছে সে হল somebody@example.com") আমাদের [HTTP POST](https://www.w3schools.com/tags/ref_httpmethods.asp) ব্যবহার করে `http://localhost:3000/sp/assertion` URL-এ যেতে হবে। + +```typescript +export const idpPublicData = { + entityID: `${idpUrl}/metadata`, + signingCert: idpCert, + wantAuthnRequestsSigned: false, + singleSignOnService: [{ + Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", + Location: `${idpUrl}/login` + }], + singleLogoutService: [{ + Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", + Location: `${idpUrl}/logout` + }], + } +``` + +পরিচয় প্রদানকারীর জন্য পাবলিক ডেটা একই রকম। এটি নির্দিষ্ট করে যে একজন ব্যবহারকারীকে লগ ইন করতে আপনাকে `http://localhost:3001/idp/login`-এ POST করতে হবে এবং একজন ব্যবহারকারীকে লগ আউট করতে আপনাকে `http://localhost:3001/idp/logout`-এ POST করতে হবে। + +#### src/sp.mts + +এটি সেই কোড যা একটি পরিষেবা প্রদানকারীকে বাস্তবায়ন করে। + +```typescript +import * as config from "./config.mts" +const fs = await import("fs") +const saml = await import("samlify") +``` + +আমরা SAML বাস্তবায়নের জন্য [`samlify`](https://www.npmjs.com/package/samlify) লাইব্রেরি ব্যবহার করি। + +```typescript +import * as validator from "@authenio/samlify-node-xmllint" +saml.setSchemaValidator(validator) +``` + +`samlify` লাইব্রেরি আশা করে যে একটি প্যাকেজ XML সঠিক কিনা, প্রত্যাশিত পাবলিক কী দিয়ে স্বাক্ষরিত কিনা, ইত্যাদি যাচাই করবে। আমরা এই উদ্দেশ্যে [`@authenio/samlify-node-xmllint`](https://www.npmjs.com/package/@authenio/samlify-node-xmllint) ব্যবহার করি। + +```typescript +const express = (await import("express")).default +const spRouter = express.Router() +const app = express() +``` + +একটি [`express`](https://expressjs.com/) [`Router`](https://expressjs.com/en/5x/api.html#router) হল একটি "মিনি ওয়েব সাইট" যা একটি ওয়েব সাইটের ভিতরে মাউন্ট করা যেতে পারে। এই ক্ষেত্রে, আমরা এটি সমস্ত পরিষেবা প্রদানকারীর সংজ্ঞাগুলিকে একত্রিত করার জন্য ব্যবহার করি। + +```typescript +const spPrivateKey = fs.readFileSync("keys/saml-sp.pem").toString() + +const sp = saml.ServiceProvider({ + privateKey: spPrivateKey, + ...config.spPublicData +}) +``` + +পরিষেবা প্রদানকারীর নিজস্ব উপস্থাপনা হল সমস্ত পাবলিক ডেটা এবং তথ্য স্বাক্ষর করার জন্য এটি যে প্রাইভেট কী ব্যবহার করে। + +```typescript +const idp = saml.IdentityProvider(config.idpPublicData); +``` + +পাবলিক ডেটাতে পরিচয় প্রদানকারী সম্পর্কে পরিষেবা প্রদানকারীর যা কিছু জানা দরকার তা রয়েছে। + +```typescript +spRouter.get(`/metadata`, + (req, res) => res.header("Content-Type", "text/xml").send(sp.getMetadata()) +) +``` + +অন্যান্য SAML উপাদানগুলির সাথে আন্তঃকার্যক্ষমতা সক্ষম করার জন্য, পরিষেবা এবং পরিচয় প্রদানকারীদের তাদের পাবলিক ডেটা (যাকে মেটাডেটা বলা হয়) `/metadata`-তে XML ফর্ম্যাটে উপলব্ধ থাকা উচিত। + +```typescript +spRouter.post(`/assertion`, +``` + +এটি সেই পৃষ্ঠা যা ব্রাউজার দ্বারা নিজের পরিচয় প্রদানের জন্য অ্যাক্সেস করা হয়। এই দাবিতে ব্যবহারকারীর শনাক্তকারী (এখানে আমরা ইমেল অ্যাড্রেস ব্যবহার করি) অন্তর্ভুক্ত থাকে এবং এতে অতিরিক্ত বৈশিষ্ট্যও অন্তর্ভুক্ত থাকতে পারে। এটি উপরের সিকোয়েন্স ডায়াগ্রামের ৭ নং ধাপের জন্য হ্যান্ডলার। + +```typescript + async (req, res) => { + // console.log(`SAML response:\n${Buffer.from(req.body.SAMLResponse, 'base64').toString('utf-8')}`) +``` + +আপনি দাবিতে প্রদত্ত XML ডেটা দেখতে কমেন্ট করা কমান্ডটি ব্যবহার করতে পারেন। এটি [বেস64 এনকোডেড](https://en.wikipedia.org/wiki/Base64)। + +```typescript + try { + const loginResponse = await sp.parseLoginResponse(idp, 'post', req); +``` + +পরিচয় সার্ভার থেকে লগইন অনুরোধটি পার্স করুন। + +```typescript + res.send(` + + +

Hello ${loginResponse.extract.nameID}

+ + + `) + res.send(); +``` + +একটি HTML প্রতিক্রিয়া পাঠান, শুধু ব্যবহারকারীকে দেখানোর জন্য যে আমরা লগইন পেয়েছি। + +```typescript + } catch (err) { + console.error('Error processing SAML response:', err); + res.status(400).send('SAML authentication failed'); + } + } +) +``` + +ব্যর্থতার ক্ষেত্রে ব্যবহারকারীকে জানান। + +```typescript +spRouter.get('/login', +``` + +ব্রাউজার যখন এই পৃষ্ঠাটি পাওয়ার চেষ্টা করে তখন একটি লগইন অনুরোধ তৈরি করুন। এটি উপরের সিকোয়েন্স ডায়াগ্রামের ১ নং ধাপের জন্য হ্যান্ডলার। + +```typescript + async (req, res) => { + const loginRequest = await sp.createLoginRequest(idp, "post") +``` + +একটি লগইন অনুরোধ পোস্ট করার জন্য তথ্য পান। + +```typescript + res.send(` + + + +``` + +এই পৃষ্ঠাটি ফর্মটি (নীচে দেখুন) স্বয়ংক্রিয়ভাবে জমা দেয়। এইভাবে ব্যবহারকারীকে পুনঃনির্দেশিত হওয়ার জন্য কিছু করতে হবে না। এটি উপরের সিকোয়েন্স ডায়াগ্রামের ২ নং ধাপ। + +```typescript +
+``` + +`loginRequest.entityEndpoint`-এ পোস্ট করুন (পরিচয় প্রদানকারী এন্ডপয়েন্টের URL)। + +```typescript + +``` + +ইনপুটের নাম হল `loginRequest.type` (`SAMLRequest`)। সেই ফিল্ডের বিষয়বস্তু হল `loginRequest.context`, যা আবার XML এবং বেস64 এনকোডেড। + +```typescript +
+ + + `) + } +) + +app.use(express.urlencoded({extended: true})) +``` + +[এই মিডলওয়্যারটি](https://expressjs.com/en/5x/api.html#express.urlencoded) [HTTP অনুরোধের](https://www.tutorialspoint.com/http/http_requests.htm) বডি পড়ে। ডিফল্টভাবে express এটিকে উপেক্ষা করে, কারণ বেশিরভাগ অনুরোধের জন্য এটির প্রয়োজন হয় না। আমাদের এটির প্রয়োজন কারণ POST বডি ব্যবহার করে। + +```typescript +app.use(`/${config.spDir}`, spRouter) +``` + +পরিষেবা প্রদানকারী ডিরেক্টরিতে (`/sp`) রাউটারটি মাউন্ট করুন। + +```typescript +app.get("/", (req, res) => { + res.send(` + + + + + + `) +}) +``` + +যদি কোনো ব্রাউজার রুট ডিরেক্টরি পাওয়ার চেষ্টা করে, তবে তাকে লগইন পৃষ্ঠার একটি লিঙ্ক দিন। + +```typescript +app.listen(config.spPort, () => { + console.log(`service provider is running on http://${config.spHostname}:${config.spPort}`) +}) +``` + +এই express অ্যাপ্লিকেশনটির সাথে `spPort`-এ শুনুন। + +#### src/idp.mts + +এটি হল পরিচয় প্রদানকারী। এটি পরিষেবা প্রদানকারীর মতোই, নীচের ব্যাখ্যাগুলি সেই অংশগুলির জন্য যা ভিন্ন। + +```typescript +const xmlParser = new (await import("fast-xml-parser")).XMLParser( + { + ignoreAttributes: false, // Preserve attributes + attributeNamePrefix: "@_", // Prefix for attributes + } +) +``` + +পরিষেবা প্রদানকারীর কাছ থেকে আমরা যে XML অনুরোধটি পাই তা আমাদের পড়তে এবং বুঝতে হবে। + +```typescript +const getLoginPage = requestId => ` +``` + +এই ফাংশনটি স্বয়ংক্রিয়-জমা দেওয়া ফর্ম সহ পৃষ্ঠাটি তৈরি করে যা উপরের সিকোয়েন্স ডায়াগ্রামের ৪ নং ধাপে ফেরত দেওয়া হয়। + +```typescript + + + লগইন পৃষ্ঠা + + +

লগইন পৃষ্ঠা

+
+ + ইমেল অ্যাড্রেস: +
+ +``` + +আমরা পরিষেবা প্রদানকারীকে দুটি ফিল্ড পাঠাই: + +1. যে `requestId`-তে আমরা প্রতিক্রিয়া জানাচ্ছি। +2. ব্যবহারকারীর শনাক্তকারী (আপাতত আমরা ব্যবহারকারীর দেওয়া ইমেল অ্যাড্রেস ব্যবহার করি)। + +```typescript +
+ + + +const idpRouter = express.Router() + +idpRouter.post("/loginSubmitted", async (req, res) => { + const loginResponse = await idp.createLoginResponse( +``` + +এটি উপরের সিকোয়েন্স ডায়াগ্রামের ৫ নং ধাপের জন্য হ্যান্ডলার। [`idp.createLoginResponse`](https://github.com/tngan/samlify/blob/master/src/entity-idp.ts#L73-L125) লগইন প্রতিক্রিয়া তৈরি করে। + +```typescript + sp, + { + authnContextClassRef: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport', + audience: sp.entityID, +``` + +অডিয়েন্স হল পরিষেবা প্রদানকারী। + +```typescript + extract: { + request: { + id: req.body.requestId + } + }, +``` + +অনুরোধ থেকে নিষ্কাশিত তথ্য। অনুরোধে আমাদের যে একটি প্যারামিটার নিয়ে চিন্তা করতে হয় তা হল requestId, যা পরিষেবা প্রদানকারীকে অনুরোধ এবং তাদের প্রতিক্রিয়াগুলির সাথে মেলাতে দেয়। + +```typescript + signingKey: { privateKey: idpPrivateKey, publicKey: config.idpCert } // Ensure signing +``` + +প্রতিক্রিয়া স্বাক্ষর করার জন্য ডেটা পেতে আমাদের `signingKey` প্রয়োজন। পরিষেবা প্রদানকারী স্বাক্ষরবিহীন অনুরোধ বিশ্বাস করে না। + +```typescript + }, + "post", + { + email: req.body.email +``` + +এটি হল সেই ফিল্ড যেখানে ব্যবহারকারীর তথ্য থাকে যা আমরা পরিষেবা প্রদানকারীকে ফেরত পাঠাই। + +```typescript + } + ); + + res.send(` + + + + +
+ +
+ + + `) +}) +``` + +আবার, একটি স্বয়ংক্রিয়ভাবে জমা দেওয়া ফর্ম ব্যবহার করুন। এটি উপরের সিকোয়েন্স ডায়াগ্রামের ৬ নং ধাপ। + +```typescript + +// IdP endpoint for login requests +idpRouter.post(`/login`, +``` + +এটি সেই এন্ডপয়েন্ট যা পরিষেবা প্রদানকারীর কাছ থেকে একটি লগইন অনুরোধ গ্রহণ করে। এটি উপরের সিকোয়েন্স ডায়াগ্রামের ৩ নং ধাপের হ্যান্ডলার। + +```typescript + async (req, res) => { + try { + // Workaround because I couldn't get parseLoginRequest to work. + // const loginRequest = await idp.parseLoginRequest(sp, 'post', req) + const samlRequest = xmlParser.parse(Buffer.from(req.body.SAMLRequest, 'base64').toString('utf-8')) + res.send(getLoginPage(samlRequest["samlp:AuthnRequest"]["@_ID"])) +``` + +প্রমাণীকরণ অনুরোধের আইডি পড়ার জন্য আমাদের [`idp.parseLoginRequest`](https://github.com/tngan/samlify/blob/master/src/entity-idp.ts#L127-L144) ব্যবহার করতে পারা উচিত। তবে, আমি এটি কাজ করাতে পারিনি এবং এর উপর অনেক সময় ব্যয় করা সার্থক ছিল না তাই আমি শুধু একটি [সাধারণ-উদ্দেশ্যের XML পার্সার](https://www.npmjs.com/package/fast-xml-parser) ব্যবহার করেছি। আমাদের যে তথ্য প্রয়োজন তা হল `` ট্যাগের ভিতরের `ID` অ্যাট্রিবিউট, যা XML-এর শীর্ষ স্তরে রয়েছে। + +## ইথেরিয়াম স্বাক্ষর ব্যবহার করা + +এখন যেহেতু আমরা পরিষেবা প্রদানকারীর কাছে একজন ব্যবহারকারীর পরিচয় পাঠাতে পারি, পরবর্তী পদক্ষেপ হল বিশ্বস্ত উপায়ে ব্যবহারকারীর পরিচয় সংগ্রহ করা। Viem আমাদের শুধু ওয়ালেট থেকে ব্যবহারকারীর অ্যাড্রেস জিজ্ঞাসা করার অনুমতি দেয়, কিন্তু এর মানে হল ব্রাউজার থেকে তথ্য চাওয়া। আমরা ব্রাউজার নিয়ন্ত্রণ করি না, তাই আমরা এর থেকে প্রাপ্ত প্রতিক্রিয়াকে স্বয়ংক্রিয়ভাবে বিশ্বাস করতে পারি না। + +এর পরিবর্তে, IdP ব্রাউজারকে স্বাক্ষর করার জন্য একটি স্ট্রিং পাঠাবে। যদি ব্রাউজারের ওয়ালেটটি এই স্ট্রিংটি স্বাক্ষর করে, তার মানে হল এটি সত্যিই সেই অ্যাড্রেস (অর্থাৎ, এটি সেই প্রাইভেট কী জানে যা অ্যাড্রেসের সাথে সম্পর্কিত)। + +এটি কার্যকর দেখতে, বিদ্যমান IdP এবং SP বন্ধ করুন এবং এই কমান্ডগুলি চালান: + +```sh +git checkout eth-signatures +pnpm install +pnpm start +``` + +তারপর [SP-তে](http://localhost:3000) ব্রাউজ করুন এবং নির্দেশাবলী অনুসরণ করুন। + +মনে রাখবেন যে এই মুহূর্তে আমরা জানি না কিভাবে ইথেরিয়াম অ্যাড্রেস থেকে ইমেল অ্যাড্রেস পেতে হয়, তাই এর পরিবর্তে আমরা SP-কে `@bad.email.address` রিপোর্ট করি। + +### বিস্তারিত ব্যাখ্যা + +পরিবর্তনগুলি আগের ডায়াগ্রামের ৪-৫ নং ধাপে রয়েছে। + +![একটি ইথেরিয়াম স্বাক্ষরসহ SAML](./fig-05-saml-w-signature.png) + +আমরা শুধুমাত্র `idp.mts` ফাইলটি পরিবর্তন করেছি। এখানে পরিবর্তিত অংশগুলি দেওয়া হল। + +```typescript +import { v4 as uuidv4 } from 'uuid' +import { verifyMessage } from 'viem' +``` + +আমাদের এই দুটি অতিরিক্ত লাইব্রেরি প্রয়োজন। আমরা [নন্স](https://en.wikipedia.org/wiki/Cryptographic_nonce) মান তৈরি করতে [`uuid`](https://www.npmjs.com/package/uuid) ব্যবহার করি। মানটি নিজেই গুরুত্বপূর্ণ নয়, শুধু এটি একবারই ব্যবহৃত হয়। + +[`viem`](https://viem.sh/) লাইব্রেরি আমাদের ইথেরিয়াম সংজ্ঞা ব্যবহার করতে দেয়। এখানে আমাদের এটি প্রয়োজন স্বাক্ষরটি সত্যিই বৈধ কিনা তা যাচাই করার জন্য। + +```typescript +const loginPrompt = "পরিষেবা প্রদানকারীতে অ্যাক্সেস করতে, এই ননসে স্বাক্ষর করুন: " +``` + +ওয়ালেটটি বার্তাটিতে স্বাক্ষর করার জন্য ব্যবহারকারীর কাছে অনুমতি চায়। একটি বার্তা যা শুধুমাত্র একটি নন্স, তা ব্যবহারকারীদের বিভ্রান্ত করতে পারে, তাই আমরা এই প্রম্পটটি অন্তর্ভুক্ত করি। + +```typescript +// Keep requestIDs here +let nonces = {} +``` + +এটিতে প্রতিক্রিয়া জানাতে আমাদের অনুরোধের তথ্য প্রয়োজন। আমরা এটি অনুরোধের সাথে পাঠাতে পারি (ধাপ 4), এবং এটি ফেরত পেতে পারি (ধাপ 5)। তবে, আমরা ব্রাউজার থেকে প্রাপ্ত তথ্য বিশ্বাস করতে পারি না, যা একজন সম্ভাব্য প্রতিকূল ব্যবহারকারীর নিয়ন্ত্রণে থাকে। তাই এটি এখানে সংরক্ষণ করা ভাল, নন্সকে কী হিসাবে ব্যবহার করে। + +মনে রাখবেন যে আমরা সরলতার জন্য এটি এখানে একটি ভেরিয়েবল হিসাবে করছি। তবে, এর বেশ কিছু অসুবিধা রয়েছে: + +- আমরা পরিষেবা অস্বীকার (denial of service) আক্রমণের জন্য ঝুঁকিপূর্ণ। একজন ক্ষতিকারক ব্যবহারকারী একাধিকবার লগ অন করার চেষ্টা করতে পারে, যা আমাদের মেমরি পূর্ণ করে দেবে। +- যদি IdP প্রক্রিয়াটি পুনরায় চালু করার প্রয়োজন হয়, আমরা বিদ্যমান মানগুলি হারিয়ে ফেলি। +- আমরা একাধিক প্রক্রিয়ার মধ্যে লোড ব্যালেন্স করতে পারি না, কারণ প্রতিটির নিজস্ব ভেরিয়েবল থাকবে। + +একটি প্রোডাকশন সিস্টেমে আমরা একটি ডেটাবেস ব্যবহার করব এবং কোনো ধরনের মেয়াদোত্তীর্ণের ব্যবস্থা প্রয়োগ করব। + +```typescript +const getSignaturePage = requestId => { + const nonce = uuidv4() + nonces[nonce] = requestId +``` + +একটি নন্স তৈরি করুন, এবং ভবিষ্যতের ব্যবহারের জন্য `requestId` সংরক্ষণ করুন। + +```typescript + return ` + + + + + +

অনুগ্রহ করে স্বাক্ষর করুন

+ +
+ + + +` +} +``` + +বাকিটা শুধু স্ট্যান্ডার্ড HTML। + +```typescript +idpRouter.get("/signature/:nonce/:account/:signature", async (req, res) => { +``` + +এটি সিকোয়েন্স ডায়াগ্রামের ৫ নং ধাপের জন্য হ্যান্ডলার। + +```typescript + const requestId = nonces[req.params.nonce] + if (requestId === undefined) { + res.send("Bad nonce") + return ; + } + + nonces[req.params.nonce] = undefined +``` + +অনুরোধ আইডি পান, এবং `nonces` থেকে নন্সটি মুছে ফেলুন যাতে এটি পুনরায় ব্যবহার করা না যায়। + +```typescript + try { +``` + +যেহেতু স্বাক্ষরটি অনেক উপায়ে অবৈধ হতে পারে, তাই আমরা এটিকে একটি `try ...`-তে মোড়ানো। `catch` ব্লকে কোনো থ্রো করা ত্রুটি ধরতে। + +```typescript + const validSignature = await verifyMessage({ + address: req.params.account, + message: `${loginPrompt}${req.params.nonce}`, + signature: req.params.signature + }) +``` + +সিকোয়েন্স ডায়াগ্রামে ৫.৫ নং ধাপ বাস্তবায়ন করতে [`verifyMessage`](https://viem.sh/docs/actions/public/verifyMessage#verifymessage) ব্যবহার করুন। + +```typescript + if (!validSignature) + throw("Bad signature") + } catch (err) { + res.send("Error:" + err) + return ; + } +``` + +হ্যান্ডলারের বাকি অংশটি পূর্বে `/loginSubmitted` হ্যান্ডলারে আমরা যা করেছি তার সমতুল্য, শুধুমাত্র একটি ছোট পরিবর্তন ছাড়া। + +```typescript + const loginResponse = await idp.createLoginResponse( + . + . + . + { + email: req.params.account + "@bad.email.address" + } + ); +``` + +আমাদের কাছে আসল ইমেল অ্যাড্রেস নেই (আমরা এটি পরবর্তী বিভাগে পাব), তাই আপাতত আমরা ইথেরিয়াম অ্যাড্রেসটি ফেরত দিই এবং এটিকে স্পষ্টভাবে একটি ইমেল অ্যাড্রেস নয় বলে চিহ্নিত করি। + +```typescript +// IdP endpoint for login requests +idpRouter.post(`/login`, + async (req, res) => { + try { + // Workaround because I couldn't get parseLoginRequest to work. + // const loginRequest = await idp.parseLoginRequest(sp, 'post', req) + const samlRequest = xmlParser.parse(Buffer.from(req.body.SAMLRequest, 'base64').toString('utf-8')) + res.send(getSignaturePage(samlRequest["samlp:AuthnRequest"]["@_ID"])) + } catch (err) { + console.error('Error processing SAML response:', err); + res.status(400).send('SAML authentication failed'); + } + } +) +``` + +`getLoginPage`-এর পরিবর্তে, এখন ৩ নং ধাপের হ্যান্ডলারে `getSignaturePage` ব্যবহার করুন। + +## ইমেল অ্যাড্রেস পাওয়া + +পরবর্তী পদক্ষেপ হল ইমেল অ্যাড্রেস পাওয়া, যা পরিষেবা প্রদানকারীর দ্বারা অনুরোধ করা শনাক্তকারী। এটি করার জন্য, আমরা [ইথেরিয়াম অ্যাটেস্টেশন সার্ভিস (EAS)](https://attest.org/) ব্যবহার করি। + +অ্যাটেস্টেশন পাওয়ার সবচেয়ে সহজ উপায় হল [GraphQL API](https://docs.attest.org/docs/developer-tools/api) ব্যবহার করা। আমরা এই কোয়েরিটি ব্যবহার করি: + +``` +query GetAttestationsByRecipient { + attestations( + where: { + recipient: { equals: "${getAddress(ethAddr)}" } + schemaId: { equals: "0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977" } + } + take: 1 + ) { + data + id + attester + } +} +``` + +এই [`schemaId`](https://optimism.easscan.org/schema/view/0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977)-এ শুধু একটি ই-মেইল অ্যাড্রেস অন্তর্ভুক্ত থাকে। এই কোয়েরিটি এই স্কিমার অ্যাটেস্টেশনের জন্য জিজ্ঞাসা করে। অ্যাটেস্টেশনের বিষয়টিকে `recipient` বা প্রাপক বলা হয়। এটি সবসময় একটি ইথেরিয়াম অ্যাড্রেস। + +সতর্কতা: আমরা এখানে যেভাবে অ্যাটেস্টেশন পাচ্ছি তাতে দুটি নিরাপত্তা সমস্যা রয়েছে। + +- আমরা API এন্ডপয়েন্ট, `https://optimism.easscan.org/graphql`-এ যাচ্ছি, যা একটি কেন্দ্রীভূত উপাদান। আমরা `id` অ্যাট্রিবিউট পেতে পারি এবং তারপর একটি অ্যাটেস্টেশন আসল কিনা তা যাচাই করার জন্য একটি অনচেইন লুকআপ করতে পারি, কিন্তু API এন্ডপয়েন্টটি আমাদের তাদের সম্পর্কে না বলে এখনও অ্যাটেস্টেশন সেন্সর করতে পারে। + + এই সমস্যাটি সমাধান করা অসম্ভব নয়, আমরা আমাদের নিজস্ব GraphQL এন্ডপয়েন্ট চালাতে পারি এবং চেইন লগ থেকে অ্যাটেস্টেশন পেতে পারি, কিন্তু তা আমাদের উদ্দেশ্যের জন্য অতিরিক্ত। + +- আমরা অ্যাটেস্টরের পরিচয় দেখি না। যেকেউ আমাদের মিথ্যা তথ্য খাওয়াতে পারে। একটি বাস্তব-বিশ্বের বাস্তবায়নে আমাদের বিশ্বস্ত অ্যাটেস্টরদের একটি সেট থাকত এবং শুধুমাত্র তাদের অ্যাটেস্টেশনগুলি দেখতাম। + +এটি কার্যকর দেখতে, বিদ্যমান IdP এবং SP বন্ধ করুন এবং এই কমান্ডগুলি চালান: + +```sh +git checkout email-address +pnpm install +pnpm start +``` + +তারপর আপনার ই-মেইল অ্যাড্রেস দিন। আপনার কাছে এটি করার দুটি উপায় আছে: + +- একটি প্রাইভেট কী ব্যবহার করে একটি ওয়ালেট আমদানি করুন, এবং টেস্টিং প্রাইভেট কী `0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80` ব্যবহার করুন। + +- আপনার নিজের ই-মেইল অ্যাড্রেসের জন্য একটি অ্যাটেস্টেশন যোগ করুন: + + 1. অ্যাটেস্টেশন এক্সপ্লোরারে [স্কিমাতে](https://optimism.easscan.org/schema/view/0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977) ব্রাউজ করুন। + + 2. **স্কিমা দিয়ে অ্যাটেস্ট করুন**-এ ক্লিক করুন। + + 3. প্রাপক হিসাবে আপনার ইথেরিয়াম অ্যাড্রেস, ইমেল অ্যাড্রেস হিসাবে আপনার ই-মেইল অ্যাড্রেস লিখুন এবং **অনচেইন** নির্বাচন করুন। তারপর **অ্যাটেস্টেশন তৈরি করুন**-এ ক্লিক করুন। + + 4. আপনার ওয়ালেটে লেনদেনটি অনুমোদন করুন। গ্যাসের জন্য অর্থ প্রদান করতে আপনার [অপটিমিজম ব্লকচেইনে](https://app.optimism.io/bridge/deposit) কিছু ETH লাগবে। + +যাই হোক, এটি করার পর [http://localhost:3000](http://localhost:3000) তে ব্রাউজ করুন এবং নির্দেশাবলী অনুসরণ করুন। আপনি যদি টেস্টিং প্রাইভেট কী আমদানি করে থাকেন, তাহলে আপনি যে ই-মেইলটি পাবেন তা হল `test_addr_0@example.com`। আপনি যদি নিজের অ্যাড্রেস ব্যবহার করে থাকেন, তাহলে এটি তাই হবে যা আপনি প্রত্যয়িত করেছেন। + +### বিস্তারিত ব্যাখ্যা + +![ইথেরিয়াম অ্যাড্রেস থেকে ই-মেইলে যাওয়া](./fig-06-saml-sig-n-email.png) + +নতুন ধাপগুলি হল GraphQL কমিউনিকেশন, ধাপ ৫.৬ এবং ৫.৭। + +আবার, এখানে `idp.mts`-এর পরিবর্তিত অংশগুলি দেওয়া হল। + +```typescript +import { GraphQLClient } from 'graphql-request' +import { SchemaEncoder } from '@ethereum-attestation-service/eas-sdk' +``` + +আমাদের প্রয়োজনীয় লাইব্রেরিগুলি আমদানি করুন। + +```typescript +const graphqlEndpointUrl = "https://optimism.easscan.org/graphql" +``` + +[প্রতিটি ব্লকচেইনের জন্য একটি পৃথক এন্ডপয়েন্ট](https://docs.attest.org/docs/developer-tools/api) রয়েছে। + +```typescript +const graphqlClient = new GraphQLClient(graphqlEndpointUrl, { fetch }) +``` + +একটি নতুন `GraphQLClient` ক্লায়েন্ট তৈরি করুন যা আমরা এন্ডপয়েন্ট কোয়েরি করার জন্য ব্যবহার করতে পারি। + +```typescript +const graphqlSchema = 'string emailAddress' +const graphqlEncoder = new SchemaEncoder(graphqlSchema) +``` + +GraphQL আমাদের শুধু বাইট সহ একটি অস্বচ্ছ ডেটা অবজেক্ট দেয়। এটি বুঝতে আমাদের স্কিমা প্রয়োজন। + +```typescript +const ethereumAddressToEmail = async ethAddr => { +``` + +একটি ইথেরিয়াম অ্যাড্রেস থেকে একটি ই-মেইল অ্যাড্রেসে যাওয়ার জন্য একটি ফাংশন। + +```typescript + const query = ` + query GetAttestationsByRecipient { +``` + +এটি একটি GraphQL কোয়েরি। + +```typescript + attestations( +``` + +আমরা অ্যাটেস্টেশন খুঁজছি। + +```typescript + where: { + recipient: { equals: "${getAddress(ethAddr)}" } + schemaId: { equals: "0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977" } + } +``` + +আমরা যে অ্যাটেস্টেশনগুলি চাই তা হল আমাদের স্কিমার মধ্যে থাকা অ্যাটেস্টেশন, যেখানে প্রাপক হল `getAddress(ethAddr)`। [`getAddress`](https://viem.sh/docs/utilities/getAddress#getaddress) ফাংশনটি নিশ্চিত করে যে আমাদের অ্যাড্রেসে সঠিক [চেকসাম](https://github.com/ethereum/ercs/blob/master/ERCS/erc-55.md) আছে। GraphQL কেস-সিগনিফিকেন্ট হওয়ায় এটি প্রয়োজনীয়। `0xBAD060A7`, `0xBad060A7`, এবং `0xbad060a7` ভিন্ন মান। + +```typescript + take: 1 +``` + +আমরা যতগুলিই অ্যাটেস্টেশন পাই না কেন, আমরা শুধুমাত্র প্রথমটি চাই। + +```typescript + ) { + data + id + attester + } + }` +``` + +আমরা যে ফিল্ডগুলি পেতে চাই। + +- `attester`: যে অ্যাড্রেসটি অ্যাটেস্টেশন জমা দিয়েছে। সাধারণত এটি অ্যাটেস্টেশনটি বিশ্বাস করা হবে কি না তা সিদ্ধান্ত নিতে ব্যবহৃত হয়। +- `id`: অ্যাটেস্টেশন আইডি। GraphQL কোয়েরি থেকে প্রাপ্ত তথ্য সঠিক কিনা তা যাচাই করতে আপনি এই মানটি ব্যবহার করে [অনচেইনে অ্যাটেস্টেশনটি পড়তে](https://optimism.blockscout.com/address/0x4200000000000000000000000000000000000021?tab=read_proxy&source_address=0x4E0275Ea5a89e7a3c1B58411379D1a0eDdc5b088#0xa3112a64) পারেন। +- `data`: স্কিমা ডেটা (এই ক্ষেত্রে, ই-মেইল অ্যাড্রেস)। + +```typescript + const queryResult = await graphqlClient.request(query) + + if (queryResult.attestations.length == 0) + return "no_address@available.is" +``` + +যদি কোনো অ্যাটেস্টেশন না থাকে, তাহলে এমন একটি মান ফেরত দিন যা স্পষ্টতই ভুল, কিন্তু যা পরিষেবা প্রদানকারীর কাছে বৈধ বলে মনে হবে। + +```typescript + const attestationDataFields = graphqlEncoder.decodeData(queryResult.attestations[0].data) + return attestationDataFields[0].value.value +} +``` + +যদি কোনো মান থাকে, তাহলে ডেটা ডিকোড করতে `decodeData` ব্যবহার করুন। আমাদের এর প্রদত্ত মেটাডেটার প্রয়োজন নেই, শুধু মানটিই প্রয়োজন। + +```typescript + const loginResponse = await idp.createLoginResponse( + sp, + { + . + . + . + }, + "post", + { + email: await ethereumAddressToEmail(req.params.account) + } + ); +``` + +ই-মেইল অ্যাড্রেস পেতে নতুন ফাংশনটি ব্যবহার করুন। + +## বিকেন্দ্রীকরণ সম্পর্কে কী? + +এই কনফিগারেশনে ব্যবহারকারীরা এমন কেউ হওয়ার ভান করতে পারে না যা তারা নয়, যতক্ষণ না আমরা ইথেরিয়াম থেকে ই-মেইল অ্যাড্রেস ম্যাপিংয়ের জন্য বিশ্বস্ত অ্যাটেস্টরদের উপর নির্ভর করি। তবে, আমাদের পরিচয় প্রদানকারী এখনও একটি কেন্দ্রীভূত উপাদান। পরিচয় প্রদানকারীর প্রাইভেট কী যার কাছে আছে, সে পরিষেবা প্রদানকারীর কাছে মিথ্যা তথ্য পাঠাতে পারে। + +[মাল্টি-পার্টি কম্পিউটেশন (MPC)](https://en.wikipedia.org/wiki/Secure_multi-party_computation) ব্যবহার করে একটি সমাধান থাকতে পারে। আমি আশা করি ভবিষ্যতে একটি টিউটোরিয়ালে এটি নিয়ে লিখব। + +## উপসংহার + +ইথেরিয়াম স্বাক্ষরের মতো একটি লগ অন স্ট্যান্ডার্ড গ্রহণ করা একটি মুরগি ও ডিমের সমস্যার সম্মুখীন হয়। পরিষেবা প্রদানকারীরা সম্ভাব্য বিস্তৃত বাজারে আবেদন করতে চায়। ব্যবহারকারীরা তাদের লগ অন স্ট্যান্ডার্ড সমর্থন করার বিষয়ে চিন্তা না করে পরিষেবাগুলি অ্যাক্সেস করতে সক্ষম হতে চায়। +ইথেরিয়াম IdP-এর মতো অ্যাডাপ্টার তৈরি করা আমাদের এই বাধা অতিক্রম করতে সাহায্য করতে পারে। + +[আমার আরও কাজের জন্য এখানে দেখুন](https://cryptodocguy.pro/)। diff --git a/public/content/translations/bn/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md b/public/content/translations/bn/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md new file mode 100644 index 00000000000..51fb7e28b30 --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md @@ -0,0 +1,156 @@ +--- +title: "Ethereum ডেভেলপমেন্ট শুরু করা" +description: "এটি Ethereum ডেভেলপমেন্ট শুরু করার জন্য নতুনদের একটি গাইড। আমরা আপনাকে একটি API এন্ডপয়েন্ট স্পিন আপ করা থেকে শুরু করে, একটি কমান্ড লাইন রিকোয়েস্ট করা, এবং আপনার প্রথম web3 স্ক্রিপ্ট লেখা পর্যন্ত নিয়ে যাব! ব্লকচেইন ডেভেলপমেন্টের কোনো অভিজ্ঞতা আবশ্যক নয়!" +author: "Elan Halpern" +tags: + [ + "javascript", + "ethers.js", + "নোড", + "querying", + "alchemy" + ] +skill: beginner +lang: bn +published: 2020-10-30 +source: Medium +sourceUrl: https://medium.com/alchemy-api/getting-started-with-ethereum-development-using-alchemy-c3d6a45c567f +--- + +![Ethereum এবং Alchemy-র লোগো](./ethereum-alchemy.png) + +এটি Ethereum ডেভেলপমেন্ট শুরু করার জন্য নতুনদের একটি গাইড। এই টিউটোরিয়ালের জন্য আমরা [Alchemy](https://alchemyapi.io/) ব্যবহার করব, যা শীর্ষস্থানীয় ব্লকচেইন ডেভেলপার প্ল্যাটফর্ম এবং যা Maker, 0x, MyEtherWallet, Dharma এবং Kyber সহ শীর্ষস্থানীয় ব্লকচেইন অ্যাপগুলোর 70% থেকে লক্ষ লক্ষ ব্যবহারকারীকে শক্তি যোগাচ্ছে। Alchemy আমাদেরকে Ethereum চেইনের একটি API এন্ডপয়েন্টে অ্যাক্সেস দেবে যাতে আমরা ট্রানজ্যাকশন পড়তে এবং লিখতে পারি। + +আমরা আপনাকে Alchemy-তে সাইন আপ করা থেকে শুরু করে আপনার প্রথম web3 স্ক্রিপ্ট লেখা পর্যন্ত নিয়ে যাব! ব্লকচেইন ডেভেলপমেন্টের কোনো অভিজ্ঞতা আবশ্যক নয়! + +## ১. একটি বিনামূল্যে Alchemy অ্যাকাউন্টের জন্য সাইন আপ করুন {#sign-up-for-a-free-alchemy-account} + +Alchemy-তে একটি অ্যাকাউন্ট তৈরি করা সহজ, [এখানে বিনামূল্যে সাইন আপ করুন](https://auth.alchemy.com/)। + +## 2. একটি Alchemy অ্যাপ তৈরি করুন {#create-an-alchemy-app} + +Ethereum চেইনের সাথে যোগাযোগ করতে এবং Alchemy-র প্রোডাক্টগুলো ব্যবহার করতে, আপনার রিকোয়েস্টগুলো প্রমাণীকরণ করার জন্য একটি API কী প্রয়োজন। + +আপনি [ড্যাশবোর্ড থেকে API কী তৈরি করতে পারেন](https://dashboard.alchemy.com/)। একটি নতুন কী তৈরি করতে, নিচে দেখানো হিসাবে “Create App”-এ নেভিগেট করুন: + +[_ShapeShift_](https://shapeshift.com/)-কে বিশেষ ধন্যবাদ _আমাদেরকে তাদের ড্যাশবোর্ড দেখানোর সুযোগ দেওয়ার জন্য!_ + +![Alchemy ড্যাশবোর্ড](./alchemy-dashboard.png) + +আপনার নতুন কী পেতে “Create App”-এর অধীনে বিবরণ পূরণ করুন। আপনি এখানে আপনার পূর্বে তৈরি করা অ্যাপ এবং আপনার দলের তৈরি করা অ্যাপগুলোও দেখতে পারেন। যেকোনো অ্যাপের জন্য “View Key”-তে ক্লিক করে বিদ্যমান কীগুলো পান। + +![Alchemy দিয়ে অ্যাপ তৈরির স্ক্রিনশট](./create-app.png) + +আপনি “Apps”-এর উপর হোভার করে এবং একটি নির্বাচন করে বিদ্যমান API কীগুলোও পেতে পারেন। আপনি এখানে “View Key” করতে পারেন, পাশাপাশি নির্দিষ্ট ডোমেন হোয়াইটলিস্ট করতে, বিভিন্ন ডেভেলপার টুলস দেখতে এবং অ্যানালিটিক্স দেখতে “Edit App” করতে পারেন। + +![একজন ব্যবহারকারী কীভাবে API কীগুলো পেতে পারে তা দেখানো একটি জিআইএফ](./pull-api-keys.gif) + +## 3. কমান্ড লাইন থেকে একটি রিকোয়েস্ট করুন {#make-a-request-from-the-command-line} + +JSON-RPC এবং curl ব্যবহার করে Alchemy-র মাধ্যমে Ethereum ব্লকচেইনের সাথে ইন্টারঅ্যাক্ট করুন। + +ম্যানুয়াল রিকোয়েস্টগুলোর জন্য, আমরা `POST` রিকোয়েস্টের মাধ্যমে `JSON-RPC`-এর সাথে ইন্টারঅ্যাক্ট করার সুপারিশ করি। কেবলমাত্র `Content-Type: application/json` হেডার এবং আপনার কোয়েরিটি নিম্নলিখিত ফিল্ডগুলো সহ `POST` বডি হিসাবে পাস করুন: + +- `jsonrpc`: JSON-RPC সংস্করণ—বর্তমানে, শুধুমাত্র `2.0` সমর্থিত। +- `method`: ETH API মেথড। [API রেফারেন্স দেখুন।](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc) +- `params`: মেথডে পাস করার জন্য প্যারামিটারগুলোর একটি তালিকা। +- `id`: আপনার রিকোয়েস্টের আইডি। রেসপন্সের মাধ্যমে এটি ফেরত দেওয়া হবে যাতে আপনি ট্র্যাক রাখতে পারেন কোন রিকোয়েস্টের জন্য কোন রেসপন্সটি এসেছে। + +এখানে একটি উদাহরণ রয়েছে যা আপনি বর্তমান গ্যাসের মূল্য পুনরুদ্ধার করতে কমান্ড লাইন থেকে চালাতে পারেন: + +```bash +curl https://eth-mainnet.alchemyapi.io/v2/demo \ +-X POST \ +-H "Content-Type: application/json" \ +-d '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":73}' +``` + +_**দ্রষ্টব্য:** [https://eth-mainnet.alchemyapi.io/v2/demo](https://eth-mainnet.alchemyapi.io/jsonrpc/demo)-কে আপনার নিজের API কী `https://eth-mainnet.alchemyapi.io/v2/**your-api-key` দিয়ে প্রতিস্থাপন করুন।_ + +**ফলাফল:** + +```json +{ "id": 73,"jsonrpc": "2.0","result": "0x09184e72a000" // 10000000000000 } +``` + +## ৪. আপনার Web3 ক্লায়েন্ট সেট আপ করুন {#set-up-your-web3-client} + +**যদি আপনার একটি বিদ্যমান ক্লায়েন্ট থাকে,** আপনার বর্তমান নোড প্রোভাইডার URL-কে আপনার API কীসহ একটি Alchemy URL-এ পরিবর্তন করুন: `“https://eth-mainnet.alchemyapi.io/v2/your-api-key"` + +**_দ্রষ্টব্য:_** নিচের স্ক্রিপ্টগুলো একটি **নোড কনটেক্সটে** চালাতে হবে অথবা **একটি ফাইলে সেভ করতে হবে**, কমান্ড লাইন থেকে চালানো যাবে না। যদি আপনার আগে থেকেই Node বা npm ইনস্টল করা না থাকে, তাহলে ম্যাকের জন্য এই দ্রুত [সেট-আপ গাইডটি](https://app.gitbook.com/@alchemyapi/s/alchemy/guides/alchemy-for-macs) দেখুন। + +অনেক [Web3 লাইব্রেরি](https://docs.alchemyapi.io/guides/getting-started#other-web3-libraries) আছে যা আপনি Alchemy-র সাথে ইন্টিগ্রেট করতে পারেন, তবে আমরা [Alchemy Web3](https://docs.alchemy.com/reference/api-overview) ব্যবহার করার সুপারিশ করি, যা web3.js-এর একটি ড্রপ-ইন প্রতিস্থাপন এবং যা Alchemy-র সাথে নির্বিঘ্নে কাজ করার জন্য তৈরি এবং কনফিগার করা হয়েছে। এটি স্বয়ংক্রিয় পুনঃপ্রচেষ্টা এবং শক্তিশালী WebSocket সাপোর্টের মতো একাধিক সুবিধা প্রদান করে। + +AlchemyWeb3.js ইনস্টল করতে, **আপনার প্রজেক্ট ডিরেক্টরিতে নেভিগেট করুন** এবং চালান: + +**Yarn দিয়ে:** + +``` +yarn add @alch/alchemy-web3 +``` + +**NPM দিয়ে:** + +``` +npm install @alch/alchemy-web3 +``` + +Alchemy-র নোড ইনফ্রাস্ট্রাকচারের সাথে ইন্টারঅ্যাক্ট করতে, NodeJS-এ চালান অথবা এটি একটি JavaScript ফাইলে যোগ করুন: + +```js +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3( + "https://eth-mainnet.alchemyapi.io/v2/your-api-key" +) +``` + +## ৫। আপনার প্রথম Web3 স্ক্রিপ্ট লিখুন! {#write-your-first-web3-script} + +এখন কিছু web3 প্রোগ্রামিংয়ে হাত পাকানোর জন্য আমরা একটি সাধারণ স্ক্রিপ্ট লিখব যা Ethereum মেইননেট থেকে সর্বশেষ ব্লক নম্বরটি প্রিন্ট করবে। + +**১. যদি আপনি এখনও না করে থাকেন, তাহলে আপনার টার্মিনালে একটি নতুন প্রজেক্ট ডিরেক্টরি তৈরি করুন এবং তার মধ্যে প্রবেশ করুন:** + +``` +mkdir web3-example +cd web3-example +``` + +**২. যদি আপনি আগে থেকে না করে থাকেন, তাহলে আপনার প্রজেক্টে Alchemy web3 (বা যেকোনো web3) ডিপেনডেন্সি ইনস্টল করুন:** + +``` +npm install @alch/alchemy-web3 +``` + +**৩. `index.js` নামে একটি ফাইল তৈরি করুন এবং নিম্নলিখিত কন্টেন্টগুলো যোগ করুন:** + +> আপনার শেষ পর্যন্ত `demo`-কে আপনার Alchemy HTTP API কী দিয়ে প্রতিস্থাপন করা উচিত। + +```js +async function main() { + const { createAlchemyWeb3 } = require("@alch/alchemy-web3") + const web3 = createAlchemyWeb3("https://eth-mainnet.alchemyapi.io/v2/demo") + const blockNumber = await web3.eth.getBlockNumber() + console.log("সর্বশেষ ব্লক নম্বরটি হল " + blockNumber) +} +main() +``` + +অ্যাসিঙ্ক বিষয়গুলোর সাথে অপরিচিত? এই [মিডিয়াম পোস্টটি](https://medium.com/better-programming/understanding-async-await-in-javascript-1d81bb079b2c) দেখুন। + +**4.** নোড ব্যবহার করে এটি আপনার টার্মিনালে চালান\*\* + +``` +node index.js +``` + +**5.** আপনি এখন আপনার কনসোলে সর্বশেষ ব্লক নম্বরের আউটপুট দেখতে পাবেন!\*\* + +``` +সর্বশেষ ব্লক নম্বর হল 11043912 +``` + +**বাহ! অভিনন্দন! আপনি এইমাত্র Alchemy ব্যবহার করে আপনার প্রথম web3 স্ক্রিপ্ট লিখেছেন 🎉** + +এরপরে কী করবেন তা নিয়ে নিশ্চিত নন? আমাদের [হ্যালো ওয়ার্ল্ড স্মার্ট কন্ট্র্যাক্ট গাইডে](https://www.alchemy.com/docs/hello-world-smart-contract) আপনার প্রথম স্মার্ট কন্ট্র্যাক্ট ডিপ্লয় করার চেষ্টা করুন এবং কিছু সলিডিটি প্রোগ্রামিংয়ের সাথে হাত পাকান, অথবা [ড্যাশবোর্ড ডেমো অ্যাপ](https://docs.alchemyapi.io/tutorials/demo-app) দিয়ে আপনার ড্যাশবোর্ডের জ্ঞান পরীক্ষা করুন! + +_[বিনামূল্যে Alchemy-তে সাইন আপ করুন](https://auth.alchemy.com/), আমাদের [নথিপত্র](https://www.alchemy.com/docs/) দেখুন এবং সর্বশেষ খবরের জন্য, আমাদের [টুইটারে](https://twitter.com/AlchemyPlatform) অনুসরণ করুন_। diff --git a/public/content/translations/bn/developers/tutorials/guide-to-smart-contract-security-tools/index.md b/public/content/translations/bn/developers/tutorials/guide-to-smart-contract-security-tools/index.md new file mode 100644 index 00000000000..9cb0d88f3c6 --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/guide-to-smart-contract-security-tools/index.md @@ -0,0 +1,102 @@ +--- +title: "স্মার্ট কন্ট্র্যাক্ট নিরাপত্তা টুলের জন্য একটি গাইড" +description: "তিনটি ভিন্ন পরীক্ষা এবং প্রোগ্রাম বিশ্লেষণ কৌশলের একটি সংক্ষিপ্ত বিবরণ" +author: "Trailofbits" +lang: bn +tags: [ "সলিডিটি", "স্মার্ট কন্ট্র্যাক্ট", "নিরাপত্তা" ] +skill: intermediate +published: 2020-09-07 +source: Building secure contracts +sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis +--- + +আমরা তিনটি স্বতন্ত্র পরীক্ষা এবং প্রোগ্রাম বিশ্লেষণ কৌশল ব্যবহার করতে যাচ্ছি: + +- **[Slither](/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/) সহ স্ট্যাটিক বিশ্লেষণ।** প্রোগ্রামের সমস্ত পাথ একই সময়ে অনুমান করা হয় এবং বিশ্লেষণ করা হয়, বিভিন্ন প্রোগ্রাম উপস্থাপনার মাধ্যমে (যেমন, কন্ট্রোল-ফ্লো-গ্রাফ) +- **[Echidna](/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/) দিয়ে ফাজিং।** একটি সিউডো-র‍্যান্ডম লেনদেন জেনারেশনের মাধ্যমে কোডটি এক্সিকিউট করা হয়। ফাজার একটি প্রদত্ত প্রপার্টি লঙ্ঘন করার জন্য লেনদেনের একটি ক্রম খুঁজে বের করার চেষ্টা করবে। +- **[Manticore](/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/) দিয়ে সিম্বলিক এক্সিকিউশন।** একটি ফর্মাল ভেরিফিকেশন কৌশল, যা প্রতিটি এক্সিকিউশন পাথকে একটি গাণিতিক সূত্রে অনুবাদ করে, যার উপর সীমাবদ্ধতা পরীক্ষা করা যেতে পারে। + +প্রতিটি কৌশলের সুবিধা এবং অসুবিধা রয়েছে, এবং [নির্দিষ্ট ক্ষেত্রে](#determining-security-properties) কার্যকর হবে: + +| কৌশল | টুল | ব্যবহার | গতি | মিস করা বাগ | ভুল অ্যালার্ম | +| ------------------- | --------- | -------------------------------- | ------- | ------------ | ------------- | +| স্ট্যাটিক বিশ্লেষণ | Slither | CLI এবং স্ক্রিপ্ট | সেকেন্ড | মাঝারি | নিম্ন | +| ফাজিং | Echidna | Solidity প্রপার্টি | মিনিট | নিম্ন | কোনোটিই নয় | +| সিম্বলিক এক্সিকিউশন | Manticore | Solidity প্রপার্টি এবং স্ক্রিপ্ট | ঘন্টা | কোনোটিই নয়\* | কোনোটিই নয় | + +\* যদি সমস্ত পথ টাইমআউট ছাড়াই অন্বেষণ করা হয় + +**Slither** কয়েক সেকেন্ডের মধ্যে কন্ট্র্যাক্ট বিশ্লেষণ করে, তবে, স্ট্যাটিক বিশ্লেষণের ফলে ভুল অ্যালার্ম হতে পারে এবং জটিল পরীক্ষার জন্য (যেমন, গাণিতিক পরীক্ষা) এটি কম উপযুক্ত হবে। বিল্ট-ইন ডিটেক্টরে পুশ-বাটন অ্যাক্সেসের জন্য API-এর মাধ্যমে বা ব্যবহারকারী-সংজ্ঞায়িত পরীক্ষার জন্য API-এর মাধ্যমে Slither চালান। + +**Echidna** কে কয়েক মিনিটের জন্য চালাতে হবে এবং এটি কেবল ট্রু পজিটিভ ফলাফল তৈরি করবে। Echidna ব্যবহারকারী-প্রদত্ত নিরাপত্তা প্রপার্টি পরীক্ষা করে, যা Solidity-তে লেখা। এটি র‍্যান্ডম অন্বেষণের উপর ভিত্তি করে হওয়ায় কিছু বাগ মিস করতে পারে। + +**Manticore** "সবচেয়ে ভারী" বিশ্লেষণ সম্পাদন করে। Echidna-র মতো, Manticore ব্যবহারকারী-প্রদত্ত প্রপার্টি যাচাই করে। এটি চালাতে আরও বেশি সময় লাগবে, তবে এটি একটি প্রপার্টির বৈধতা প্রমাণ করতে পারে এবং ভুল অ্যালার্ম রিপোর্ট করবে না। + +## প্রস্তাবিত ওয়ার্কফ্লো {#suggested-workflow} + +এখন কোনো সাধারণ বাগ উপস্থিত নেই বা পরে যোগ করা হবে না তা নিশ্চিত করতে Slither-এর বিল্ট-ইন ডিটেক্টর দিয়ে শুরু করুন। ইনহেরিটেন্স, ভ্যারিয়েবল নির্ভরতা এবং কাঠামোগত সমস্যা সম্পর্কিত প্রপার্টি পরীক্ষা করতে Slither ব্যবহার করুন। কোডবেস বড় হওয়ার সাথে সাথে স্টেট মেশিনের আরও জটিল প্রপার্টি পরীক্ষা করতে Echidna ব্যবহার করুন। Solidity থেকে পাওয়া যায় না এমন সুরক্ষার জন্য কাস্টম চেক তৈরি করতে Slither-কে আবার ব্যবহার করুন, যেমন একটি ফাংশনকে ওভাররাইড করার বিরুদ্ধে সুরক্ষা। সবশেষে, জটিল নিরাপত্তা প্রপার্টি, যেমন, গাণিতিক ক্রিয়াকলাপের লক্ষ্যযুক্ত যাচাইকরণের জন্য Manticore ব্যবহার করুন। + +- সাধারণ সমস্যাগুলি ধরতে Slither-এর CLI ব্যবহার করুন +- আপনার কন্ট্র্যাক্টের উচ্চ-স্তরের নিরাপত্তা প্রপার্টি পরীক্ষা করতে Echidna ব্যবহার করুন +- কাস্টম স্ট্যাটিক চেক লিখতে Slither ব্যবহার করুন +- আপনি যখন জটিল নিরাপত্তা প্রপার্টির গভীরতর নিশ্চয়তা চান তখন Manticore ব্যবহার করুন + +**ইউনিট পরীক্ষার উপর একটি নোট**। উচ্চ-মানের সফ্টওয়্যার তৈরি করতে ইউনিট পরীক্ষা প্রয়োজন। তবে, এই কৌশলগুলি নিরাপত্তা ত্রুটি খুঁজে বের করার জন্য সবচেয়ে উপযুক্ত নয়। এগুলি সাধারণত কোডের পজিটিভ আচরণ পরীক্ষা করার জন্য ব্যবহৃত হয় (অর্থাৎ, কোডটি স্বাভাবিক প্রসঙ্গে প্রত্যাশিতভাবে কাজ করে), যেখানে নিরাপত্তা ত্রুটিগুলি এজ কেসে থাকে যা ডেভেলপাররা বিবেচনা করেননি। কয়েক ডজন স্মার্ট কন্ট্র্যাক্ট নিরাপত্তা পর্যালোচনার উপর আমাদের গবেষণায়, আমরা আমাদের ক্লায়েন্টের কোডে যে পরিমাণ বা তীব্রতার নিরাপত্তা ত্রুটি পেয়েছি, তার উপর [ইউনিট পরীক্ষা কভারেজের কোনো প্রভাব ছিল না](https://blog.trailofbits.com/2019/08/08/246-findings-from-our-smart-contract-audits-an-executive-summary/)। + +## নিরাপত্তা প্রপার্টি নির্ধারণ {#determining-security-properties} + +আপনার কোড কার্যকরভাবে পরীক্ষা এবং যাচাই করার জন্য, আপনাকে মনোযোগের প্রয়োজন এমন ক্ষেত্রগুলি চিহ্নিত করতে হবে। যেহেতু নিরাপত্তার জন্য আপনার রিসোর্স সীমিত, তাই আপনার প্রচেষ্টাকে অপ্টিমাইজ করার জন্য আপনার কোডবেসের দুর্বল বা উচ্চ-মূল্যের অংশগুলির পরিধি নির্ধারণ করা গুরুত্বপূর্ণ। থ্রেট মডেলিং সাহায্য করতে পারে। পর্যালোচনা করার কথা বিবেচনা করুন: + +- [র‍্যাপিড রিস্ক অ্যাসেসমেন্ট](https://infosec.mozilla.org/guidelines/risk/rapid_risk_assessment.html) (সময় কম থাকলে আমাদের পছন্দের পদ্ধতি) +- [ডেটা-সেন্ট্রিক সিস্টেম থ্রেট মডেলিংয়ের জন্য নির্দেশিকা](https://csrc.nist.gov/pubs/sp/800/154/ipd) (ওরফে NIST 800-154) +- [Shostack থ্রেট মডেলিং](https://www.amazon.com/Threat-Modeling-Designing-Adam-Shostack/dp/1118809998) +- [STRIDE](https://wikipedia.org/wiki/STRIDE_\(security\)) / [DREAD](https://wikipedia.org/wiki/DREAD_\(risk_assessment_model\)) +- [PASTA](https://wikipedia.org/wiki/Threat_model#P.A.S.T.A.) +- [অ্যাসার্শনের ব্যবহার](https://blog.regehr.org/archives/1091) + +### উপাদান {#components} + +আপনি কী পরীক্ষা করতে চান তা জানলে সঠিক টুল বেছে নিতেও সাহায্য করবে। + +স্মার্ট কন্ট্র্যাক্টের জন্য প্রায়শই প্রাসঙ্গিক বিস্তৃত ক্ষেত্রগুলির মধ্যে রয়েছে: + +- **স্টেট মেশিন।** বেশিরভাগ কন্ট্র্যাক্টকে একটি স্টেট মেশিন হিসাবে উপস্থাপন করা যেতে পারে। বিবেচনা করুন যে (1) কোনো অবৈধ স্টেটে পৌঁছানো যাবে না, (2) যদি একটি স্টেট বৈধ হয় তবে সেখানে পৌঁছানো যাবে, এবং (3) কোনো স্টেট কন্ট্র্যাক্টকে ফাঁদে ফেলবে না। + + - স্টেট-মেশিন স্পেসিফিকেশন পরীক্ষা করার জন্য Echidna এবং Manticore হল পছন্দের টুল। + +- **অ্যাক্সেস কন্ট্রোল।** যদি আপনার সিস্টেমে বিশেষ সুবিধাপ্রাপ্ত ব্যবহারকারী থাকে (যেমন, একজন মালিক, কন্ট্রোলার, ...) আপনাকে নিশ্চিত করতে হবে যে (1) প্রত্যেক ব্যবহারকারী শুধুমাত্র অনুমোদিত কাজ সম্পাদন করতে পারে এবং (2) কোনো ব্যবহারকারী আরও সুবিধাপ্রাপ্ত ব্যবহারকারীর কাজ ব্লক করতে পারে না। + + - Slither, Echidna এবং Manticore সঠিক অ্যাক্সেস কন্ট্রোল পরীক্ষা করতে পারে। উদাহরণস্বরূপ, Slither পরীক্ষা করতে পারে যে শুধুমাত্র হোয়াইটলিস্ট করা ফাংশনগুলিতে onlyOwner মডিফায়ার নেই। Echidna এবং Manticore আরও জটিল অ্যাক্সেস কন্ট্রোলের জন্য উপযোগী, যেমন একটি কন্ট্র্যাক্ট একটি নির্দিষ্ট স্টেটে পৌঁছালে তবেই অনুমতি দেওয়া হয়। + +- **গাণিতিক অপারেশন।** গাণিতিক অপারেশনের সুস্থতা পরীক্ষা করা গুরুত্বপূর্ণ। ওভারফ্লো/আন্ডারফ্লো প্রতিরোধ করতে সর্বত্র `SafeMath` ব্যবহার করা একটি ভাল পদক্ষেপ, তবে, আপনাকে এখনও অন্যান্য গাণিতিক ত্রুটিগুলি বিবেচনা করতে হবে, যার মধ্যে রাউন্ডিং সমস্যা এবং কন্ট্র্যাক্টকে ফাঁদে ফেলার মতো ত্রুটি রয়েছে। + + - Manticore এখানে সেরা পছন্দ। যদি গাণিতিক হিসাব SMT সলভারের সুযোগের বাইরে থাকে তবে Echidna ব্যবহার করা যেতে পারে। + +- **ইনহেরিটেন্সের সঠিকতা।** Solidity কন্ট্র্যাক্টগুলি মাল্টিপল ইনহেরিটেন্সের উপর ব্যাপকভাবে নির্ভর করে। `super` কল মিস করা শ্যাডোইং ফাংশন এবং ভুল ব্যাখ্যা করা c3 লিনিয়ারাইজেশন অর্ডারের মতো ভুলগুলি সহজেই চালু করা যেতে পারে। + + - Slither এই সমস্যাগুলি সনাক্তকরণ নিশ্চিত করার টুল। + +- **বাহ্যিক ইন্টারঅ্যাকশন।** কন্ট্র্যাক্টগুলি একে অপরের সাথে ইন্টারঅ্যাক্ট করে, এবং কিছু বাহ্যিক কন্ট্র্যাক্টকে বিশ্বাস করা উচিত নয়। উদাহরণস্বরূপ, যদি আপনার কন্ট্র্যাক্ট বাহ্যিক ওরাকলের উপর নির্ভর করে, তাহলে উপলব্ধ ওরাকলের অর্ধেক আপোস করা হলে এটি কি সুরক্ষিত থাকবে? + + - আপনার কন্ট্র্যাক্টের সাথে বাহ্যিক ইন্টারঅ্যাকশন পরীক্ষা করার জন্য Manticore এবং Echidna সেরা পছন্দ। Manticore-এর বাহ্যিক কন্ট্র্যাক্ট স্টাব করার জন্য একটি বিল্ট-ইন মেকানিজম রয়েছে। + +- **স্ট্যান্ডার্ড কনফরমেন্স।** Ethereum স্ট্যান্ডার্ডগুলির (যেমন, ERC20) ডিজাইনে ত্রুটির ইতিহাস রয়েছে। আপনি যে স্ট্যান্ডার্ডের উপর ভিত্তি করে তৈরি করছেন তার সীমাবদ্ধতা সম্পর্কে সচেতন থাকুন। + - Slither, Echidna, এবং Manticore আপনাকে একটি প্রদত্ত স্ট্যান্ডার্ড থেকে বিচ্যুতি সনাক্ত করতে সাহায্য করবে। + +### টুল নির্বাচনের চিটশিট {#tool-selection-cheatsheet} + +| উপাদান | টুলস | উদাহরণ | +| ----------------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| স্টেট মেশিন | Echidna, Manticore | | +| অ্যাক্সেস কন্ট্রোল | Slither, Echidna, Manticore | [Slither এক্সারসাইজ 2](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise2.md), [Echidna এক্সারসাইজ 2](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-2.md) | +| গাণিতিক অপারেশন | Manticore, Echidna | [Echidna এক্সারসাইজ 1](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-1.md), [Manticore এক্সারসাইজ 1 - 3](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/manticore/exercises) | +| ইনহেরিটেন্সের সঠিকতা | Slither | [Slither এক্সারসাইজ 1](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise1.md) | +| বাহ্যিক ইন্টারঅ্যাকশন | Manticore, Echidna | | +| স্ট্যান্ডার্ড কনফরমেন্স | Slither, Echidna, Manticore | [`slither-erc`](https://github.com/crytic/slither/wiki/ERC-Conformance) | + +আপনার লক্ষ্যের উপর নির্ভর করে অন্যান্য ক্ষেত্রগুলি পরীক্ষা করার প্রয়োজন হবে, তবে এই স্থূল-দানাযুক্ত ক্ষেত্রগুলি যেকোনো স্মার্ট কন্ট্র্যাক্ট সিস্টেমের জন্য একটি ভাল শুরু। + +আমাদের পাবলিক অডিটগুলিতে যাচাইকৃত বা পরীক্ষিত প্রপার্টির উদাহরণ রয়েছে। বাস্তব-বিশ্বের নিরাপত্তা প্রপার্টি পর্যালোচনা করতে নিম্নলিখিত রিপোর্টগুলির `স্বয়ংক্রিয় পরীক্ষা এবং যাচাইকরণ` বিভাগগুলি পড়ার কথা বিবেচনা করুন: + +- [0x](https://github.com/trailofbits/publications/blob/master/reviews/0x-protocol.pdf) +- [Balancer](https://github.com/trailofbits/publications/blob/master/reviews/BalancerCore.pdf) diff --git a/public/content/translations/bn/developers/tutorials/hello-world-smart-contract-fullstack/index.md b/public/content/translations/bn/developers/tutorials/hello-world-smart-contract-fullstack/index.md new file mode 100644 index 00000000000..8b1939fa53f --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/hello-world-smart-contract-fullstack/index.md @@ -0,0 +1,1542 @@ +--- +title: "নতুনদের জন্য Hello World স্মার্ট কন্ট্র্যাক্ট - ফুলস্ট্যাক" +description: "Ethereum-এ একটি সহজ স্মার্ট কন্ট্র্যাক্ট লেখা এবং স্থাপন করার উপর একটি পরিচিতি টিউটোরিয়াল।" +author: "nstrike2" +tags: + [ + "সলিডিটি", + "hardhat", + "alchemy", + "স্মার্ট কন্ট্র্যাক্ট", + "ডেপ্লয়িং", + "ব্লক এক্সপ্লোরার", + "ফ্রন্টএন্ড", + "লেনদেনসমূহ" + ] +skill: beginner +lang: bn +published: 2021-10-25 +--- + +আপনি যদি ব্লকচেইন ডেভেলপমেন্টে নতুন হন এবং কোথা থেকে শুরু করবেন বা কীভাবে স্মার্ট কন্ট্র্যাক্ট স্থাপন এবং তার সঙ্গে যোগাযোগ করবেন তা না জানেন, তবে এই গাইডটি আপনার জন্য। আমরা [MetaMask](https://metamask.io), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org), এবং [Alchemy](https://alchemy.com/eth) ব্যবহার করে Goerli টেস্ট নেটওয়ার্কে একটি সহজ, স্মার্ট কন্ট্র্যাক্ট তৈরি এবং স্থাপন করার পদ্ধতি ধাপে ধাপে দেখাব। + +এই টিউটোরিয়ালটি সম্পূর্ণ করতে আপনার একটি Alchemy অ্যাকাউন্ট লাগবে। [একটি বিনামূল্যে অ্যাকাউন্টের জন্য সাইন আপ করুন](https://www.alchemy.com/)। + +যেকোনো পর্যায়ে আপনার কোনো প্রশ্ন থাকলে, নির্দ্বিধায় [Alchemy Discord](https://discord.gg/gWuC7zB)-এ যোগাযোগ করুন! + +## পর্ব ১ - Hardhat ব্যবহার করে আপনার স্মার্ট কন্ট্র্যাক্ট তৈরি ও স্থাপন করুন {#part-1} + +### ইথেরিয়াম নেটওয়ার্কের সাথে সংযোগ করুন {#connect-to-the-ethereum-network} + +Ethereum চেইনে রিকুয়েস্ট করার অনেক উপায় আছে। সহজ করার জন্য, আমরা Alchemy-তে একটি বিনামূল্যের অ্যাকাউন্ট ব্যবহার করব, যা একটি ব্লকচেইন ডেভেলপার প্ল্যাটফর্ম এবং API, যা আমাদের নিজেদের নোড না চালিয়ে ইথেরিয়াম চেইনের সাথে যোগাযোগ করতে দেয়। Alchemy-তে নিরীক্ষণ এবং বিশ্লেষণের জন্য ডেভেলপার টুলও রয়েছে; আমাদের স্মার্ট কন্ট্র্যাক্ট স্থাপনার নেপথ্যে কী ঘটছে তা বোঝার জন্য আমরা এই টিউটোরিয়ালে এগুলির সুবিধা নেব। + +### আপনার অ্যাপ এবং API কী তৈরি করুন {#create-your-app-and-api-key} + +একবার আপনি একটি Alchemy অ্যাকাউন্ট তৈরি করলে, আপনি একটি অ্যাপ তৈরি করে একটি API কী তৈরি করতে পারেন। এটি আপনাকে Goerli টেস্টনেটে অনুরোধ করার অনুমতি দেবে। আপনি যদি টেস্টনেটগুলির সাথে পরিচিত না হন তবে আপনি [একটি নেটওয়ার্ক বেছে নেওয়ার জন্য Alchemy-র গাইডটি পড়তে পারেন](https://www.alchemy.com/docs/choosing-a-web3-network)। + +Alchemy ড্যাশবোর্ডে, নেভিগেশন বারে **অ্যাপস** ড্রপডাউনটি খুঁজুন এবং **অ্যাপ তৈরি করুন**-এ ক্লিক করুন। + +![হ্যালো ওয়ার্ল্ড অ্যাপ তৈরি করুন](./hello-world-create-app.png) + +আপনার অ্যাপটিকে '_Hello World_' নাম দিন এবং একটি সংক্ষিপ্ত বিবরণ লিখুন। আপনার এনভায়রনমেন্ট হিসেবে **Staging** এবং আপনার নেটওয়ার্ক হিসেবে **Goerli** নির্বাচন করুন। + +![অ্যাপ ভিউ হ্যালো ওয়ার্ল্ড তৈরি করুন](./create-app-view-hello-world.png) + +_দ্রষ্টব্য: অবশ্যই **Goerli** নির্বাচন করুন, নতুবা এই টিউটোরিয়ালটি কাজ করবে না।_ + +**অ্যাপ তৈরি করুন**-এ ক্লিক করুন। আপনার অ্যাপটি নীচের টেবিলে প্রদর্শিত হবে। + +### একটি ইথেরিয়াম অ্যাকাউন্ট তৈরি করুন {#create-an-ethereum-account} + +লেনদেন পাঠানো এবং গ্রহণ করার জন্য আপনার একটি ইথেরিয়াম অ্যাকাউন্ট প্রয়োজন। আমরা MetaMask ব্যবহার করব, যা ব্রাউজারের একটি ভার্চুয়াল ওয়ালেট এবং এটি ব্যবহারকারীদের তাদের ইথেরিয়াম অ্যাকাউন্টের ঠিকানা পরিচালনা করতে দেয়। + +আপনি [এখানে](https://metamask.io/download) বিনামূল্যে একটি MetaMask অ্যাকাউন্ট ডাউনলোড এবং তৈরি করতে পারেন। যখন আপনি একটি অ্যাকাউন্ট তৈরি করছেন, বা যদি আপনার আগে থেকেই একটি অ্যাকাউন্ট থাকে, তবে উপরের ডানদিকে থাকা "Goerli Test Network"-এ স্যুইচ করতে ভুলবেন না (যাতে আমরা আসল টাকা নিয়ে কাজ না করি)। + +### ধাপ ৪: একটি ফসেট থেকে ইথার যোগ করুন {#step-4-add-ether-from-a-faucet} + +টেস্ট নেটওয়ার্কে আপনার স্মার্ট কন্ট্র্যাক্ট স্থাপন করতে, আপনার কিছু নকল ETH লাগবে। Goerli নেটওয়ার্কে ETH পেতে, একটি Goerli ফসেটে যান এবং আপনার Goerli অ্যাকাউন্টের ঠিকানা লিখুন। উল্লেখ্য যে Goerli ফসেটগুলি সম্প্রতি কিছুটা অবিশ্বাস্য হতে পারে - চেষ্টা করার জন্য বিকল্পগুলির একটি তালিকার জন্য [টেস্ট নেটওয়ার্ক পৃষ্ঠাটি](/developers/docs/networks/#goerli) দেখুন: + +_দ্রষ্টব্য: নেটওয়ার্ক কনজেশনের কারণে, এতে কিছুটা সময় লাগতে পারে।_ +`` + +### ধাপ ৫: আপনার ব্যালেন্স পরীক্ষা করুন {#step-5-check-your-balance} + +আপনার ওয়ালেটে ETH আছে কিনা তা পুনরায় পরীক্ষা করতে, আসুন আমরা [Alchemy-র কম্পোজার টুল](https://composer.alchemyapi.io/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D) ব্যবহার করে একটি [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance) অনুরোধ করি। এটি আমাদের ওয়ালেটে থাকা ETH-এর পরিমাণ ফেরত দেবে। আরও জানতে [কম্পোজার টুলটি কীভাবে ব্যবহার করবেন সে সম্পর্কে Alchemy-র সংক্ষিপ্ত টিউটোরিয়ালটি](https://youtu.be/r6sjRxBZJuU) দেখুন। + +আপনার MetaMask অ্যাকাউন্টের ঠিকানা ইনপুট করুন এবং **অনুরোধ পাঠান**-এ ক্লিক করুন। আপনি নীচের কোড স্নিপেটের মতো একটি প্রতিক্রিয়া দেখতে পাবেন। + +```json +{ "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" } +``` + +> _দ্রষ্টব্য: এই ফলাফলটি wei-তে, ETH-এ নয়।_ Wei ইথারের ক্ষুদ্রতম একক হিসাবে ব্যবহৃত হয়।_ + +যাক বাবা! আমাদের নকল টাকা সব আছে। + +### ধাপ ৬: আমাদের প্রজেক্ট শুরু করুন {#step-6-initialize-our-project} + +প্রথমে, আমাদের প্রজেক্টের জন্য একটি ফোল্ডার তৈরি করতে হবে। আপনার কমান্ড লাইনে নেভিগেট করুন এবং নিম্নলিখিতটি ইনপুট করুন। + +``` +mkdir hello-world +cd hello-world +``` + +এখন যেহেতু আমরা আমাদের প্রজেক্ট ফোল্ডারের ভিতরে আছি, আমরা প্রজেক্টটি শুরু করতে `npm init` ব্যবহার করব। + +> যদি আপনার এখনও npm ইনস্টল করা না থাকে, তাহলে [Node.js এবং npm ইনস্টল করার জন্য এই নির্দেশাবলী](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm) অনুসরণ করুন। + +এই টিউটোরিয়ালের উদ্দেশ্যে, আপনি ইনিশিয়ালাইজেশন প্রশ্নগুলির কীভাবে উত্তর দেন তা বিবেচ্য নয়। এখানে আমরা রেফারেন্সের জন্য কীভাবে এটি করেছি তা দেওয়া হলো: + +``` +প্যাকেজের নাম: (hello-world) +সংস্করণ: (1.0.0) +বর্ণনা: hello world স্মার্ট কন্ট্র্যাক্ট +এন্ট্রি পয়েন্ট: (index.js) +টেস্ট কমান্ড: +git রিপোজিটরি: +কীওয়ার্ড: +লেখক: +লাইসেন্স: (ISC) + +/Users/.../.../.../hello-world/package.json এ লিখতে চলেছে: + +{ + "name": "hello-world", + "version": "1.0.0", + "description": "hello world স্মার্ট কন্ট্র্যাক্ট", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} +``` + +package.json অনুমোদন করুন এবং আমরা এগিয়ে যাওয়ার জন্য প্রস্তুত! + +### ধাপ ৭: Hardhat ডাউনলোড করুন {#step-7-download-hardhat} + +Hardhat হল আপনার Ethereum সফ্টওয়্যার কম্পাইল, স্থাপন, পরীক্ষা এবং ডিবাগ করার জন্য একটি ডেভেলপমেন্ট পরিবেশ। এটি ডেভেলপারদের লাইভ চেইনে স্থাপন করার আগে স্থানীয়ভাবে স্মার্ট কন্ট্র্যাক্ট এবং ডিএ্যাপস তৈরি করতে সাহায্য করে। + +আমাদের `hello-world` প্রজেক্টের ভিতরে চালান: + +``` +npm install --save-dev hardhat +``` + +[ইনস্টলেশন নির্দেশাবলী](https://hardhat.org/getting-started/#overview) সম্পর্কে আরও বিস্তারিত জানতে এই পৃষ্ঠাটি দেখুন। + +### ধাপ ৮: Hardhat প্রজেক্ট তৈরি করুন {#step-8-create-hardhat-project} + +আমাদের `hello-world` প্রজেক্ট ফোল্ডারের ভিতরে, চালান: + +``` +npx hardhat +``` + +তারপরে আপনার একটি স্বাগত বার্তা এবং আপনি কী করতে চান তা নির্বাচন করার একটি বিকল্প দেখতে পাওয়া উচিত। “একটি খালি hardhat.config.js তৈরি করুন” নির্বাচন করুন: + +``` +888 888 888 888 888 +888 888 888 888 888 +888 888 888 888 888 +8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888 +888 888 "88b 888P" d88" 888 888 "88b "88b 888 +888 888 .d888888 888 888 888 888 888 .d888888 888 +888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. +888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 + +👷 Hardhat v2.0.11-এ আপনাকে স্বাগত 👷‍ + +আপনি কী করতে চান? … +একটি নমুনা প্রজেক্ট তৈরি করুন +❯ একটি খালি hardhat.config.js তৈরি করুন +প্রস্থান করুন +``` + +এটি প্রজেক্টে একটি `hardhat.config.js` ফাইল তৈরি করবে। আমরা আমাদের প্রজেক্টের সেটআপ নির্দিষ্ট করার জন্য টিউটোরিয়ালের পরে এটি ব্যবহার করব। + +### ধাপ ৯: প্রজেক্ট ফোল্ডার যোগ করুন {#step-9-add-project-folders} + +প্রজেক্টটি সংগঠিত রাখতে, আসুন দুটি নতুন ফোল্ডার তৈরি করি। কমান্ড লাইনে, আপনার `hello-world` প্রজেক্টের রুট ডিরেক্টরিতে নেভিগেট করুন এবং টাইপ করুন: + +``` +mkdir contracts +mkdir scripts +``` + +- `contracts/` হল যেখানে আমরা আমাদের হ্যালো ওয়ার্ল্ড স্মার্ট কন্ট্র্যাক্ট কোড ফাইল রাখব +- `scripts/` হল যেখানে আমরা আমাদের কন্ট্র্যাক্ট স্থাপন এবং ইন্টারঅ্যাক্ট করার জন্য স্ক্রিপ্ট রাখব + +### ধাপ ১০: আমাদের কন্ট্র্যাক্ট লিখুন {#step-10-write-our-contract} + +আপনি নিজেকে জিজ্ঞাসা করতে পারেন, আমরা কখন কোড লিখতে যাচ্ছি? এখন সেই সময়! + +আপনার প্রিয় এডিটরে hello-world প্রজেক্টটি খুলুন। স্মার্ট কন্ট্র্যাক্টগুলি সাধারণত সলিডিটিতে লেখা হয়, যা আমরা আমাদের স্মার্ট কন্ট্র্যাক্ট লেখার জন্য ব্যবহার করব।‌ + +1. `contracts` ফোল্ডারে নেভিগেট করুন এবং `HelloWorld.sol` নামে একটি নতুন ফাইল তৈরি করুন +2. নীচে একটি নমুনা Hello World স্মার্ট কন্ট্র্যাক্ট রয়েছে যা আমরা এই টিউটোরিয়ালের জন্য ব্যবহার করব। নীচের বিষয়বস্তুগুলি `HelloWorld.sol` ফাইলে কপি করুন। + +_দ্রষ্টব্য: এই কন্ট্র্যাক্টটি কী করে তা বোঝার জন্য মন্তব্যগুলি পড়তে ভুলবেন না।_ + +``` +// সিমেন্টিক ভার্সনিং ব্যবহার করে সলিডিটির সংস্করণ নির্দিষ্ট করে। +// আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +pragma solidity >=0.7.3; + +// `HelloWorld` নামের একটি কন্ট্র্যাক্ট সংজ্ঞায়িত করে। +// একটি কন্ট্র্যাক্ট হল ফাংশন এবং ডেটার একটি সংগ্রহ (তার স্টেট)। একবার স্থাপন করা হলে, একটি কন্ট্র্যাক্ট ইথেরিয়াম ব্লকচেইনে একটি নির্দিষ্ট ঠিকানায় থাকে। আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +contract HelloWorld { + + //আপডেট ফাংশন কল করা হলে নির্গত হয় + //স্মার্ট কন্ট্র্যাক্ট ইভেন্টগুলি হল আপনার কন্ট্র্যাক্টের জন্য আপনার অ্যাপ ফ্রন্ট-এন্ডে ব্লকচেইনে কিছু ঘটেছে তা জানানোর একটি উপায়, যা নির্দিষ্ট ইভেন্টের জন্য 'শুনতে' পারে এবং সেগুলি ঘটলে ব্যবস্থা নিতে পারে। + event UpdatedMessages(string oldStr, string newStr); + + // `string` টাইপের একটি স্টেট ভেরিয়েবল `message` ঘোষণা করে। + // স্টেট ভেরিয়েবলগুলি হল এমন ভেরিয়েবল যার মানগুলি স্থায়ীভাবে কন্ট্র্যাক্ট স্টোরেজে সংরক্ষণ করা হয়। `public` কীওয়ার্ডটি কন্ট্র্যাক্টের বাইরে থেকে ভেরিয়েবলগুলিকে অ্যাক্সেসযোগ্য করে তোলে এবং একটি ফাংশন তৈরি করে যা অন্য কন্ট্র্যাক্ট বা ক্লায়েন্টরা মান অ্যাক্সেস করার জন্য কল করতে পারে। + string public message; + + // অনেক ক্লাস-ভিত্তিক অবজেক্ট-ওরিয়েন্টেড ভাষার মতো, একটি কনস্ট্রাক্টর হল একটি বিশেষ ফাংশন যা শুধুমাত্র কন্ট্র্যাক্ট তৈরির সময় কার্যকর করা হয়। + // কনস্ট্রাক্টরগুলি কন্ট্র্যাক্টের ডেটা ইনিশিয়ালাইজ করতে ব্যবহৃত হয়। আরও জানুন:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + constructor(string memory initMessage) { + + // একটি স্ট্রিং আর্গুমেন্ট `initMessage` গ্রহণ করে এবং কন্ট্র্যাক্টের `message` স্টোরেজ ভেরিয়েবলে মান সেট করে)। + message = initMessage; + } + + // একটি পাবলিক ফাংশন যা একটি স্ট্রিং আর্গুমেন্ট গ্রহণ করে এবং `message` স্টোরেজ ভেরিয়েবল আপডেট করে। + function update(string memory newMessage) public { + string memory oldMsg = message; + message = newMessage; + emit UpdatedMessages(oldMsg, newMessage); + } +} +``` + +এটি একটি বেসিক স্মার্ট কন্ট্র্যাক্ট যা তৈরি হওয়ার পরে একটি বার্তা সংরক্ষণ করে। এটি `update` ফাংশন কল করে আপডেট করা যেতে পারে। + +### ধাপ ১১: আপনার প্রজেক্টের সাথে MetaMask এবং Alchemy সংযোগ করুন {#step-11-connect-metamask-alchemy-to-your-project} + +আমরা একটি MetaMask ওয়ালেট, Alchemy অ্যাকাউন্ট তৈরি করেছি এবং আমাদের স্মার্ট কন্ট্র্যাক্ট লিখেছি, এখন এই তিনটি সংযোগ করার সময়। + +আপনার ওয়ালেট থেকে পাঠানো প্রতিটি লেনদেনের জন্য আপনার অনন্য ব্যক্তিগত কী ব্যবহার করে একটি স্বাক্ষরের প্রয়োজন। আমাদের প্রোগ্রামকে এই অনুমতি দেওয়ার জন্য, আমরা আমাদের ব্যক্তিগত কী একটি এনভায়রনমেন্ট ফাইলে নিরাপদে সংরক্ষণ করতে পারি। আমরা এখানে Alchemy-র জন্য একটি API কীও সংরক্ষণ করব। + +> লেনদেন পাঠানোর বিষয়ে আরও জানতে, ওয়েব3 ব্যবহার করে লেনদেন পাঠানোর উপর [এই টিউটোরিয়ালটি](https://www.alchemy.com/docs/hello-world-smart-contract#step-11-connect-metamask--alchemy-to-your-project) দেখুন। + +প্রথমে, আপনার প্রজেক্ট ডিরেক্টরিতে ডটএনভ প্যাকেজটি ইনস্টল করুন: + +``` +npm install dotenv --save +``` + +তারপর, প্রজেক্টের রুট ডিরেক্টরিতে একটি `.env` ফাইল তৈরি করুন। এতে আপনার MetaMask ব্যক্তিগত কী এবং HTTP Alchemy API URL যোগ করুন। + +আপনার এনভায়রনমেন্ট ফাইলের নাম অবশ্যই `.env` হতে হবে, নতুবা এটি এনভায়রনমেন্ট ফাইল হিসেবে স্বীকৃত হবে না। + +এটির নাম `process.env` বা `.env-custom` বা অন্য কিছু দেবেন না। + +- আপনার ব্যক্তিগত কী এক্সপোর্ট করতে [এই নির্দেশাবলী](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key) অনুসরণ করুন +- HTTP Alchemy API URL পেতে নিচে দেখুন + +![](./get-alchemy-api-key.gif) + +আপনার `.env` ফাইলটি এইরকম দেখতে হবে: + +``` +API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key" +PRIVATE_KEY = "your-metamask-private-key" +``` + +এগুলিকে আমাদের কোডের সাথে সংযোগ করতে, আমরা ধাপ 13-এ আমাদের `hardhat.config.js` ফাইলে এই ভেরিয়েবলগুলিকে রেফারেন্স করব। + +### ধাপ 12: Ethers.js ইনস্টল করুন {#step-12-install-ethersjs} + +Ethers.js হল একটি লাইব্রেরি যা আরও ব্যবহারকারী-বান্ধব পদ্ধতির সাথে [স্ট্যান্ডার্ড JSON-RPC পদ্ধতি](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc) মোড়ক দিয়ে ইথেরিয়ামের সাথে ইন্টারঅ্যাক্ট করা এবং অনুরোধ করা সহজ করে তোলে। + +Hardhat আমাদের অতিরিক্ত টুলিং এবং বর্ধিত কার্যকারিতার জন্য [প্লাগইন](https://hardhat.org/plugins/) একীভূত করার অনুমতি দেয়। আমরা কন্ট্র্যাক্ট স্থাপনার জন্য [Ethers প্লাগইন](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers) এর সুবিধা নেব। + +আপনার প্রজেক্ট ডিরেক্টরিতে টাইপ করুন: + +```bash +npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0" +``` + +### ধাপ ১৩: hardhat.config.js আপডেট করুন {#step-13-update-hardhat-configjs} + +আমরা এখন পর্যন্ত বেশ কিছু নির্ভরতা এবং প্লাগইন যোগ করেছি, এখন আমাদের `hardhat.config.js` আপডেট করতে হবে যাতে আমাদের প্রজেক্ট তাদের সকলের সম্পর্কে জানতে পারে। + +আপনার `hardhat.config.js` আপডেট করে এইরকম করুন: + +```javascript +/** + * @type import('hardhat/config').HardhatUserConfig + */ + +require("dotenv").config() +require("@nomiclabs/hardhat-ethers") + +const { API_URL, PRIVATE_KEY } = process.env + +module.exports = { + solidity: "0.7.3", + defaultNetwork: "goerli", + networks: { + hardhat: {}, + goerli: { + url: API_URL, + accounts: [`0x${PRIVATE_KEY}`], + }, + }, +} +``` + +### ধাপ ১৪: আমাদের কন্ট্র্যাক্ট কম্পাইল করুন {#step-14-compile-our-contract} + +সবকিছু ঠিকঠাক কাজ করছে কিনা তা নিশ্চিত করতে, আসুন আমাদের কন্ট্র্যাক্ট কম্পাইল করি। `compile` টাস্কটি অন্তর্নির্মিত হার্ডহ্যাট টাস্কগুলির মধ্যে একটি। + +কমান্ড লাইন থেকে রান করুন: + +```bash +npx hardhat compile +``` + +আপনি `SPDX লাইসেন্স আইডেন্টিফায়ার সোর্স ফাইলে সরবরাহ করা হয়নি` সম্পর্কে একটি সতর্কতা পেতে পারেন, কিন্তু সে সম্পর্কে চিন্তা করার দরকার নেই — আশা করি বাকি সব ঠিকঠাক দেখাবে! যদি না হয়, আপনি সবসময় [Alchemy ডিসকর্ড](https://discord.gg/u72VCg3)-এ বার্তা দিতে পারেন। + +### ধাপ ১৫: আমাদের ডেপ্লয় স্ক্রিপ্ট লিখুন {#step-15-write-our-deploy-script} + +এখন যেহেতু আমাদের কন্ট্র্যাক্ট লেখা হয়ে গেছে এবং আমাদের কনফিগারেশন ফাইল প্রস্তুত, এখন আমাদের কন্ট্র্যাক্ট ডেপ্লয় স্ক্রিপ্ট লেখার সময়। + +`scripts/` ফোল্ডারে যান এবং `deploy.js` নামে একটি নতুন ফাইল তৈরি করুন, এতে নিম্নলিখিত বিষয়বস্তু যোগ করুন: + +```javascript +async function main() { + const HelloWorld = await ethers.getContractFactory("HelloWorld") + + // ডেপ্লয়মেন্ট শুরু করুন, যা একটি কন্ট্র্যাক্ট অবজেক্টের সমাধান করে এমন একটি প্রতিশ্রুতি প্রদান করে + const hello_world = await HelloWorld.deploy("Hello World!") + console.log("Contract deployed to address:", hello_world.address) +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) +``` + +Hardhat তাদের [কন্ট্র্যাক্টস টিউটোরিয়াল](https://hardhat.org/tutorial/testing-contracts.html#writing-tests)-এ এই কোডের প্রতিটি লাইন কী করে তা চমৎকারভাবে ব্যাখ্যা করেছে, আমরা এখানে তাদের ব্যাখ্যাগুলি গ্রহণ করেছি। + +```javascript +const HelloWorld = await ethers.getContractFactory("HelloWorld") +``` + +ethers.js-এ একটি `ContractFactory` হল নতুন স্মার্ট কন্ট্র্যাক্ট ডেপ্লয় করার জন্য ব্যবহৃত একটি বিমূর্তকরণ, তাই এখানে `HelloWorld` হল আমাদের hello world কন্ট্র্যাক্টের উদাহরণগুলির জন্য একটি [ফ্যাক্টরি](https://en.wikipedia.org/wiki/Factory_\(object-oriented_programming\))। `hardhat-ethers` প্লাগইন `ContractFactory` এবং `Contract` ব্যবহার করার সময়, ইনস্ট্যান্সগুলি ডিফল্টরূপে প্রথম স্বাক্ষরকারী (মালিক) এর সাথে সংযুক্ত থাকে। + +```javascript +const hello_world = await HelloWorld.deploy() +``` + +একটি `ContractFactory`-এ `deploy()` কল করলে ডেপ্লয়মেন্ট শুরু হবে, এবং একটি `Promise` প্রদান করবে যা একটি `Contract` অবজেক্টে সমাধান করে। এটি সেই অবজেক্ট যার আমাদের প্রতিটি স্মার্ট কন্ট্র্যাক্ট ফাংশনের জন্য একটি পদ্ধতি রয়েছে। + +### ধাপ 16: আমাদের কন্ট্র্যাক্ট স্থাপন করুন {#step-16-deploy-our-contract} + +আমরা অবশেষে আমাদের স্মার্ট কন্ট্র্যাক্ট স্থাপন করার জন্য প্রস্তুত! কমান্ড লাইনে যান এবং চালান: + +```bash +npx hardhat run scripts/deploy.js --network goerli +``` + +তারপরে আপনার এইরকম কিছু দেখা উচিত: + +```bash +কন্ট্র্যাক্টটি এই ঠিকানায় স্থাপন করা হয়েছে: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 +``` + +**অনুগ্রহ করে এই ঠিকানাটি সংরক্ষণ করুন**। আমরা এটি টিউটোরিয়ালের পরে ব্যবহার করব। + +আমরা যদি [Goerli etherscan](https://goerli.etherscan.io)-এ যাই এবং আমাদের কন্ট্র্যাক্টের ঠিকানা অনুসন্ধান করি, তাহলে আমরা দেখতে পাব যে এটি সফলভাবে স্থাপন করা হয়েছে। লেনদেনটি এইরকম কিছু দেখাবে: + +![](./etherscan-contract.png) + +`From` ঠিকানাটি আপনার MetaMask অ্যাকাউন্টের ঠিকানার সাথে মিলবে এবং `To` ঠিকানায় বলা হবে **কন্ট্র্যাক্ট তৈরি**। যদি আমরা লেনদেনের মধ্যে ক্লিক করি, তাহলে আমরা `To` ফিল্ডে আমাদের কন্ট্র্যাক্টের ঠিকানা দেখতে পাব। + +![](./etherscan-transaction.png) + +অভিনন্দন! আপনি সবেমাত্র একটি ইথেরিয়াম টেস্টনেটে একটি স্মার্ট কন্ট্র্যাক্ট স্থাপন করেছেন। + +নেপথ্যে কী ঘটছে তা বোঝার জন্য, আসুন আমাদের [Alchemy ড্যাশবোর্ড](https://dashboard.alchemy.com/explorer)-এর এক্সপ্লোরার ট্যাবে নেভিগেট করি। আপনার যদি একাধিক Alchemy অ্যাপ থাকে তবে অ্যাপ অনুসারে ফিল্টার করে **Hello World** নির্বাচন করতে ভুলবেন না। + +![](./hello-world-explorer.png) + +এখানে আপনি কিছু JSON-RPC পদ্ধতি দেখতে পাবেন যা Hardhat/Ethers আমাদের জন্য নেপথ্যে তৈরি করেছে যখন আমরা `.deploy()` ফাংশন কল করেছিলাম। এখানকার দুটি গুরুত্বপূর্ণ পদ্ধতি হল [`eth_sendRawTransaction`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_sendrawtransaction), যা Goerli চেইনে আমাদের কন্ট্র্যাক্ট লেখার অনুরোধ, এবং [`eth_getTransactionByHash`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_gettransactionbyhash), যা হ্যাস দেওয়া হলে আমাদের transaction সম্পর্কে তথ্য পড়ার জন্য একটি অনুরোধ। লেনদেন পাঠানোর বিষয়ে আরও জানতে, [Web3 ব্যবহার করে লেনদেন পাঠানোর উপর আমাদের টিউটোরিয়ালটি দেখুন](/developers/tutorials/sending-transactions-using-web3-and-alchemy/)। + +## পর্ব ২: আপনার স্মার্ট কন্ট্র্যাক্টের সাথে ইন্টারঅ্যাক্ট করুন {#part-2-interact-with-your-smart-contract} + +যেহেতু আমরা সফলভাবে Goerli নেটওয়ার্কে একটি স্মার্ট কন্ট্র্যাক্ট স্থাপন করেছি, আসুন এবার শিখি কীভাবে এর সাথে ইন্টারঅ্যাক্ট করতে হয়। + +### একটি interact.js ফাইল তৈরি করুন {#create-a-interactjs-file} + +এটি সেই ফাইল যেখানে আমরা আমাদের ইন্টারঅ্যাকশন স্ক্রিপ্ট লিখব। আমরা Ethers.js লাইব্রেরি ব্যবহার করব যা আপনি পূর্বে পর্ব ১-এ ইনস্টল করেছিলেন। + +`scripts/` ফোল্ডারের ভিতরে, `interact.js` নামে একটি নতুন ফাইল তৈরি করুন এবং নিম্নলিখিত কোডটি যোগ করুন: + +```javascript +// interact.js + +const API_KEY = process.env.API_KEY +const PRIVATE_KEY = process.env.PRIVATE_KEY +const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS +``` + +### আপনার .env ফাইল আপডেট করুন {#update-your-env-file} + +আমরা নতুন এনভায়রনমেন্ট ভেরিয়েবল ব্যবহার করব, তাই আমাদের `.env` ফাইলে সেগুলিকে সংজ্ঞায়িত করতে হবে যা [আমরা আগে তৈরি করেছি](#step-11-connect-metamask-&-alchemy-to-your-project)। + +আমাদের Alchemy `API_KEY` এবং `CONTRACT_ADDRESS`-এর জন্য একটি সংজ্ঞা যোগ করতে হবে যেখানে আপনার স্মার্ট কন্ট্র্যাক্টটি স্থাপন করা হয়েছিল। + +আপনার `.env` ফাইলটি এইরকম কিছু দেখতে হবে: + +```bash +# .env + +API_URL = "https://eth-goerli.alchemyapi.io/v2/" +API_KEY = "" +PRIVATE_KEY = "" +CONTRACT_ADDRESS = "0x" +``` + +### আপনার কন্ট্র্যাক্ট ABI নিন {#grab-your-contract-ABI} + +আমাদের কন্ট্র্যাক্ট [ABI (অ্যাপ্লিকেশন বাইনারি ইন্টারফেস)](/glossary/#abi) হল আমাদের স্মার্ট কন্ট্র্যাক্টের সাথে ইন্টারঅ্যাক্ট করার ইন্টারফেস। Hardhat স্বয়ংক্রিয়ভাবে একটি ABI তৈরি করে এবং এটি `HelloWorld.json`-এ সংরক্ষণ করে। ABI ব্যবহার করতে, আমাদের `interact.js` ফাইলে নিম্নলিখিত কোডের লাইনগুলি যোগ করে বিষয়বস্তুগুলি পার্স করতে হবে: + +```javascript +// interact.js +const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json") +``` + +আপনি যদি ABI দেখতে চান তবে আপনি এটি আপনার কনসোলে প্রিন্ট করতে পারেন: + +```javascript +console.log(JSON.stringify(contract.abi)) +``` + +আপনার ABI কনসোলে প্রিন্ট করা দেখতে, আপনার টার্মিনালে নেভিগেট করুন এবং চালান: + +```bash +npx hardhat run scripts/interact.js +``` + +### আপনার কন্ট্র্যাক্টের একটি উদাহরণ তৈরি করুন {#create-an-instance-of-your-contract} + +আমাদের কন্ট্র্যাক্টের সাথে ইন্টারঅ্যাক্ট করতে, আমাদের কোডে একটি কন্ট্র্যাক্ট ইনস্ট্যান্স তৈরি করতে হবে। Ethers.js দিয়ে এটি করতে, আমাদের তিনটি ধারণা নিয়ে কাজ করতে হবে: + +1. প্রোভাইডার - একটি নোড প্রোভাইডার যা আপনাকে ব্লকচেইনে পঠন ও লেখার অ্যাক্সেস দেয় +2. স্বাক্ষরকারী - একটি ইথেরিয়াম অ্যাকাউন্ট যা লেনদেন স্বাক্ষর করতে পারে +3. কন্ট্র্যাক্ট - একটি Ethers.js অবজেক্ট যা অনচেইনে স্থাপন করা একটি নির্দিষ্ট কন্ট্র্যাক্টকে প্রতিনিধিত্ব করে + +আমরা আগের ধাপ থেকে কন্ট্র্যাক্ট ABI ব্যবহার করে কন্ট্র্যাক্টের উদাহরণ তৈরি করব: + +```javascript +// interact.js + +// Provider +const alchemyProvider = new ethers.providers.AlchemyProvider( + (network = "goerli"), + API_KEY +) + +// Signer +const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider) + +// Contract +const helloWorldContract = new ethers.Contract( + CONTRACT_ADDRESS, + contract.abi, + signer +) +``` + +[ethers.js ডকুমেন্টেশনে](https://docs.ethers.io/v5/) প্রোভাইডার, স্বাক্ষরকারী এবং কন্ট্র্যাক্ট সম্পর্কে আরও জানুন। + +### প্রারম্ভিক বার্তা পড়ুন {#read-the-init-message} + +মনে আছে যখন আমরা `initMessage = "Hello world!"` দিয়ে আমাদের কন্ট্র্যাক্ট স্থাপন করেছিলাম? আমরা এখন আমাদের স্মার্ট কন্ট্র্যাক্টে সংরক্ষিত সেই বার্তাটি পড়ব এবং কনসোলে প্রিন্ট করব। + +জাভাস্ক্রিপ্টে, নেটওয়ার্কের সাথে ইন্টারঅ্যাক্ট করার সময় অ্যাসিঙ্ক্রোনাস ফাংশন ব্যবহার করা হয়। অ্যাসিঙ্ক্রোনাস ফাংশন সম্পর্কে আরও জানতে, [এই মিডিয়াম নিবন্ধটি পড়ুন](https://blog.bitsrc.io/understanding-asynchronous-javascript-the-event-loop-74cd408419ff)। + +আমাদের স্মার্ট কন্ট্র্যাক্টে `message` ফাংশন কল করতে এবং প্রারম্ভিক বার্তা পড়তে নীচের কোডটি ব্যবহার করুন: + +```javascript +// interact.js + +// ... + +asyn'c function main() { + const message = await helloWorldContract.message() + console.log("The message is: " + message) +} +main() +``` + +টার্মিনালে `npx hardhat run scripts/interact.js` ব্যবহার করে ফাইলটি চালানোর পর আমরা এই প্রতিক্রিয়া দেখতে পাব: + +``` +বার্তাটি হল: Hello world! +``` + +অভিনন্দন! আপনি সবেমাত্র ইথেরিয়াম ব্লকচেইন থেকে সফলভাবে স্মার্ট কন্ট্র্যাক্ট ডেটা পড়েছেন, সাবাশ! + +### বার্তা আপডেট করুন {#update-the-message} + +শুধু বার্তা পড়ার পরিবর্তে, আমরা `update` ফাংশন ব্যবহার করে আমাদের স্মার্ট কন্ট্র্যাক্টে সংরক্ষিত বার্তাও আপডেট করতে পারি! বেশ দারুণ, তাই না? + +বার্তা আপডেট করতে, আমরা সরাসরি আমাদের ইনস্ট্যানশিয়েটেড কন্ট্র্যাক্ট অবজেক্টে `update` ফাংশন কল করতে পারি: + +```javascript +// interact.js + +// ... + +async function main() { + const message = await helloWorldContract.message() + console.log("The message is: " + message) + + console.log("Updating the message...") + const tx = await helloWorldContract.update("This is the new message.") + await tx.wait() +} +main() +``` + +লক্ষ্য করুন যে ১১ নং লাইনে, আমরা ফেরত দেওয়া লেনদেন অবজেক্টে `.wait()` কল করি। এটি নিশ্চিত করে যে আমাদের স্ক্রিপ্ট ফাংশন থেকে বের হওয়ার আগে ব্লকচেইনে লেনদেন মাইন হওয়ার জন্য অপেক্ষা করে। যদি `.wait()` কলটি অন্তর্ভুক্ত না করা হয়, তবে স্ক্রিপ্টটি কন্ট্র্যাক্টে আপডেট করা `message` মান দেখতে নাও পারে। + +### নতুন বার্তা পড়ুন {#read-the-new-message} + +আপডেট করা `message` মান পড়ার জন্য আপনি [আগের ধাপটি](#read-the-init-message) পুনরাবৃত্তি করতে সক্ষম হবেন। একটু সময় নিন এবং দেখুন আপনি সেই নতুন মানটি প্রিন্ট করার জন্য প্রয়োজনীয় পরিবর্তনগুলি করতে পারেন কিনা! + +আপনার যদি একটি ইঙ্গিত প্রয়োজন হয়, তাহলে এখানে আপনার `interact.js` ফাইলটি এই মুহূর্তে কেমন দেখতে হবে: + +```javascript +// interact.js + +const API_KEY = process.env.API_KEY +const PRIVATE_KEY = process.env.PRIVATE_KEY +const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS + +const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json") + +// প্রোভাইডার - Alchemy +const alchemyProvider = new ethers.providers.AlchemyProvider( + (network = "goerli"), + API_KEY +) + +// স্বাক্ষরকারী - আপনি +const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider) + +// কন্ট্র্যাক্ট ইনস্ট্যান্স +const helloWorldContract = new ethers.Contract( + CONTRACT_ADDRESS, + contract.abi, + signer +) + +async function main() { + const message = await helloWorldContract.message() + console.log("The message is: " + message) + + console.log("Updating the message...") + const tx = await helloWorldContract.update("this is the new message") + await tx.wait() + + const newMessage = await helloWorldContract.message() + console.log("The new message is: " + newMessage) +} + +main() +``` + +এখন শুধু স্ক্রিপ্টটি চালান এবং আপনি আপনার টার্মিনালে পুরানো বার্তা, আপডেটিং স্থিতি এবং নতুন বার্তা প্রিন্ট করা দেখতে সক্ষম হবেন! + +`npx hardhat run scripts/interact.js --network goerli` + +``` +বার্তাটি হল: Hello World! +বার্তা আপডেট হচ্ছে... +নতুন বার্তাটি হল: এটি নতুন বার্তা। +``` + +সেই স্ক্রিপ্টটি চালানোর সময়, আপনি লক্ষ্য করতে পারেন যে নতুন বার্তা লোড হওয়ার আগে `বার্তা আপডেট হচ্ছে...` ধাপটি লোড হতে কিছুটা সময় নেয়। এটি মাইনিং প্রক্রিয়ার কারণে হয়; যদি আপনি মাইনিং করার সময় লেনদেন ট্র্যাক করতে আগ্রহী হন, তবে একটি লেনদেনের স্থিতি দেখতে [Alchemy mempool](https://dashboard.alchemyapi.io/mempool) পরিদর্শন করুন। যদি লেনদেনটি ড্রপ হয়ে যায়, তবে [Goerli Etherscan](https://goerli.etherscan.io) পরীক্ষা করা এবং আপনার লেনদেন হ্যাস অনুসন্ধান করাও সহায়ক। + +## পর্ব ৩: আপনার স্মার্ট কন্ট্র্যাক্ট Etherscan-এ প্রকাশ করুন {#part-3-publish-your-smart-contract-to-etherscan} + +আপনি আপনার স্মার্ট কন্ট্র্যাক্টকে জীবন্ত করার সমস্ত কঠিন কাজ করেছেন; এখন সময় এসেছে এটি বিশ্বের সাথে ভাগ করে নেওয়ার! + +Etherscan-এ আপনার স্মার্ট কন্ট্র্যাক্ট যাচাই করার মাধ্যমে, যে কেউ আপনার সোর্স কোড দেখতে এবং আপনার স্মার্ট কন্ট্র্যাক্টের সাথে ইন্টারঅ্যাক্ট করতে পারে। চলুন শুরু করা যাক! + +### ধাপ ১: আপনার Etherscan অ্যাকাউন্টে একটি API কী তৈরি করুন {#step-1-generate-an-api-key-on-your-etherscan-account} + +আপনি যে স্মার্ট কন্ট্র্যাক্টটি প্রকাশ করার চেষ্টা করছেন তার মালিকানা যাচাই করার জন্য একটি Etherscan API কী প্রয়োজন। + +আপনার যদি আগে থেকেই একটি Etherscan অ্যাকাউন্ট না থাকে, তাহলে [একটি অ্যাকাউন্টের জন্য সাইন আপ করুন](https://etherscan.io/register)। + +লগ ইন করার পর, নেভিগেশন বারে আপনার ব্যবহারকারীর নাম খুঁজুন, তার উপর হোভার করুন এবং **আমার প্রোফাইল** বোতামটি নির্বাচন করুন। + +আপনার প্রোফাইল পৃষ্ঠায়, আপনি একটি সাইড নেভিগেশন বার দেখতে পাবেন। সাইড নেভিগেশন বার থেকে, **API কী** নির্বাচন করুন। এরপরে, একটি নতুন API কী তৈরি করতে "যোগ করুন" বোতামটি টিপুন, আপনার অ্যাপের নাম দিন **hello-world** এবং **নতুন API কী তৈরি করুন** বোতামটি টিপুন। + +আপনার নতুন API কী API কী টেবিলে প্রদর্শিত হবে। API কীটি আপনার ক্লিপবোর্ডে কপি করুন। + +এরপরে, আমাদের `.env` ফাইলে Etherscan API কী যোগ করতে হবে। + +এটি যোগ করার পর, আপনার `.env` ফাইলটি এইরকম দেখতে হবে: + +```javascript +API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key" +PUBLIC_KEY = "your-public-account-address" +PRIVATE_KEY = "your-private-account-address" +CONTRACT_ADDRESS = "your-contract-address" +ETHERSCAN_API_KEY = "your-etherscan-key" +``` + +### Hardhat-ডেপ্লয়ড স্মার্ট কন্ট্র্যাক্ট {#hardhat-deployed-smart-contracts} + +#### hardhat-etherscan ইনস্টল করুন {#install-hardhat-etherscan} + +Hardhat ব্যবহার করে Etherscan-এ আপনার কন্ট্র্যাক্ট প্রকাশ করা খুবই সহজ। শুরু করার জন্য আপনাকে প্রথমে `hardhat-etherscan` প্লাগইন ইনস্টল করতে হবে। `hardhat-etherscan` স্বয়ংক্রিয়ভাবে স্মার্ট কন্ট্র্যাক্টের সোর্স কোড এবং ABI Etherscan-এ যাচাই করবে। এটি যোগ করতে, `hello-world` ডিরেক্টরিতে চালান: + +```text +npm install --save-dev @nomiclabs/hardhat-etherscan +``` + +ইনস্টল হয়ে গেলে, আপনার `hardhat.config.js`-এর শীর্ষে নিম্নলিখিত স্টেটমেন্টটি অন্তর্ভুক্ত করুন এবং Etherscan কনফিগারেশন বিকল্পগুলি যোগ করুন: + +```javascript +// hardhat.config.js + +require("dotenv").config() +require("@nomiclabs/hardhat-ethers") +require("@nomiclabs/hardhat-etherscan") + +const { API_URL, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env + +module.exports = { + solidity: "0.7.3", + defaultNetwork: "goerli", + networks: { + hardhat: {}, + goerli: { + url: API_URL, + accounts: [`0x${PRIVATE_KEY}`], + }, + }, + etherscan: { + // Etherscan-এর জন্য আপনার API কী + // https://etherscan.io/ থেকে একটি পান + apiKey: ETHERSCAN_API_KEY, + }, +} +``` + +#### Etherscan-এ আপনার স্মার্ট কন্ট্র্যাক্ট যাচাই করুন {#verify-your-smart-contract-on-etherscan} + +নিশ্চিত করুন যে সমস্ত ফাইল সংরক্ষিত হয়েছে এবং সমস্ত `.env` ভেরিয়েবল সঠিকভাবে কনফিগার করা হয়েছে। + +`verify` টাস্কটি চালান, কন্ট্র্যাক্টের ঠিকানা এবং যেখানে এটি স্থাপন করা হয়েছে সেই নেটওয়ার্কটি পাস করে: + +```text +npx hardhat verify --network goerli DEPLOYED_CONTRACT_ADDRESS 'Hello World!' +``` + +নিশ্চিত করুন যে `DEPLOYED_CONTRACT_ADDRESS` হল Goerli টেস্ট নেটওয়ার্কে আপনার স্থাপন করা স্মার্ট কন্ট্র্যাক্টের ঠিকানা। এছাড়াও, চূড়ান্ত আর্গুমেন্ট (`'Hello World!'`) অবশ্যই [পর্ব ১-এর ডেপ্লয় ধাপের](#write-our-deploy-script) সময় ব্যবহৃত একই স্ট্রিং মান হতে হবে। + +যদি সবকিছু ঠিকঠাক থাকে, আপনি আপনার টার্মিনালে নিম্নলিখিত বার্তাটি দেখতে পাবেন: + +```text +কন্ট্র্যাক্টের জন্য সোর্স কোড সফলভাবে জমা দেওয়া হয়েছে +contracts/HelloWorld.sol:HelloWorld at 0xdeployed-contract-address +Etherscan-এ যাচাইকরণের জন্য। যাচাইকরণের ফলাফলের জন্য অপেক্ষা করা হচ্ছে... + + +সফলভাবে Etherscan-এ HelloWorld কন্ট্র্যাক্ট যাচাই করা হয়েছে। +https://goerli.etherscan.io/address/#contracts +``` + +অভিনন্দন! আপনার স্মার্ট কন্ট্র্যাক্ট কোড Etherscan-এ আছে! + +### Etherscan-এ আপনার স্মার্ট কন্ট্র্যাক্ট দেখুন! {#check-out-your-smart-contract-on-etherscan} + +যখন আপনি আপনার টার্মিনালে দেওয়া লিঙ্কে নেভিগেট করবেন, তখন আপনি আপনার স্মার্ট কন্ট্র্যাক্ট কোড এবং ABI Etherscan-এ প্রকাশিত দেখতে পাবেন! + +**ইয়াহু - তুমি করে ফেলেছ চ্যাম্পিয়ন! এখন যে কেউ আপনার স্মার্ট কন্ট্র্যাক্টে কল বা লিখতে পারে! আমরা দেখার জন্য অপেক্ষা করছি আপনি এরপরে কী তৈরি করেন!** + +## পর্ব ৪ - আপনার স্মার্ট কন্ট্র্যাক্টকে ফ্রন্টএন্ডের সাথে একীভূত করা {#part-4-integrating-your-smart-contract-with-the-frontend} + +এই টিউটোরিয়ালের শেষে, আপনি জানতে পারবেন কীভাবে: + +- আপনার ডিএ্যাপস-এর সাথে একটি MetaMask ওয়ালেট সংযোগ করা +- [Alchemy Web3](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) API ব্যবহার করে আপনার স্মার্ট কন্ট্র্যাক্ট থেকে ডেটা পড়া +- MetaMask ব্যবহার করে ইথেরিয়াম লেনদেন স্বাক্ষর করা + +এই ডিএ্যাপ-এর জন্য, আমরা আমাদের ফ্রন্টএন্ড ফ্রেমওয়ার্ক হিসেবে [React](https://react.dev/) ব্যবহার করব; তবে, এটি মনে রাখা গুরুত্বপূর্ণ যে আমরা এর মূল বিষয়গুলি ভেঙে দেখতে বেশি সময় ব্যয় করব না, কারণ আমরা মূলত আমাদের প্রজেক্টে Web3 কার্যকারিতা আনার উপর মনোযোগ দেব। + +পূর্বশর্ত হিসেবে, আপনার React সম্পর্কে প্রাথমিক স্তরের ধারণা থাকা উচিত। যদি না থাকে, আমরা অফিসিয়াল [Intro to React টিউটোরিয়াল](https://react.dev/learn) সম্পূর্ণ করার সুপারিশ করি। + +### স্টার্টার ফাইলগুলো ক্লোন করুন {#clone-the-starter-files} + +প্রথমে, [hello-world-part-four GitHub রিপোজিটরি](https://github.com/alchemyplatform/hello-world-part-four-tutorial)-তে যান এই প্রজেক্টের জন্য স্টার্টার ফাইলগুলি পেতে এবং এই রিপোজিটরিটি আপনার স্থানীয় মেশিনে ক্লোন করুন। + +ক্লোন করা রিপোজিটরিটি স্থানীয়ভাবে খুলুন। লক্ষ্য করুন যে এতে দুটি ফোল্ডার রয়েছে: `starter-files` এবং `completed`। + +- `starter-files`- **আমরা এই ডিরেক্টরিতে কাজ করব**, আমরা UI-কে আপনার ইথেরিয়াম ওয়ালেট এবং [পর্ব ৩](#part-3)-এ Etherscan-এ প্রকাশিত স্মার্ট কন্ট্র্যাক্টের সাথে সংযোগ করব। +- `completed` সম্পূর্ণ টিউটোরিয়াল ধারণ করে এবং শুধুমাত্র যদি আপনি আটকে যান তবে একটি রেফারেন্স হিসাবে ব্যবহার করা উচিত। + +এরপর, আপনার `starter-files`-এর কপিটি আপনার প্রিয় কোড এডিটরে খুলুন, এবং তারপর `src` ফোল্ডারে নেভিগেট করুন। + +আমরা যে সমস্ত কোড লিখব তা `src` ফোল্ডারের অধীনে থাকবে। আমরা আমাদের প্রজেক্টে Web3 কার্যকারিতা দেওয়ার জন্য `HelloWorld.js` কম্পোনেন্ট এবং `util/interact.js` জাভাস্ক্রিপ্ট ফাইলগুলি সম্পাদনা করব। + +### স্টার্টার ফাইলগুলি দেখুন {#check-out-the-starter-files} + +আমরা কোডিং শুরু করার আগে, আসুন দেখে নেওয়া যাক স্টার্টার ফাইলগুলিতে আমাদের কী সরবরাহ করা হয়েছে। + +#### আপনার React প্রজেক্টটি চালু করুন {#get-your-react-project-running} + +চলুন আমাদের ব্রাউজারে React প্রজেক্টটি চালিয়ে শুরু করি। React-এর সৌন্দর্য হলো যে একবার আমাদের প্রজেক্ট ব্রাউজারে চালু হয়ে গেলে, আমরা যে কোনো পরিবর্তন সেভ করব তা আমাদের ব্রাউজারে লাইভ আপডেট হবে। + +প্রজেক্টটি চালানোর জন্য, `starter-files` ফোল্ডারের রুট ডিরেক্টরিতে নেভিগেট করুন, এবং প্রজেক্টের নির্ভরতা ইনস্টল করার জন্য আপনার টার্মিনালে `npm install` চালান: + +```bash +cd starter-files +npm install +``` + +সেগুলো ইনস্টল করা শেষ হলে, আপনার টার্মিনালে `npm start` রান করুন: + +```bash +npm start +``` + +এটি করলে আপনার ব্রাউজারে [http://localhost:3000/](http://localhost:3000/) খোলা উচিত, যেখানে আপনি আমাদের প্রজেক্টের ফ্রন্টএন্ড দেখতে পাবেন। এতে একটি ফিল্ড (আপনার স্মার্ট কন্ট্র্যাক্টে সংরক্ষিত বার্তা আপডেট করার জন্য একটি স্থান), একটি "ওয়ালেট সংযোগ করুন" বোতাম, এবং একটি "আপডেট" বোতাম থাকা উচিত। + +আপনি যদি কোনো বোতামে ক্লিক করার চেষ্টা করেন, আপনি লক্ষ্য করবেন যে সেগুলি কাজ করে না—এর কারণ হল আমাদের এখনও তাদের কার্যকারিতা প্রোগ্রাম করতে হবে। + +#### `HelloWorld.js` কম্পোনেন্ট {#the-helloworld-js-component} + +চলুন আমাদের এডিটরে `src` ফোল্ডারে ফিরে যাই এবং `HelloWorld.js` ফাইলটি খুলি। এই ফাইলের সবকিছু বোঝা অত্যন্ত গুরুত্বপূর্ণ, কারণ এটিই মূল React কম্পোনেন্ট যা নিয়ে আমরা কাজ করব। + +এই ফাইলের শীর্ষে, আপনি লক্ষ্য করবেন যে আমাদের প্রজেক্ট চালানোর জন্য প্রয়োজনীয় বেশ কয়েকটি আমদানি বিবৃতি রয়েছে, যার মধ্যে রয়েছে React লাইব্রেরি, useEffect এবং useState হুক, `./util/interact.js` থেকে কিছু আইটেম (আমরা শীঘ্রই সেগুলিকে আরও বিশদে বর্ণনা করব!), এবং Alchemy লোগো। + +```javascript +// HelloWorld.js + +import React from "react" +import { useEffect, useState } from "react" +import { + helloWorldContract, + connectWallet, + updateMessage, + loadCurrentMessage, + getCurrentWalletConnected, +} from "./util/interact.js" + +import alchemylogo from "./alchemylogo.svg" +``` + +এরপর, আমাদের স্টেট ভেরিয়েবলগুলি রয়েছে যা আমরা নির্দিষ্ট ইভেন্টের পরে আপডেট করব। + +```javascript +// HelloWorld.js + +//স্টেট ভেরিয়েবল +const [walletAddress, setWallet] = useState("") +const [status, setStatus] = useState("") +const [message, setMessage] = useState("নেটওয়ার্কের সাথে কোনো সংযোগ নেই।") +const [newMessage, setNewMessage] = useState("") +``` + +এখানে প্রতিটি ভেরিয়েবল কী প্রতিনিধিত্ব করে: + +- `walletAddress` - একটি স্ট্রিং যা ব্যবহারকারীর ওয়ালেট অ্যাড্রেস সংরক্ষণ করে +- `status`- একটি স্ট্রিং যা ব্যবহারকারীকে ডিএ্যাপস-এর সাথে কীভাবে ইন্টারঅ্যাক্ট করতে হবে সে সম্পর্কে একটি সহায়ক বার্তা সংরক্ষণ করে +- `message` - একটি স্ট্রিং যা স্মার্ট কন্ট্র্যাক্টে বর্তমান বার্তা সংরক্ষণ করে +- `newMessage` - একটি স্ট্রিং যা নতুন বার্তা সংরক্ষণ করে যা স্মার্ট কন্ট্র্যাক্টে লেখা হবে + +স্টেট ভেরিয়েবলের পর, আপনি পাঁচটি বাস্তবায়ন-না-করা ফাংশন দেখতে পাবেন: `useEffect` ,`addSmartContractListener`, `addWalletListener` , `connectWalletPressed`, এবং `onUpdatePressed`। আমরা নীচে ব্যাখ্যা করব তারা কী করে: + +```javascript +// HelloWorld.js + +//একবারই বলা হয় +useEffect(async () => { + //TODO: বাস্তবায়ন করুন +}, []) + +function addSmartContractListener() { + //TODO: বাস্তবায়ন করুন +} + +function addWalletListener() { + //TODO: বাস্তবায়ন করুন +} + +const connectWalletPressed = async () => { + //TODO: বাস্তবায়ন করুন +} + +const onUpdatePressed = async () => { + //TODO: বাস্তবায়ন করুন +} +``` + +- [`useEffect`](https://legacy.reactjs.org/docs/hooks-effect.html)- এটি একটি React হুক যা আপনার কম্পোনেন্ট রেন্ডার হওয়ার পরে কল করা হয়। কারণ এতে একটি খালি অ্যারে `[]` প্রপ পাস করা হয়েছে (লাইন ৪ দেখুন), এটি শুধুমাত্র কম্পোনেন্টের _প্রথম_ রেন্ডারে কল করা হবে। এখানে আমরা আমাদের স্মার্ট কন্ট্র্যাক্টে সংরক্ষিত বর্তমান বার্তা লোড করব, আমাদের স্মার্ট কন্ট্র্যাক্ট এবং ওয়ালেট লিসেনারদের কল করব, এবং আমাদের UI আপডেট করব যাতে একটি ওয়ালেট ইতিমধ্যে সংযুক্ত কিনা তা প্রতিফলিত হয়। +- `addSmartContractListener`- এই ফাংশনটি একটি লিসেনার সেট আপ করে যা আমাদের HelloWorld কন্ট্র্যাক্টের `UpdatedMessages` ইভেন্টের জন্য নজর রাখবে এবং আমাদের স্মার্ট কন্ট্র্যাক্টে বার্তা পরিবর্তন হলে আমাদের UI আপডেট করবে। +- `addWalletListener`- এই ফাংশনটি একটি লিসেনার সেট আপ করে যা ব্যবহারকারীর MetaMask ওয়ালেটের অবস্থার পরিবর্তন সনাক্ত করে, যেমন ব্যবহারকারী যখন তাদের ওয়ালেট সংযোগ বিচ্ছিন্ন করে বা ঠিকানা পরিবর্তন করে। +- `connectWalletPressed`- এই ফাংশনটি ব্যবহারকারীর MetaMask ওয়ালেটকে আমাদের ডিএ্যাপস-এর সাথে সংযোগ করতে কল করা হবে। +- `onUpdatePressed` - এই ফাংশনটি কল করা হবে যখন ব্যবহারকারী স্মার্ট কন্ট্র্যাক্টে সংরক্ষিত বার্তা আপডেট করতে চায়। + +এই ফাইলের শেষের দিকে, আমাদের কম্পোনেন্টের UI রয়েছে। + +```javascript +// HelloWorld.js + +//আমাদের কম্পোনেন্টের UI +return ( +
+ + + +

বর্তমান বার্তা:

+

{message}

+ +

নতুন বার্তা:

+ +
+ setNewMessage(e.target.value)} + value={newMessage} + /> +

{status}

+ + +
+ +
+) +``` + +আপনি যদি এই কোডটি সাবধানে স্ক্যান করেন, আপনি লক্ষ্য করবেন যে আমরা আমাদের UI-তে আমাদের বিভিন্ন স্টেট ভেরিয়েবল কোথায় ব্যবহার করি: + +- ৬-১২ লাইনে, যদি ব্যবহারকারীর ওয়ালেট সংযুক্ত থাকে (অর্থাৎ `walletAddress.length > 0`), আমরা "walletButton" আইডি সহ বোতামে ব্যবহারকারীর `walletAddress`-এর একটি সংক্ষিপ্ত সংস্করণ প্রদর্শন করি; অন্যথায় এটি কেবল "ওয়ালেট সংযোগ করুন" বলে। +- ১৭ লাইনে, আমরা স্মার্ট কন্ট্র্যাক্টে সংরক্ষিত বর্তমান বার্তা প্রদর্শন করি, যা `message` স্ট্রিং-এ ধরা হয়। +- ২৩-২৬ লাইনে, আমরা টেক্সট ফিল্ডে ইনপুট পরিবর্তন হলে আমাদের `newMessage` স্টেট ভেরিয়েবল আপডেট করার জন্য একটি [নিয়ন্ত্রিত কম্পোনেন্ট](https://legacy.reactjs.org/docs/forms.html#controlled-components) ব্যবহার করি। + +আমাদের স্টেট ভেরিয়েবল ছাড়াও, আপনি আরও দেখতে পাবেন যে `publishButton` এবং `walletButton` আইডি সহ বোতামগুলি ক্লিক করা হলে যথাক্রমে `connectWalletPressed` এবং `onUpdatePressed` ফাংশনগুলি কল করা হয়। + +অবশেষে, আসুন দেখা যাক এই `HelloWorld.js` কম্পোনেন্টটি কোথায় যুক্ত করা হয়েছে। + +আপনি যদি `App.js` ফাইলে যান, যা React-এর প্রধান কম্পোনেন্ট এবং অন্যান্য সমস্ত কম্পোনেন্টের জন্য একটি ধারক হিসেবে কাজ করে, আপনি দেখতে পাবেন যে আমাদের `HelloWorld.js` কম্পোনেন্টটি ৭ নং লাইনে ইনজেক্ট করা হয়েছে। + +সবশেষে, আসুন আপনার জন্য সরবরাহ করা আরও একটি ফাইল, `interact.js` ফাইলটি দেখে নেওয়া যাক। + +#### `interact.js` ফাইল {#the-interact-js-file} + +যেহেতু আমরা [M-V-C](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) প্যারাডাইম অনুসরণ করতে চাই, আমরা একটি পৃথক ফাইল চাইব যা আমাদের ডিএ্যাপস-এর যুক্তি, ডেটা এবং নিয়মগুলি পরিচালনা করার জন্য আমাদের সমস্ত ফাংশন ধারণ করে এবং তারপরে সেই ফাংশনগুলিকে আমাদের ফ্রন্টএন্ডে (আমাদের `HelloWorld.js` কম্পোনেন্ট) এক্সপোর্ট করতে সক্ষম হব। + +👆🏽এইটিই আমাদের `interact.js` ফাইলের সঠিক উদ্দেশ্য! + +আপনার `src` ডিরেক্টরিতে `util` ফোল্ডারে নেভিগেট করুন, এবং আপনি লক্ষ্য করবেন যে আমরা `interact.js` নামে একটি ফাইল অন্তর্ভুক্ত করেছি যা আমাদের সমস্ত স্মার্ট কন্ট্র্যাক্ট ইন্টারঅ্যাকশন এবং ওয়ালেট ফাংশন ও ভেরিয়েবল ধারণ করবে। + +```javascript +// interact.js + +//export const helloWorldContract; + +export const loadCurrentMessage = async () => {} + +export const connectWallet = async () => {} + +const getCurrentWalletConnected = async () => {} + +export const updateMessage = async (message) => {} +``` + +আপনি ফাইলের শীর্ষে লক্ষ্য করবেন যে আমরা `helloWorldContract` অবজেক্টটি কমেন্ট আউট করেছি। এই টিউটোরিয়ালের পরে, আমরা এই অবজেক্টটি আনকমেন্ট করব এবং এই ভেরিয়েবলে আমাদের স্মার্ট কন্ট্র্যাক্টটি ইনস্ট্যানশিয়েট করব, যা আমরা তারপরে আমাদের `HelloWorld.js` কম্পোনেন্টে এক্সপোর্ট করব। + +আমাদের `helloWorldContract` অবজেক্টের পরে চারটি অবাস্তবায়িত ফাংশন নিম্নলিখিত কাজগুলি করে: + +- `loadCurrentMessage` - এই ফাংশনটি স্মার্ট কন্ট্র্যাক্টে সংরক্ষিত বর্তমান বার্তা লোড করার যুক্তি পরিচালনা করে। এটি [Alchemy Web3 API](https://github.com/alchemyplatform/alchemy-web3) ব্যবহার করে Hello World স্মার্ট কন্ট্র্যাক্টে একটি _পড়ার_ কল করবে। +- `connectWallet` - এই ফাংশনটি ব্যবহারকারীর MetaMask কে আমাদের ডিএ্যাপস-এর সাথে সংযোগ করবে। +- `getCurrentWalletConnected` - এই ফাংশনটি পৃষ্ঠা লোড হওয়ার সময় আমাদের ডিএ্যাপস-এর সাথে একটি ইথেরিয়াম অ্যাকাউন্ট ইতিমধ্যে সংযুক্ত আছে কিনা তা পরীক্ষা করবে এবং সেই অনুযায়ী আমাদের UI আপডেট করবে। +- `updateMessage` - এই ফাংশনটি স্মার্ট কন্ট্র্যাক্টে সংরক্ষিত বার্তা আপডেট করবে। এটি Hello World স্মার্ট কন্ট্র্যাক্টে একটি _লেখার_ কল করবে, তাই বার্তা আপডেট করার জন্য ব্যবহারকারীর MetaMask ওয়ালেটকে একটি ইথেরিয়াম লেনদেন স্বাক্ষর করতে হবে। + +এখন যেহেতু আমরা বুঝতে পারছি আমরা কী নিয়ে কাজ করছি, আসুন দেখি কীভাবে আমাদের স্মার্ট কন্ট্র্যাক্ট থেকে পড়তে হয়! + +### ধাপ ৩: আপনার স্মার্ট কন্ট্র্যাক্ট থেকে পড়ুন {#step-3-read-from-your-smart-contract} + +আপনার স্মার্ট কন্ট্র্যাক্ট থেকে পড়তে, আপনাকে সফলভাবে সেট আপ করতে হবে: + +- ইথেরিয়াম চেইনের সাথে একটি API সংযোগ +- আপনার স্মার্ট কন্ট্র্যাক্টের একটি লোড করা উদাহরণ +- আপনার স্মার্ট কন্ট্র্যাক্ট ফাংশনে কল করার জন্য একটি ফাংশন +- স্মার্ট কন্ট্র্যাক্ট থেকে আপনি যে ডেটা পড়ছেন তা পরিবর্তন হলে আপডেটগুলির জন্য নজর রাখার জন্য একটি লিসেনার + +এটি অনেক ধাপের মতো শোনাতে পারে, কিন্তু চিন্তা করবেন না! আমরা আপনাকে ধাপে ধাপে প্রতিটি কিভাবে করতে হয় তা দেখাব! :\) + +#### ইথেরিয়াম চেইনের সাথে একটি API সংযোগ স্থাপন করুন {#establish-an-api-connection-to-the-ethereum-chain} + +তাহলে মনে আছে এই টিউটোরিয়ালের পর্ব ২-এ, আমরা আমাদের স্মার্ট কন্ট্র্যাক্ট থেকে পড়ার জন্য আমাদের [Alchemy Web3 কী ব্যবহার করেছিলাম](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract/interacting-with-a-smart-contract#step-1-install-web3-library)? চেইন থেকে পড়ার জন্য আপনার ডিএ্যাপস-এ একটি Alchemy Web3 কীও প্রয়োজন হবে। + +আপনার যদি এটি ইতিমধ্যে না থাকে, তাহলে প্রথমে [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) ইনস্টল করুন আপনার `starter-files`-এর রুট ডিরেক্টরিতে নেভিগেট করে এবং আপনার টার্মিনালে নিম্নলিখিতটি চালান: + +```text +npm install @alch/alchemy-web3 +``` + +[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) হল [Web3.js](https://docs.web3js.org/)-এর একটি র‍্যাপার, যা উন্নত API পদ্ধতি এবং অন্যান্য গুরুত্বপূর্ণ সুবিধা প্রদান করে যাতে আপনার জীবন একজন ওয়েব3 ডেভেলপার হিসাবে সহজ হয়। এটি এমনভাবে ডিজাইন করা হয়েছে যাতে ন্যূনতম কনফিগারেশনের প্রয়োজন হয় যাতে আপনি এখনই আপনার অ্যাপে এটি ব্যবহার করা শুরু করতে পারেন! + +তারপর, আপনার প্রজেক্ট ডিরেক্টরিতে [dotenv](https://www.npmjs.com/package/dotenv) প্যাকেজটি ইনস্টল করুন, যাতে আমরা আমাদের API কী আনার পরে এটি সংরক্ষণের জন্য একটি নিরাপদ জায়গা পাই। + +```text +npm install dotenv --save +``` + +আমাদের ডিএ্যাপস-এর জন্য, **আমরা আমাদের HTTP API কী-এর পরিবর্তে আমাদের Websockets API কী ব্যবহার করব**, কারণ এটি আমাদের একটি লিসেনার সেট আপ করতে দেবে যা স্মার্ট কন্ট্র্যাক্টে সংরক্ষিত বার্তা পরিবর্তন হলে সনাক্ত করে। + +আপনার API কী পাওয়ার পর, আপনার রুট ডিরেক্টরিতে একটি `.env` ফাইল তৈরি করুন এবং এতে আপনার Alchemy Websockets url যোগ করুন। এরপরে, আপনার `.env` ফাইলটি এরকম দেখতে হবে: + +```javascript +REACT_APP_ALCHEMY_KEY = wss://eth-goerli.ws.alchemyapi.io/v2/ +``` + +এখন, আমরা আমাদের ডিএ্যাপস-এ আমাদের Alchemy Web3 এন্ডপয়েন্ট সেট আপ করতে প্রস্তুত! চলুন আমাদের `util` ফোল্ডারের মধ্যে থাকা `interact.js` ফাইলে ফিরে যাই এবং ফাইলের শীর্ষে নিম্নলিখিত কোডটি যোগ করি: + +```javascript +// interact.js + +require("dotenv").config() +const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(alchemyKey) + +//export const helloWorldContract; +``` + +উপরে, আমরা প্রথমে আমাদের `.env` ফাইল থেকে Alchemy কী ইম্পোর্ট করেছি এবং তারপর আমাদের Alchemy Web3 এন্ডপয়েন্ট স্থাপন করার জন্য `createAlchemyWeb3`-এ আমাদের `alchemyKey` পাস করেছি। + +এই এন্ডপয়েন্ট প্রস্তুত হলে, এখন আমাদের স্মার্ট কন্ট্র্যাক্ট লোড করার সময়! + +#### আপনার Hello World স্মার্ট কন্ট্র্যাক্ট লোড করা হচ্ছে {#loading-your-hello-world-smart-contract} + +আপনার Hello World স্মার্ট কন্ট্র্যাক্ট লোড করার জন্য, আপনার এর কন্ট্র্যাক্ট ঠিকানা এবং ABI প্রয়োজন হবে, উভয়ই Etherscan-এ পাওয়া যাবে যদি আপনি [এই টিউটোরিয়ালের পর্ব ৩ সম্পূর্ণ করে থাকেন।](/developers/tutorials/hello-world-smart-contract-fullstack/#part-3-publish-your-smart-contract-to-etherscan-part-3-publish-your-smart-contract-to-etherscan) + +#### Etherscan থেকে আপনার কন্ট্র্যাক্ট ABI কীভাবে পাবেন {#how-to-get-your-contract-abi-from-etherscan} + +আপনি যদি এই টিউটোরিয়ালের পর্ব ৩ এড়িয়ে যান, আপনি ঠিকানা [0x6f3f635A9762B47954229Ea479b4541eAF402A6A](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code) সহ HelloWorld কন্ট্র্যাক্ট ব্যবহার করতে পারেন। এর ABI [এখানে](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code) পাওয়া যাবে। + +একটি কন্ট্র্যাক্ট ABI একটি কন্ট্র্যাক্ট কোন ফাংশনটি চালু করবে তা নির্দিষ্ট করার জন্য এবং ফাংশনটি আপনার প্রত্যাশিত ফর্ম্যাটে ডেটা ফেরত দেবে তা নিশ্চিত করার জন্য প্রয়োজনীয়। আমাদের কন্ট্র্যাক্ট ABI কপি করার পর, আসুন এটিকে আপনার `src` ডিরেক্টরিতে `contract-abi.json` নামে একটি JSON ফাইল হিসেবে সংরক্ষণ করি। + +আপনার contract-abi.json আপনার src ফোল্ডারে সংরক্ষণ করা উচিত। + +আমাদের কন্ট্র্যাক্ট ঠিকানা, ABI, এবং Alchemy Web3 এন্ডপয়েন্ট নিয়ে, আমরা আমাদের স্মার্ট কন্ট্র্যাক্টের একটি উদাহরণ লোড করতে [কন্ট্র্যাক্ট পদ্ধতি](https://docs.web3js.org/api/web3-eth-contract/class/Contract) ব্যবহার করতে পারি। `interact.js` ফাইলে আপনার কন্ট্র্যাক্ট ABI ইম্পোর্ট করুন এবং আপনার কন্ট্র্যাক্ট ঠিকানা যোগ করুন। + +```javascript +// interact.js + +const contractABI = require("../contract-abi.json") +const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A" +``` + +আমরা এখন অবশেষে আমাদের `helloWorldContract` ভেরিয়েবলটি আনকমেন্ট করতে পারি, এবং আমাদের AlchemyWeb3 এন্ডপয়েন্ট ব্যবহার করে স্মার্ট কন্ট্র্যাক্ট লোড করতে পারি: + +```javascript +// interact.js +export const helloWorldContract = new web3.eth.Contract( + contractABI, + contractAddress +) +``` + +সংক্ষেপে, আপনার `interact.js`-এর প্রথম ১২টি লাইন এখন এরকম দেখতে হবে: + +```javascript +// interact.js + +require("dotenv").config() +const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(alchemyKey) + +const contractABI = require("../contract-abi.json") +const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A" + +export const helloWorldContract = new web3.eth.Contract( + contractABI, + contractAddress +) +``` + +এখন যে আমাদের কন্ট্র্যাক্ট লোড হয়েছে, আমরা আমাদের `loadCurrentMessage` ফাংশনটি বাস্তবায়ন করতে পারি! + +#### আপনার `interact.js` ফাইলে `loadCurrentMessage` বাস্তবায়ন করা {#implementing-loadCurrentMessage-in-your-interact-js-file} + +এই ফাংশনটি খুবই সহজ। আমরা আমাদের কন্ট্র্যাক্ট থেকে পড়ার জন্য একটি সহজ অ্যাসিঙ্ক web3 কল করব। আমাদের ফাংশনটি স্মার্ট কন্ট্র্যাক্টে সংরক্ষিত বার্তা ফেরত দেবে: + +আপনার `interact.js` ফাইলে `loadCurrentMessage` আপডেট করে নিম্নলিখিতটি করুন: + +```javascript +// interact.js + +export const loadCurrentMessage = async () => { + const message = await helloWorldContract.methods.message().call() + return message +} +``` + +যেহেতু আমরা এই স্মার্ট কন্ট্র্যাক্টটি আমাদের UI-তে প্রদর্শন করতে চাই, আসুন আমাদের `HelloWorld.js` কম্পোনেন্টে `useEffect` ফাংশনটি নিম্নলিখিতভাবে আপডেট করি: + +```javascript +// HelloWorld.js + +//একবারই বলা হয় +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) +}, []) +``` + +লক্ষ্য করুন, আমরা চাই যে আমাদের `loadCurrentMessage` শুধুমাত্র কম্পোনেন্টের প্রথম রেন্ডারের সময় একবার কল করা হোক। আমরা শীঘ্রই `addSmartContractListener` বাস্তবায়ন করব যাতে স্মার্ট কন্ট্র্যাক্টে বার্তা পরিবর্তন হওয়ার পরে UI স্বয়ংক্রিয়ভাবে আপডেট হয়। + +আমাদের লিসেনারে ডুব দেওয়ার আগে, আসুন দেখি আমরা এখন পর্যন্ত কী করেছি! আপনার `HelloWorld.js` এবং `interact.js` ফাইলগুলি সংরক্ষণ করুন, এবং তারপরে [http://localhost:3000/](http://localhost:3000/)-এ যান + +আপনি লক্ষ্য করবেন যে বর্তমান বার্তায় আর "নেটওয়ার্কের সাথে কোনো সংযোগ নেই" লেখা নেই। পরিবর্তে এটি স্মার্ট কন্ট্র্যাক্টে সংরক্ষিত বার্তা প্রতিফলিত করে। দারুণ! + +#### আপনার UI এখন স্মার্ট কন্ট্র্যাক্টে সংরক্ষিত বার্তা প্রতিফলিত করবে {#your-UI-should-now-reflect-the-message-stored-in-the-smart-contract} + +এখন সেই লিসেনারের কথা বলি... + +#### `addSmartContractListener` বাস্তবায়ন করুন {#implement-addsmartcontractlistener} + +আপনি যদি এই টিউটোরিয়াল সিরিজের [পর্ব ১-এ](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract#step-10-write-our-contract) লেখা `HelloWorld.sol` ফাইলটি মনে করেন, আপনি মনে করতে পারবেন যে `UpdatedMessages` নামে একটি স্মার্ট কন্ট্র্যাক্ট ইভেন্ট আছে যা আমাদের স্মার্ট কন্ট্র্যাক্টের `update` ফাংশন চালু করার পরে নির্গত হয় (লাইন ৯ এবং ২৭ দেখুন): + +```javascript +// HelloWorld.sol + +// সিমেন্টিক ভার্সনিং ব্যবহার করে সলিডিটির সংস্করণ নির্দিষ্ট করে। +// আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +pragma solidity ^0.7.3; + +// `HelloWorld` নামের একটি কন্ট্র্যাক্ট সংজ্ঞায়িত করে। +// একটি কন্ট্র্যাক্ট হল ফাংশন এবং ডেটার একটি সংগ্রহ (তার স্টেট)। একবার স্থাপন করা হলে, একটি কন্ট্র্যাক্ট ইথেরিয়াম ব্লকচেইনে একটি নির্দিষ্ট ঠিকানায় থাকে। আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +contract HelloWorld { + + //আপডেট ফাংশন কল করা হলে নির্গত হয় + //স্মার্ট কন্ট্র্যাক্ট ইভেন্টগুলি হল আপনার কন্ট্র্যাক্টের জন্য আপনার অ্যাপ ফ্রন্ট-এন্ডে ব্লকচেইনে কিছু ঘটেছে তা জানানোর একটি উপায়, যা নির্দিষ্ট ইভেন্টের জন্য 'শুনতে' পারে এবং সেগুলি ঘটলে ব্যবস্থা নিতে পারে। + event UpdatedMessages(string oldStr, string newStr); + + // `string` টাইপের একটি স্টেট ভেরিয়েবল `message` ঘোষণা করে। + // স্টেট ভেরিয়েবলগুলি হল এমন ভেরিয়েবল যার মানগুলি স্থায়ীভাবে কন্ট্র্যাক্ট স্টোরেজে সংরক্ষণ করা হয়। `public` কীওয়ার্ডটি কন্ট্র্যাক্টের বাইরে থেকে ভেরিয়েবলগুলিকে অ্যাক্সেসযোগ্য করে তোলে এবং একটি ফাংশন তৈরি করে যা অন্য কন্ট্র্যাক্ট বা ক্লায়েন্টরা মান অ্যাক্সেস করার জন্য কল করতে পারে। + string public message; + + // অনেক ক্লাস-ভিত্তিক অবজেক্ট-ওরিয়েন্টেড ভাষার মতো, একটি কনস্ট্রাক্টর হল একটি বিশেষ ফাংশন যা শুধুমাত্র কন্ট্র্যাক্ট তৈরির সময় কার্যকর করা হয়। + // কনস্ট্রাক্টরগুলি কন্ট্র্যাক্টের ডেটা ইনিশিয়ালাইজ করতে ব্যবহৃত হয়। আরও জানুন:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + constructor(string memory initMessage) { + + // একটি স্ট্রিং আর্গুমেন্ট `initMessage` গ্রহণ করে এবং কন্ট্র্যাক্টের `message` স্টোরেজ ভেরিয়েবলে মান সেট করে)। + message = initMessage; + } + + // একটি পাবলিক ফাংশন যা একটি স্ট্রিং আর্গুমেন্ট গ্রহণ করে এবং `message` স্টোরেজ ভেরিয়েবল আপডেট করে। + function update(string memory newMessage) public { + string memory oldMsg = message; + message = newMessage; + emit UpdatedMessages(oldMsg, newMessage); + } +} +``` + +স্মার্ট কন্ট্র্যাক্ট ইভেন্টগুলি আপনার কন্ট্র্যাক্টের জন্য ব্লকচেইনে কিছু ঘটেছে (অর্থাৎ, একটি _ইভেন্ট_ হয়েছে) তা আপনার ফ্রন্ট-এন্ড অ্যাপ্লিকেশনে জানানোর একটি উপায়, যা নির্দিষ্ট ইভেন্টগুলির জন্য 'শুনতে' পারে এবং সেগুলি ঘটলে ব্যবস্থা নিতে পারে। + +`addSmartContractListener` ফাংশনটি বিশেষভাবে আমাদের Hello World স্মার্ট কন্ট্র্যাক্টের `UpdatedMessages` ইভেন্টের জন্য শুনবে এবং নতুন বার্তা প্রদর্শন করার জন্য আমাদের UI আপডেট করবে। + +`addSmartContractListener`-কে নিম্নলিখিতভাবে পরিবর্তন করুন: + +```javascript +// HelloWorld.js + +function addSmartContractListener() { + helloWorldContract.events.UpdatedMessages({}, (error, data) => { + if (error) { + setStatus("😥 " + error.message) + } else { + setMessage(data.returnValues[1]) + setNewMessage("") + setStatus("🎉 আপনার বার্তা আপডেট করা হয়েছে!") + } + }) +} +``` + +আসুন দেখা যাক লিসেনার যখন একটি ইভেন্ট সনাক্ত করে তখন কী হয়: + +- যদি ইভেন্ট নির্গত হওয়ার সময় একটি ত্রুটি ঘটে, তবে এটি আমাদের `status` স্টেট ভেরিয়েবলের মাধ্যমে UI-তে প্রতিফলিত হবে। +- অন্যথায়, আমরা ফেরত দেওয়া `data` অবজেক্ট ব্যবহার করব। `data.returnValues` একটি শূন্য-সূচীকৃত অ্যারে যেখানে প্রথম উপাদানটি আগের বার্তা এবং দ্বিতীয় উপাদানটি আপডেট করা বার্তা সংরক্ষণ করে। সব মিলিয়ে, একটি সফল ইভেন্টে আমরা আমাদের `message` স্ট্রিংটিকে আপডেট করা বার্তায় সেট করব, `newMessage` স্ট্রিংটি পরিষ্কার করব এবং আমাদের `status` স্টেট ভেরিয়েবলটি আপডেট করব যাতে প্রতিফলিত হয় যে আমাদের স্মার্ট কন্ট্র্যাক্টে একটি নতুন বার্তা প্রকাশিত হয়েছে। + +অবশেষে, আসুন আমাদের লিসেনারকে আমাদের `useEffect` ফাংশনে কল করি যাতে এটি `HelloWorld.js` কম্পোনেন্টের প্রথম রেন্ডারে শুরু হয়। সব মিলিয়ে, আপনার `useEffect` ফাংশনটি এইরকম দেখতে হবে: + +```javascript +// HelloWorld.js + +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) + addSmartContractListener() +}, []) +``` + +এখন যেহেতু আমরা আমাদের স্মার্ট কন্ট্র্যাক্ট থেকে পড়তে সক্ষম, এটিতে কীভাবে লিখতেও হয় তা বের করা দুর্দান্ত হবে! তবে, আমাদের ডিএ্যাপস-এ লিখতে হলে, আমাদের প্রথমে একটি ইথেরিয়াম ওয়ালেট এর সাথে সংযুক্ত থাকতে হবে। + +সুতরাং, এরপর আমরা আমাদের ইথেরিয়াম ওয়ালেট (MetaMask) সেট আপ করব এবং তারপরে এটি আমাদের ডিএ্যাপস-এর সাথে সংযোগ করব! + +### ধাপ ৪: আপনার ইথেরিয়াম ওয়ালেট সেট আপ করুন {#step-4-set-up-your-ethereum-wallet} + +ইথেরিয়াম চেইনে কিছু লিখতে, ব্যবহারকারীদের তাদের ভার্চুয়াল ওয়ালেটের ব্যক্তিগত কী ব্যবহার করে লেনদেন স্বাক্ষর করতে হবে। এই টিউটোরিয়ালের জন্য, আমরা [MetaMask](https://metamask.io/) ব্যবহার করব, যা ব্রাউজারে একটি ভার্চুয়াল ওয়ালেট এবং আপনার ইথেরিয়াম অ্যাকাউন্টের ঠিকানা পরিচালনা করতে ব্যবহৃত হয়, কারণ এটি শেষ-ব্যবহারকারীর জন্য এই লেনদেন স্বাক্ষর করা খুব সহজ করে তোলে। + +আপনি যদি Ethereum-এ লেনদেন কিভাবে কাজ করে সে সম্পর্কে আরও বুঝতে চান, তাহলে ইথেরিয়াম ফাইন্ডেশনের [এই পৃষ্ঠাটি](/developers/docs/transactions/) দেখুন। + +#### MetaMask ডাউনলোড করুন {#download-metamask} + +আপনি [এখানে](https://metamask.io/download) বিনামূল্যে একটি MetaMask অ্যাকাউন্ট ডাউনলোড এবং তৈরি করতে পারেন। যখন আপনি একটি অ্যাকাউন্ট তৈরি করছেন, বা যদি আপনার আগে থেকেই একটি অ্যাকাউন্ট থাকে, তবে উপরের ডানদিকে থাকা "Goerli Test Network"-এ স্যুইচ করতে ভুলবেন না (যাতে আমরা আসল টাকা নিয়ে কাজ না করি)। + +#### একটি ফসেট থেকে ইথার যোগ করুন {#add-ether-from-a-faucet} + +ইথেরিয়াম ব্লকচেইনে একটি লেনদেন স্বাক্ষর করার জন্য, আমাদের কিছু নকল Eth লাগবে। Eth পেতে আপনি [FaucETH](https://fauceth.komputing.org)-এ যেতে পারেন এবং আপনার Goerli অ্যাকাউন্টের ঠিকানা লিখতে পারেন, "ফান্ড অনুরোধ করুন"-এ ক্লিক করুন, তারপরে ড্রপডাউনে "Ethereum Testnet Goerli" নির্বাচন করুন এবং অবশেষে আবার "ফান্ড অনুরোধ করুন" বোতামটি ক্লিক করুন। এর কিছুক্ষণ পরেই আপনার MetaMask অ্যাকাউন্টে Eth দেখতে পাবেন! + +#### আপনার ব্যালেন্স পরীক্ষা করুন {#check-your-balance} + +আমাদের ব্যালেন্স সেখানে আছে কিনা তা দুবার চেক করতে, চলুন [Alchemy’s composer tool](https://composer.alchemyapi.io/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D) ব্যবহার করে একটি [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance) রিকোয়েস্ট করি। এটি আমাদের ওয়ালেটে থাকা Eth-এর পরিমাণ ফেরত দেবে। আপনার MetaMask অ্যাকাউন্ট অ্যাড্রেস ইনপুট করার পরে এবং “অনুরোধ পাঠান”-এ ক্লিক করার পরে, আপনার এইরকম একটি প্রতিক্রিয়া দেখা উচিত: + +```text +{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"} +``` + +**দ্রষ্টব্য:** এই ফলাফলটি wei-তে, eth-এ নয়। Wei ইথারের ক্ষুদ্রতম একক হিসাবে ব্যবহৃত হয়। wei থেকে eth-এ রূপান্তর হলো: 1 eth = 10¹⁸ wei। সুতরাং যদি আমরা 0xde0b6b3a7640000 কে দশমিকে রূপান্তর করি, আমরা 1\*10¹⁸ পাই যা 1 eth-এর সমান। + +যাক বাবা! আমাদের নকল টাকা সব আছে! 🤑 + +### ধাপ ৫: MetaMask-কে আপনার UI-এর সাথে সংযোগ করুন {#step-5-connect-metamask-to-your-UI} + +এখন যেহেতু আমাদের MetaMask ওয়ালেট সেট আপ করা হয়েছে, চলুন আমাদের dApp-কে এর সাথে সংযুক্ত করি! + +#### `connectWallet` ফাংশন {#the-connectWallet-function} + +আমাদের `interact.js` ফাইলে, আসুন `connectWallet` ফাংশনটি বাস্তবায়ন করি, যা আমরা তারপরে আমাদের `HelloWorld.js` কম্পোনেন্টে কল করতে পারি। + +আসুন `connectWallet`-কে নিম্নলিখিতভাবে পরিবর্তন করি: + +```javascript +// interact.js + +export const connectWallet = async () => { + if (window.ethereum) { + try { + const addressArray = await window.ethereum.request({ + method: "eth_requestAccounts", + }) + const obj = { + status: "👆🏽 উপরের টেক্সট-ফিল্ডে একটি বার্তা লিখুন।", + address: addressArray[0], + } + return obj + } catch (err) { + return { + address: "", + status: "😥 " + err.message, + } + } + } else { + return { + address: "", + status: ( + +

+ {" "} + 🦊 + আপনাকে অবশ্যই আপনার ব্রাউজারে MetaMask ইনস্টল করতে হবে, যা একটি ভার্চুয়াল ইথেরিয়াম ওয়ালেট। + +

+
+ ), + } + } +} +``` + +তাহলে এই বিশাল কোড ব্লকটি ঠিক কী করে? + +ওয়েল, প্রথমে, এটি পরীক্ষা করে যে আপনার ব্রাউজারে `window.ethereum` সক্ষম আছে কিনা। + +`window.ethereum` হল একটি গ্লোবাল API যা MetaMask এবং অন্যান্য ওয়ালেট প্রদানকারীরা ইনজেক্ট করে, যা ওয়েবসাইটগুলিকে ব্যবহারকারীদের ইথেরিয়াম অ্যাকাউন্টগুলির জন্য অনুরোধ করার অনুমতি দেয়। অনুমোদিত হলে, এটি ব্যবহারকারী যে ব্লকচেইনগুলির সাথে সংযুক্ত রয়েছে সেখান থেকে ডেটা পড়তে পারে এবং ব্যবহারকারীকে বার্তা এবং লেনদেন স্বাক্ষর করার পরামর্শ দিতে পারে। আরও তথ্যের জন্য [MetaMask ডক্স](https://docs.metamask.io/guide/ethereum-provider.html#table-of-contents) দেখুন! + +যদি `window.ethereum` উপস্থিত _না থাকে_, তার মানে MetaMask ইনস্টল করা নেই। এর ফলে একটি JSON অবজেক্ট রিটার্ন করা হয়, যেখানে রিটার্ন করা `address` একটি খালি স্ট্রিং এবং `status` JSX অবজেক্টটি ব্যবহারকারীকে MetaMask ইনস্টল করার বার্তা দেয়। + +এখন যদি `window.ethereum` উপস্থিত _থাকে_, তখনই বিষয়গুলো আকর্ষণীয় হয়ে ওঠে। + +একটি try/catch লুপ ব্যবহার করে, আমরা [`window.ethereum.request({ method: \"eth_requestAccounts\" });`](https://docs.metamask.io/guide/rpc-api.html#eth-requestaccounts) কল করে MetaMask-এর সাথে সংযোগ করার চেষ্টা করব। এই ফাংশনটি কল করলে ব্রাউজারে MetaMask খুলবে, যেখানে ব্যবহারকারীকে তাদের ওয়ালেট আপনার dApp-এর সাথে সংযোগ করার জন্য অনুরোধ করা হবে। + +- ব্যবহারকারী যদি সংযোগ করতে চান, তবে `method: "eth_requestAccounts"` একটি অ্যারে প্রদান করবে যাতে ব্যবহারকারীর সমস্ত অ্যাকাউন্ট ঠিকানা থাকবে যা ডিএ্যাপস-এর সাথে সংযুক্ত হয়েছে। সবমিলিয়ে, আমাদের `connectWallet` ফাংশনটি একটি JSON অবজেক্ট রিটার্ন করবে যা এই অ্যারের _প্রথম_ `address` (লাইন ৯ দেখুন) এবং একটি `status` বার্তা ধারণ করবে যা ব্যবহারকারীকে স্মার্ট কন্ট্র্যাক্টে একটি বার্তা লিখতে অনুরোধ করে। +- যদি ব্যবহারকারী সংযোগ প্রত্যাখ্যান করে, তাহলে JSON অবজেক্টটি রিটার্ন করা `address`-এর জন্য একটি খালি স্ট্রিং এবং একটি `status` বার্তা ধারণ করবে যা ব্যবহারকারীর সংযোগ প্রত্যাখ্যান করার বিষয়টি প্রতিফলিত করে। + +এখন যেহেতু আমরা এই `connectWallet` ফাংশনটি লিখেছি, পরবর্তী ধাপ হল এটিকে আমাদের `HelloWorld.js` কম্পোনেন্টে কল করা। + +#### `connectWallet` ফাংশনটি আপনার `HelloWorld.js` UI কম্পোনেন্টে যোগ করুন {#add-the-connectWallet-function-to-your-HelloWorld-js-ui-component} + +`HelloWorld.js`-এর `connectWalletPressed` ফাংশনে নেভিগেট করুন এবং এটিকে নিম্নলিখিতভাবে আপডেট করুন: + +```javascript +// HelloWorld.js + +const connectWalletPressed = async () => { + const walletResponse = await connectWallet() + setStatus(walletResponse.status) + setWallet(walletResponse.address) +} +``` + +লক্ষ্য করুন কীভাবে আমাদের বেশিরভাগ কার্যকারিতা `interact.js` ফাইল থেকে আমাদের `HelloWorld.js` কম্পোনেন্ট থেকে বিমূর্ত করা হয়েছে? এটি যাতে আমরা M-V-C প্যারাডাইম মেনে চলি! + +`connectWalletPressed`-এ, আমরা কেবল আমাদের ইম্পোর্ট করা `connectWallet` ফাংশনে একটি await কল করি, এবং এর প্রতিক্রিয়ার সাহায্যে, আমরা তাদের স্টেট হুকগুলির মাধ্যমে আমাদের `status` এবং `walletAddress` ভেরিয়েবলগুলি আপডেট করি। + +এখন, আসুন উভয় ফাইল (`HelloWorld.js` এবং `interact.js`) সংরক্ষণ করি এবং আমাদের UI পরীক্ষা করি। + +আপনার ব্রাউজারে [http://localhost:3000/](http://localhost:3000/) পৃষ্ঠাটি খুলুন এবং পৃষ্ঠার উপরের ডানদিকে "ওয়ালেট সংযোগ করুন" বোতামটি টিপুন। + +আপনার যদি MetaMask ইনস্টল করা থাকে, তাহলে আপনাকে আপনার ওয়ালেটটি আপনার dApp-এর সাথে সংযোগ করার জন্য অনুরোধ করা হবে। সংযোগ করার জন্য আমন্ত্রণ গ্রহণ করুন। + +আপনি দেখতে পাবেন যে ওয়ালেট বোতামটি এখন প্রতিফলিত করছে যে আপনার ঠিকানা সংযুক্ত! ইয়াসসসস 🔥 + +এরপর, পৃষ্ঠাটি রিফ্রেশ করার চেষ্টা করুন... এটা অদ্ভুত। আমাদের ওয়ালেট বোতামটি আমাদের MetaMask সংযোগ করার জন্য অনুরোধ করছে, যদিও এটি ইতিমধ্যে সংযুক্ত রয়েছে... + +তবে, ভয় পাবেন না! আমরা সহজেই এটি সমাধান করতে পারি (বুঝতে পারছেন?) `getCurrentWalletConnected` বাস্তবায়ন করে, যা পরীক্ষা করবে যে কোনও ঠিকানা ইতিমধ্যে আমাদের ডিএ্যাপস-এর সাথে সংযুক্ত আছে কিনা এবং সেই অনুযায়ী আমাদের UI আপডেট করবে! + +#### `getCurrentWalletConnected` ফাংশন {#the-getcurrentwalletconnected-function} + +`interact.js` ফাইলে আপনার `getCurrentWalletConnected` ফাংশনটি নিম্নলিখিতভাবে আপডেট করুন: + +```javascript +// interact.js + +export const getCurrentWalletConnected = async () => { + if (window.ethereum) { + try { + const addressArray = await window.ethereum.request({ + method: "eth_accounts", + }) + if (addressArray.length > 0) { + return { + address: addressArray[0], + status: "👆🏽 উপরের টেক্সট-ফিল্ডে একটি বার্তা লিখুন।", + } + } else { + return { + address: "", + status: "🦊 উপরের ডানদিকের বোতাম ব্যবহার করে MetaMask-এর সাথে সংযোগ করুন।", + } + } + } catch (err) { + return { + address: "", + status: "😥 " + err.message, + } + } + } else { + return { + address: "", + status: ( + +

+ {" "} + 🦊 + আপনাকে অবশ্যই আপনার ব্রাউজারে MetaMask ইনস্টল করতে হবে, যা একটি ভার্চুয়াল ইথেরিয়াম ওয়ালেট। + +

+
+ ), + } + } +} +``` + +এই কোডটি _খুবই_ অনুরূপ `connectWallet` ফাংশনটির মতো যা আমরা আগের ধাপে লিখেছি। + +প্রধান পার্থক্য হল `eth_requestAccounts` মেথড কল করার পরিবর্তে, যা ব্যবহারকারীর ওয়ালেট সংযোগ করার জন্য MetaMask খোলে, এখানে আমরা `eth_accounts` মেথড কল করি, যা সহজভাবে আমাদের dApp-এর সাথে বর্তমানে সংযুক্ত MetaMask অ্যাড্রেসগুলো ধারণকারী একটি অ্যারে রিটার্ন করে। + +এই ফাংশনটি কার্যকর দেখতে, আসুন এটিকে আমাদের `HelloWorld.js` কম্পোনেন্টের `useEffect` ফাংশনে কল করি: + +```javascript +// HelloWorld.js + +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) + addSmartContractListener() + + const { address, status } = await getCurrentWalletConnected() + setWallet(address) + setStatus(status) +}, []) +``` + +লক্ষ্য করুন, আমরা `getCurrentWalletConnected`-এ আমাদের কলের প্রতিক্রিয়া ব্যবহার করে আমাদের `walletAddress` এবং `status` স্টেট ভেরিয়েবলগুলিকে আপডেট করি। + +এখন যে আপনি এই কোডটি যোগ করেছেন, আসুন আমাদের ব্রাউজার উইন্ডোটি রিফ্রেশ করার চেষ্টা করি। + +দারুণ! বোতামটি বলা উচিত যে আপনি সংযুক্ত, এবং আপনার সংযুক্ত ওয়ালেটের অ্যাড্রেসের একটি প্রিভিউ দেখানো উচিত - এমনকি আপনি রিফ্রেশ করার পরেও! + +#### `addWalletListener` বাস্তবায়ন করুন {#implement-addwalletlistener} + +আমাদের dApp ওয়ালেট সেটআপের চূড়ান্ত ধাপ হল ওয়ালেট লিসেনার ইমপ্লিমেন্ট করা যাতে আমাদের UI আপডেট হয় যখন আমাদের ওয়ালেটের স্টেট পরিবর্তন হয়, যেমন যখন ব্যবহারকারী সংযোগ বিচ্ছিন্ন করে বা অ্যাকাউন্ট পরিবর্তন করে। + +আপনার `HelloWorld.js` ফাইলে, আপনার `addWalletListener` ফাংশনটি নিম্নলিখিতভাবে পরিবর্তন করুন: + +```javascript +// HelloWorld.js + +function addWalletListener() { + if (window.ethereum) { + window.ethereum.on("accountsChanged", (accounts) => { + if (accounts.length > 0) { + setWallet(accounts[0]) + setStatus("👆🏽 উপরের টেক্সট-ফিল্ডে একটি বার্তা লিখুন।") + } else { + setWallet("") + setStatus("🦊 উপরের ডানদিকের বোতাম ব্যবহার করে MetaMask-এর সাথে সংযোগ করুন।") + } + }) + } else { + setStatus( +

+ {" "} + 🦊 + আপনাকে অবশ্যই আপনার ব্রাউজারে MetaMask ইনস্টল করতে হবে, যা একটি ভার্চুয়াল ইথেরিয়াম ওয়ালেট। + +

+ ) + } +} +``` + +আমি বাজি ধরতে পারি যে এই মুহূর্তে এখানে কী ঘটছে তা বোঝার জন্য আপনার আর আমাদের সাহায্যের প্রয়োজন নেই, তবে সম্পূর্ণতার জন্য, আসুন দ্রুত এটি ভেঙে দেখা যাক: + +- প্রথমে, আমাদের ফাংশন পরীক্ষা করে যে `window.ethereum` সক্রিয় আছে কিনা (অর্থাৎ MetaMask ইনস্টল করা আছে)। + - যদি না থাকে, আমরা কেবল আমাদের `status` স্টেট ভেরিয়েবলটিকে একটি JSX স্ট্রিং-এ সেট করি যা ব্যবহারকারীকে MetaMask ইনস্টল করার জন্য অনুরোধ করে। + - যদি এটি সক্রিয় থাকে, আমরা লাইন ৩-এ `window.ethereum.on(\"accountsChanged\")` লিসেনার সেট আপ করি যা MetaMask ওয়ালেটের স্টেট পরিবর্তন শোনে, যার মধ্যে রয়েছে যখন ব্যবহারকারী dApp-এ একটি অতিরিক্ত অ্যাকাউন্ট সংযোগ করে, অ্যাকাউন্ট পরিবর্তন করে বা একটি অ্যাকাউন্ট সংযোগ বিচ্ছিন্ন করে। যদি অন্তত একটি অ্যাকাউন্ট সংযুক্ত থাকে, তাহলে `walletAddress` স্টেট ভেরিয়েবলটি লিসেনারের দ্বারা রিটার্ন করা `accounts` অ্যারের প্রথম অ্যাকাউন্ট হিসেবে আপডেট হয়। অন্যথায়, `walletAddress` একটি খালি স্ট্রিং হিসাবে সেট করা হয়। + +সবশেষে, আমাদের এটিকে আমাদের `useEffect` ফাংশনে কল করতে হবে: + +```javascript +// HelloWorld.js + +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) + addSmartContractListener() + + const { address, status } = await getCurrentWalletConnected() + setWallet(address) + setStatus(status) + + addWalletListener() +}, []) +``` + +এবং এটাই সব! আমরা সফলভাবে আমাদের সমস্ত ওয়ালেট কার্যকারিতা প্রোগ্রামিং সম্পন্ন করেছি! এখন আমাদের শেষ কাজ: আমাদের স্মার্ট কন্ট্র্যাক্টে সংরক্ষিত বার্তা আপডেট করা! + +### ধাপ ৬: `updateMessage` ফাংশনটি বাস্তবায়ন করুন {#step-6-implement-the-updateMessage-function} + +ঠিক আছে বন্ধুরা, আমরা শেষ পর্যায়ে পৌঁছেছি! আপনার `interact.js` ফাইলের `updateMessage`-এ, আমরা নিম্নলিখিতগুলি করতে যাচ্ছি: + +1. নিশ্চিত করুন যে আমরা আমাদের স্মার্ট কন্টাক্টে যে বার্তাটি প্রকাশ করতে চাই তা বৈধ +2. MetaMask ব্যবহার করে আমাদের লেনদেন স্বাক্ষর করুন +3. আমাদের `HelloWorld.js` ফ্রন্টএন্ড কম্পোনেন্ট থেকে এই ফাংশনটি কল করুন + +এতে খুব বেশি সময় লাগবে না; চলুন এই ডিএ্যাপসটি শেষ করি! + +#### ইনপুট ত্রুটি হ্যান্ডলিং {#input-error-handling} + +স্বাভাবিকভাবেই, ফাংশনের শুরুতে কিছু ধরণের ইনপুট ত্রুটি হ্যান্ডলিং থাকা যুক্তিযুক্ত। + +আমরা চাইব যে যদি কোনো MetaMask এক্সটেনশন ইনস্টল করা না থাকে, কোনো ওয়ালেট সংযুক্ত না থাকে (অর্থাৎ, পাস করা `address` একটি খালি স্ট্রিং হয়), বা `message` একটি খালি স্ট্রিং হয়, তাহলে আমাদের ফাংশনটি তাড়াতাড়ি ফিরে আসবে। `updateMessage`-এ নিম্নলিখিত ত্রুটি হ্যান্ডলিং যোগ করুন: + +```javascript +// interact.js + +export const updateMessage = async (address, message) => { + if (!window.ethereum || address === null) { + return { + status: + "💡 ব্লকচেইনে বার্তা আপডেট করতে আপনার MetaMask ওয়ালেট সংযোগ করুন।", + } + } + + if (message.trim() === "") { + return { + status: "❌ আপনার বার্তা একটি খালি স্ট্রিং হতে পারে না।", + } + } +} +``` + +এখন যে এটিতে সঠিক ইনপুট ত্রুটি হ্যান্ডলিং রয়েছে, এখন MetaMask এর মাধ্যমে লেনদেন স্বাক্ষর করার সময়! + +#### আমাদের লেনদেন স্বাক্ষর করা {#signing-our-transaction} + +আপনি যদি ঐতিহ্যবাহী ওয়েব3 ইথেরিয়াম লেনদেনের সাথে ইতিমধ্যে স্বাচ্ছন্দ্য বোধ করেন, তবে আমরা পরবর্তীতে যে কোডটি লিখব তা খুব পরিচিত হবে। আপনার ইনপুট ত্রুটি হ্যান্ডলিং কোডের নীচে, `updateMessage`-এ নিম্নলিখিতগুলি যোগ করুন: + +```javascript +// interact.js + +//লেনদেন প্যারামিটার সেট আপ করুন +const transactionParameters = { + to: contractAddress, // কন্ট্র্যাক্ট প্রকাশনার সময় ছাড়া প্রয়োজনীয়। + from: address, // ব্যবহারকারীর সক্রিয় ঠিকানার সাথে মিলতে হবে। + data: helloWorldContract.methods.update(message).encodeABI(), +} + +//লেনদেন স্বাক্ষর করুন +try { + const txHash = await window.ethereum.request({ + method: "eth_sendTransaction", + params: [transactionParameters], + }) + return { + status: ( + + ✅{" "} + + Etherscan-এ আপনার লেনদেনের স্থিতি দেখুন! + +
+ ℹ️ নেটওয়ার্ক দ্বারা লেনদেন যাচাই হয়ে গেলে, বার্তাটি + স্বয়ংক্রিয়ভাবে আপডেট হয়ে যাবে। +
+ ), + } +} catch (error) { + return { + status: "😥 " + error.message, + } +} +``` + +আসুন দেখা যাক কী ঘটছে। প্রথমে, আমরা আমাদের লেনদেনের প্যারামিটার সেট আপ করি, যেখানে: + +- `to` প্রাপকের ঠিকানা (আমাদের স্মার্ট কন্ট্র্যাক্ট) নির্দিষ্ট করে +- `from` লেনদেনের স্বাক্ষরকারীকে নির্দিষ্ট করে, যা আমাদের ফাংশনে পাস করা `address` ভেরিয়েবল +- `data` আমাদের Hello World স্মার্ট কন্ট্র্যাক্টের `update` পদ্ধতিতে কল ধারণ করে, যা ইনপুট হিসাবে আমাদের `message` স্ট্রিং ভেরিয়েবল গ্রহণ করে + +তারপর, আমরা একটি অ্যাওয়েট কল করি, `window.ethereum.request`, যেখানে আমরা MetaMask-কে লেনদেন স্বাক্ষর করতে বলি। লক্ষ্য করুন, ১১ এবং ১২ লাইনে, আমরা আমাদের eth পদ্ধতি, `eth_sendTransaction` নির্দিষ্ট করছি এবং আমাদের `transactionParameters` পাস করছি। + +এই সময়ে, MetaMask ব্রাউজারে খুলবে, এবং ব্যবহারকারীকে ট্রানজ্যাকশন সাইন বা প্রত্যাখ্যান করতে অনুরোধ করবে। + +- যদি লেনদেন সফল হয়, ফাংশনটি একটি JSON অবজেক্ট প্রদান করবে যেখানে `status` JSX স্ট্রিং ব্যবহারকারীকে তাদের লেনদেন সম্পর্কে আরও তথ্যের জন্য Etherscan দেখতে অনুরোধ করে। +- যদি লেনদেন ব্যর্থ হয়, ফাংশনটি একটি JSON অবজেক্ট প্রদান করবে যেখানে `status` স্ট্রিং ত্রুটির বার্তা রিলে করে। + +সব মিলিয়ে, আমাদের `updateMessage` ফাংশনটি এইরকম দেখতে হবে: + +```javascript +// interact.js + +export const updateMessage = async (address, message) => { + //ইনপুট ত্রুটি হ্যান্ডলিং + if (!window.ethereum || address === null) { + return { + status: + "💡 ব্লকচেইনে বার্তা আপডেট করতে আপনার MetaMask ওয়ালেট সংযোগ করুন।", + } + } + + if (message.trim() === "") { + return { + status: "❌ আপনার বার্তা একটি খালি স্ট্রিং হতে পারে না।", + } + } + + //লেনদেন প্যারামিটার সেট আপ করুন + const transactionParameters = { + to: contractAddress, // কন্ট্র্যাক্ট প্রকাশনার সময় ছাড়া প্রয়োজনীয়। + from: address, // ব্যবহারকারীর সক্রিয় ঠিকানার সাথে মিলতে হবে। + data: helloWorldContract.methods.update(message).encodeABI(), + } + + //লেনদেন স্বাক্ষর করুন + try { + const txHash = await window.ethereum.request({ + method: "eth_sendTransaction", + params: [transactionParameters], + }) + return { + status: ( + + ✅{" "} + + Etherscan-এ আপনার লেনদেনের স্থিতি দেখুন! + +
+ ℹ️ নেটওয়ার্ক দ্বারা লেনদেন যাচাই হয়ে গেলে, বার্তাটি + স্বয়ংক্রিয়ভাবে আপডেট হয়ে যাবে। +
+ ), + } + } catch (error) { + return { + status: "😥 " + error.message, + } + } +} +``` + +সবশেষে, আমাদের `updateMessage` ফাংশনটিকে আমাদের `HelloWorld.js` কম্পোনেন্টের সাথে সংযোগ করতে হবে। + +#### `updateMessage`-কে `HelloWorld.js` ফ্রন্টএন্ডের সাথে সংযোগ করুন {#connect-updatemessage-to-the-helloworld-js-frontend} + +আমাদের `onUpdatePressed` ফাংশনটি ইম্পোর্ট করা `updateMessage` ফাংশনে একটি অ্যাওয়েট কল করবে এবং `status` স্টেট ভেরিয়েবলটি পরিবর্তন করবে যাতে আমাদের লেনদেন সফল হয়েছে নাকি ব্যর্থ হয়েছে তা প্রতিফলিত হয়: + +```javascript +// HelloWorld.js + +const onUpdatePressed = async () => { + const { status } = await updateMessage(walletAddress, newMessage) + setStatus(status) +} +``` + +এটি খুবই পরিচ্ছন্ন এবং সহজ। আর জানেন কী... আপনার ডিএ্যাপস সম্পূর্ণ!!! + +এগিয়ে যান এবং **আপডেট** বোতামটি পরীক্ষা করুন! + +### আপনার নিজের কাস্টম ডিএ্যাপস তৈরি করুন {#make-your-own-custom-dapp} + +ওয়াও, আপনি টিউটোরিয়ালের শেষ পর্যন্ত পৌঁছেছেন! সংক্ষেপে, আপনি শিখেছেন কীভাবে: + +- আপনার ডিএ্যাপস প্রজেক্টের সাথে একটি MetaMask ওয়ালেট সংযোগ করা +- [Alchemy Web3](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) API ব্যবহার করে আপনার স্মার্ট কন্ট্র্যাক্ট থেকে ডেটা পড়া +- MetaMask ব্যবহার করে ইথেরিয়াম লেনদেন স্বাক্ষর করা + +এখন আপনি এই টিউটোরিয়ালের দক্ষতা প্রয়োগ করে আপনার নিজের কাস্টম ডিএ্যাপস প্রজেক্ট তৈরি করার জন্য সম্পূর্ণরূপে সজ্জিত! বরাবরের মতো, আপনার যদি কোনো প্রশ্ন থাকে, তাহলে [Alchemy Discord](https://discord.gg/gWuC7zB)-এ আমাদের কাছে সাহায্যের জন্য পৌঁছাতে দ্বিধা করবেন না। 🧙‍♂️ + +এই টিউটোরিয়ালটি সম্পূর্ণ করার পর, টুইটারে [@alchemyplatform](https://twitter.com/AlchemyPlatform)-এ আমাদের ট্যাগ করে আপনার অভিজ্ঞতা কেমন ছিল বা আপনার কোনো মতামত থাকলে আমাদের জানান! diff --git a/public/content/translations/bn/developers/tutorials/hello-world-smart-contract/index.md b/public/content/translations/bn/developers/tutorials/hello-world-smart-contract/index.md new file mode 100644 index 00000000000..bf5ea083d3a --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/hello-world-smart-contract/index.md @@ -0,0 +1,366 @@ +--- +title: "শিক্ষানবিশদের জন্য হ্যালো ওয়ার্ল্ড স্মার্ট কন্ট্র্যাক্ট" +description: "Ethereum-এ একটি সহজ স্মার্ট কন্ট্র্যাক্ট লেখা এবং স্থাপন করার উপর একটি পরিচিতি টিউটোরিয়াল।" +author: "elanh" +tags: + [ + "সলিডিটি", + "hardhat", + "alchemy", + "স্মার্ট কন্ট্র্যাক্ট", + "ডেপ্লয়িং" + ] +skill: beginner +lang: bn +published: 2021-03-31 +--- + +আপনি যদি ব্লকচেইন ডেভেলপমেন্টে নতুন হন এবং কোথা থেকে শুরু করবেন তা না জানেন, অথবা আপনি যদি শুধুমাত্র স্মার্ট কন্ট্র্যাক্টগুলি কীভাবে স্থাপন এবং তার সাথে ইন্টারঅ্যাক্ট করতে হয় তা বুঝতে চান, তাহলে এই নির্দেশিকাটি আপনার জন্য। আমরা একটি ভার্চুয়াল ওয়ালেট [MetaMask](https://metamask.io/), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org/), এবং [Alchemy](https://www.alchemy.com/eth) ব্যবহার করে Sepolia টেস্ট নেটওয়ার্কে একটি সহজ স্মার্ট কন্ট্র্যাক্ট তৈরি এবং স্থাপন করার মাধ্যমে দেখাব (যদি আপনি এখনও এর কোনটির অর্থ না বোঝেন তবে চিন্তা করবেন না, আমরা এটি ব্যাখ্যা করব)। + +এই টিউটোরিয়ালের [পার্ট 2](https://docs.alchemy.com/docs/interacting-with-a-smart-contract)-তে আমরা দেখব কিভাবে আমাদের স্মার্ট কন্ট্র্যাক্টটি এখানে স্থাপন করার পরে তার সাথে ইন্টারঅ্যাক্ট করা যায়, এবং [পার্ট 3](https://www.alchemy.com/docs/submitting-your-smart-contract-to-etherscan)-তে আমরা দেখব কিভাবে এটি Etherscan-এ প্রকাশ করা যায়। + +যেকোনো সময়ে আপনার কোনো প্রশ্ন থাকলে [Alchemy Discord](https://discord.gg/gWuC7zB)-এ যোগাযোগ করতে পারেন! + +## ধাপ 1: Ethereum নেটওয়ার্কের সাথে সংযোগ করুন {#step-1} + +Ethereum চেইনে রিকুয়েস্ট করার অনেক উপায় আছে। সহজ করার জন্য, আমরা Alchemy-তে একটি বিনামূল্যের অ্যাকাউন্ট ব্যবহার করব, এটি একটি ব্লকচেইন ডেভেলপার প্ল্যাটফর্ম এবং API যা আমাদের নিজস্ব নোড চালানোর প্রয়োজন ছাড়াই Ethereum চেইনের সাথে যোগাযোগ করতে দেয়। প্ল্যাটফর্মটিতে পর্যবেক্ষণ এবং বিশ্লেষণের জন্য ডেভেলপার টুলসও রয়েছে যা আমরা এই টিউটোরিয়ালে আমাদের স্মার্ট কন্ট্র্যাক্ট স্থাপনার আড়ালে কী ঘটছে তা বোঝার জন্য ব্যবহার করব। আপনার যদি আগে থেকেই একটি Alchemy অ্যাকাউন্ট না থাকে, [আপনি এখানে বিনামূল্যে সাইন আপ করতে পারেন](https://dashboard.alchemy.com/signup)। + +## ধাপ 2: আপনার অ্যাপ (এবং API কী) তৈরি করুন {#step-2} + +একবার আপনি একটি Alchemy অ্যাকাউন্ট তৈরি করে ফেললে, আপনি একটি অ্যাপ তৈরি করে একটি API কী জেনারেট করতে পারেন। এটি আমাদের Sepolia টেস্ট নেটওয়ার্কে অনুরোধ করার অনুমতি দেবে। আপনি যদি টেস্টনেটের সাথে পরিচিত না হন, তাহলে [এই পেজটি](/developers/docs/networks/) দেখুন। + +1. ন্যাভ বারে "Select an app" নির্বাচন করে এবং "Create new app" ক্লিক করে আপনার Alchemy ড্যাশবোর্ডের "Create new app" পেজে যান + +![হ্যালো ওয়ার্ল্ড অ্যাপ তৈরি করুন](./hello-world-create-app.png) + +2. আপনার অ্যাপের নাম দিন “Hello World”, একটি সংক্ষিপ্ত বিবরণ দিন, এবং একটি ব্যবহারের ক্ষেত্র বেছে নিন, যেমন, "Infra & Tooling."। এরপরে, "Ethereum" অনুসন্ধান করুন এবং নেটওয়ার্কটি নির্বাচন করুন। + +![অ্যাপ ভিউ হ্যালো ওয়ার্ল্ড তৈরি করুন](./create-app-view-hello-world.png) + +3. এগিয়ে যেতে "Next" এ ক্লিক করুন, তারপর "Create app" এ এবং এটিই! আপনার অ্যাপটি একটি API কী কপির জন্য উপলব্ধ সহ ন্যাভ বার ড্রপডাউন মেনুতে উপস্থিত হওয়া উচিত। + +## ধাপ 3: একটি Ethereum অ্যাকাউন্ট (অ্যাড্রেস) তৈরি করুন {#step-3} + +লেনদেন পাঠানো এবং গ্রহণ করার জন্য আমাদের একটি Ethereum অ্যাকাউন্ট প্রয়োজন। এই টিউটোরিয়ালের জন্য, আমরা MetaMask ব্যবহার করব, যা ব্রাউজারের একটি ভার্চুয়াল ওয়ালেট এবং আপনার Ethereum অ্যাকাউন্ট অ্যাড্রেস পরিচালনা করতে ব্যবহৃত হয়। [লেনদেন](/developers/docs/transactions/) সম্পর্কে আরও। + +আপনি [এখানে](https://metamask.io/download) বিনামূল্যে MetaMask ডাউনলোড করতে এবং একটি Ethereum অ্যাকাউন্ট তৈরি করতে পারেন। আপনি যখন একটি অ্যাকাউন্ট তৈরি করছেন, অথবা যদি আপনার আগে থেকেই একটি অ্যাকাউন্ট থাকে, তাহলে নেটওয়ার্ক ড্রপডাউন মেনু ব্যবহার করে "Sepolia" টেস্ট নেটওয়ার্কে স্যুইচ করতে ভুলবেন না (যাতে আমরা আসল টাকা নিয়ে কাজ না করি)। + +আপনি যদি Sepolia তালিকাভুক্ত না দেখেন, তাহলে মেনুতে যান, তারপর Advanced-এ যান এবং "Show test networks" চালু করতে নিচে স্ক্রোল করুন। নেটওয়ার্ক নির্বাচন মেনুতে, টেস্টনেটগুলির একটি তালিকা খুঁজে পেতে "Custom" ট্যাবটি বেছে নিন এবং "Sepolia" নির্বাচন করুন। + +![মেটামাস্ক সেপোলিয়া উদাহরণ](./metamask-sepolia-example.png) + +## ধাপ 4: একটি ফসেট থেকে ইথার যোগ করুন {#step-4} + +টেস্ট নেটওয়ার্কে আমাদের স্মার্ট কন্ট্র্যাক্ট স্থাপন করার জন্য, আমাদের কিছু নকল Eth লাগবে। Sepolia ETH পেতে আপনি বিভিন্ন ফসেটের একটি তালিকা দেখতে [Sepolia নেটওয়ার্কের বিবরণ](/developers/docs/networks/#sepolia)-এ যেতে পারেন। যদি একটি কাজ না করে, তবে অন্যটি চেষ্টা করুন কারণ সেগুলি মাঝে মাঝে খালি হয়ে যেতে পারে। নেটওয়ার্ক ট্রাফিকের কারণে আপনার নকল ETH পেতে কিছু সময় লাগতে পারে। কিছুক্ষণ পরেই আপনার Metamask অ্যাকাউন্টে ETH দেখতে পাবেন! + +## ধাপ 5: আপনার ব্যালেন্স পরীক্ষা করুন {#step-5} + +আমাদের ব্যালেন্স আছে কিনা তা দুবার পরীক্ষা করার জন্য, আসুন [Alchemy’s composer tool](https://sandbox.alchemy.com/?network=ETH_SEPOLIA&method=eth_getBalance&body.id=1&body.jsonrpc=2.0&body.method=eth_getBalance&body.params%5B0%5D=&body.params%5B1%5D=latest) ব্যবহার করে একটি [eth_getBalance](/developers/docs/apis/json-rpc/#eth_getbalance) অনুরোধ করি। এটি আমাদের ওয়ালেটে থাকা ETH-এর পরিমাণ ফেরত দেবে। আপনার MetaMask অ্যাকাউন্ট অ্যাড্রেস ইনপুট করার পরে এবং “অনুরোধ পাঠান”-এ ক্লিক করার পরে, আপনার এইরকম একটি প্রতিক্রিয়া দেখা উচিত: + +```json +{ "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" } +``` + +> **দ্রষ্টব্য:** এই ফলাফলটি ETH-এ নয়, wei-তে। Wei ইথারের ক্ষুদ্রতম একক হিসাবে ব্যবহৃত হয়। wei থেকে ETH-তে রূপান্তর হল: 1 eth = 1018 wei। সুতরাং যদি আমরা 0x2B5E3AF16B1880000 কে দশমিকে রূপান্তর করি তাহলে আমরা 5\*10¹⁸ পাই যা 5 ETH এর সমান। +> +> যাক বাবা! আমাদের নকল টাকা সব আছে । + +## ধাপ 6: আমাদের প্রজেক্ট শুরু করুন {#step-6} + +প্রথমে, আমাদের প্রজেক্টের জন্য একটি ফোল্ডার তৈরি করতে হবে। আপনার কমান্ড লাইনে যান এবং টাইপ করুন: + +``` +mkdir hello-world +cd hello-world +``` + +এখন যেহেতু আমরা আমাদের প্রজেক্ট ফোল্ডারের ভিতরে আছি, আমরা প্রজেক্টটি শুরু করতে `npm init` ব্যবহার করব। আপনার যদি আগে থেকেই npm ইনস্টল করা না থাকে, তাহলে [এই নির্দেশাবলী](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm) অনুসরণ করুন (আমাদের Node.js-ও লাগবে তাই সেটিও ডাউনলোড করুন!)। + +``` +npm init +``` + +ইনস্টলেশন প্রশ্নগুলির উত্তর আপনি কীভাবে দেন তা সত্যিই গুরুত্বপূর্ণ নয়, রেফারেন্সের জন্য আমরা কীভাবে এটি করেছি তা এখানে রয়েছে: + +``` +প্যাকেজের নাম: (hello-world) +ভার্সন: (1.0.0) +বিবরণ: হ্যালো ওয়ার্ল্ড স্মার্ট কন্ট্র্যাক্ট +এন্ট্রি পয়েন্ট: (index.js) +টেস্ট কমান্ড: +গিট রিপোজিটরি: +কীওয়ার্ড: +লেখক: +লাইসেন্স: (ISC) +/Users/.../.../.../hello-world/package.json এ লিখতে চলেছি: + +{ + "name": "hello-world", + "version": "1.0.0", + "description": "হ্যালো ওয়ার্ল্ড স্মার্ট কন্ট্র্যাক্ট", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} +``` + +package.json অনুমোদন করুন এবং আমরা এগিয়ে যাওয়ার জন্য প্রস্তুত! + +## ধাপ 7: [Hardhat](https://hardhat.org/getting-started/#overview) ডাউনলোড করুন {#step-7} + +Hardhat হল আপনার Ethereum সফ্টওয়্যার কম্পাইল, স্থাপন, পরীক্ষা এবং ডিবাগ করার জন্য একটি ডেভেলপমেন্ট পরিবেশ। এটি ডেভেলপারদের লাইভ চেইনে স্থাপন করার আগে স্থানীয়ভাবে স্মার্ট কন্ট্র্যাক্ট এবং ডিএ্যাপস তৈরি করতে সাহায্য করে। + +আমাদের `hello-world` প্রজেক্টের ভিতরে চালান: + +``` +npm install --save-dev hardhat +``` + +[ইনস্টলেশন নির্দেশাবলী](https://hardhat.org/getting-started/#overview) সম্পর্কে আরও বিস্তারিত জানতে এই পৃষ্ঠাটি দেখুন। + +## ধাপ 8: Hardhat প্রজেক্ট তৈরি করুন {#step-8} + +আমাদের প্রজেক্ট ফোল্ডারের ভিতরে চালান: + +``` +npx hardhat +``` + +তারপরে আপনার একটি স্বাগত বার্তা এবং আপনি কী করতে চান তা নির্বাচন করার একটি বিকল্প দেখতে পাওয়া উচিত। “একটি খালি hardhat.config.js তৈরি করুন” নির্বাচন করুন: + +``` +888 888 888 888 888 +888 888 888 888 888 +888 888 888 888 888 +8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888 +888 888 "88b 888P" d88" 888 888 "88b "88b 888 +888 888 .d888888 888 888 888 888 888 .d888888 888 +888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. +888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 + +👷 Hardhat v2.0.11-এ আপনাকে স্বাগতম 👷‍? + +আপনি কি করতে চান? … +একটি নমুনা প্রজেক্ট তৈরি করুন +❯ একটি খালি hardhat.config.js তৈরি করুন +প্রস্থান করুন +``` + +এটি আমাদের জন্য একটি `hardhat.config.js` ফাইল তৈরি করবে যেখানে আমরা আমাদের প্রজেক্টের জন্য সমস্ত সেট আপ নির্দিষ্ট করব (ধাপ 13-এ)। + +## ধাপ 9: প্রজেক্ট ফোল্ডার যোগ করুন {#step-9} + +আমাদের প্রজেক্টকে সংগঠিত রাখতে আমরা দুটি নতুন ফোল্ডার তৈরি করব। আপনার কমান্ড লাইনে আপনার প্রজেক্টের রুট ডিরেক্টরিতে নেভিগেট করুন এবং টাইপ করুন: + +``` +mkdir contracts +mkdir scripts +``` + +- `contracts/` হল যেখানে আমরা আমাদের হ্যালো ওয়ার্ল্ড স্মার্ট কন্ট্র্যাক্ট কোড ফাইল রাখব +- `scripts/` হল যেখানে আমরা আমাদের কন্ট্র্যাক্ট স্থাপন এবং ইন্টারঅ্যাক্ট করার জন্য স্ক্রিপ্ট রাখব + +## ধাপ 10: আমাদের কন্ট্র্যাক্ট লিখুন {#step-10} + +আপনি হয়তো নিজেকে জিজ্ঞাসা করছেন, আমরা কখন কোড লিখতে যাচ্ছি?? আচ্ছা, আমরা এখানে, ধাপ 10-এ। + +আপনার প্রিয় এডিটরে হ্যালো-ওয়ার্ল্ড প্রজেক্টটি খুলুন (আমরা [VSCode](https://code.visualstudio.com/) পছন্দ করি)। স্মার্ট কন্ট্র্যাক্টগুলি সলিডিটি নামক একটি ভাষায় লেখা হয় যা আমরা আমাদের HelloWorld.sol স্মার্ট কন্ট্র্যাক্ট লিখতে ব্যবহার করব।‌ + +1. "contracts" ফোল্ডারে যান এবং HelloWorld.sol নামে একটি নতুন ফাইল তৈরি করুন +2. নিচে Ethereum ফাউন্ডেশন থেকে একটি নমুনা Hello World স্মার্ট কন্ট্র্যাক্ট দেওয়া হলো যা আমরা এই টিউটোরিয়ালের জন্য ব্যবহার করব। নিচের বিষয়বস্তুগুলি আপনার HelloWorld.sol ফাইলে কপি এবং পেস্ট করুন, এবং এই কন্ট্র্যাক্টটি কী করে তা বোঝার জন্য মন্তব্যগুলি পড়তে ভুলবেন না: + +```solidity +// সলিডিটির ভার্সন নির্দিষ্ট করে, সেমান্টিক ভার্সনিং ব্যবহার করে। +// আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +pragma solidity ^0.7.0; + +// `HelloWorld` নামে একটি কন্ট্র্যাক্ট সংজ্ঞায়িত করে। +// একটি কন্ট্র্যাক্ট হল ফাংশন এবং ডেটার (তার স্টেট) একটি সংগ্রহ। একবার স্থাপন করা হলে, একটি কন্ট্র্যাক্ট Ethereum ব্লকচেইনের একটি নির্দিষ্ট অ্যাড্রেসে থাকে। আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +contract HelloWorld { + + // `string` টাইপের একটি স্টেট ভেরিয়েবল `message` ঘোষণা করে। + // স্টেট ভেরিয়েবল হল এমন ভেরিয়েবল যার মানগুলি স্থায়ীভাবে কন্ট্র্যাক্ট স্টোরেজে সংরক্ষিত থাকে। `public` কীওয়ার্ডটি ভেরিয়েবলগুলিকে কন্ট্র্যাক্টের বাইরে থেকে অ্যাক্সেসযোগ্য করে তোলে এবং একটি ফাংশন তৈরি করে যা অন্য কন্ট্র্যাক্ট বা ক্লায়েন্টরা মান অ্যাক্সেস করার জন্য কল করতে পারে। + string public message; + + // অনেক ক্লাস-ভিত্তিক অবজেক্ট-ওরিয়েন্টেড ভাষার মতো, একটি কনস্ট্রাক্টর হল একটি বিশেষ ফাংশন যা শুধুমাত্র কন্ট্র্যাক্ট তৈরির সময় কার্যকর করা হয়। + // কনস্ট্রাক্টরগুলি কন্ট্র্যাক্টের ডেটা শুরু করতে ব্যবহৃত হয়। আরও জানুন:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + constructor(string memory initMessage) { + + // একটি স্ট্রিং আর্গুমেন্ট `initMessage` গ্রহণ করে এবং মানটি কন্ট্র্যাক্টের `message` স্টোরেজ ভেরিয়েবলে সেট করে)। + message = initMessage; + } + + // একটি পাবলিক ফাংশন যা একটি স্ট্রিং আর্গুমেন্ট গ্রহণ করে এবং `message` স্টোরেজ ভেরিয়েবল আপডেট করে। + function update(string memory newMessage) public { + message = newMessage; + } +} +``` + +এটি একটি অত্যন্ত সহজ স্মার্ট কন্ট্র্যাক্ট যা তৈরির সময় একটি বার্তা সংরক্ষণ করে এবং `update` ফাংশন কল করে আপডেট করা যায়। + +## ধাপ 11: আপনার প্রজেক্টে MetaMask এবং Alchemy সংযোগ করুন {#step-11} + +আমরা একটি MetaMask ওয়ালেট, Alchemy অ্যাকাউন্ট তৈরি করেছি এবং আমাদের স্মার্ট কন্ট্র্যাক্ট লিখেছি, এখন এই তিনটি সংযোগ করার সময়। + +আপনার ভার্চুয়াল ওয়ালেট থেকে পাঠানো প্রতিটি লেনদেনের জন্য আপনার অনন্য ব্যক্তিগত কী ব্যবহার করে একটি স্বাক্ষরের প্রয়োজন। আমাদের প্রোগ্রামকে এই অনুমতি দেওয়ার জন্য, আমরা আমাদের ব্যক্তিগত কী (এবং Alchemy API কী) একটি এনভায়রনমেন্ট ফাইলে নিরাপদে সংরক্ষণ করতে পারি। + +> লেনদেন পাঠানো সম্পর্কে আরও জানতে, ওয়েব3 ব্যবহার করে লেনদেন পাঠানোর উপর [এই টিউটোরিয়ালটি](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) দেখুন। + +প্রথমে, আপনার প্রজেক্ট ডিরেক্টরিতে ডটএনভ প্যাকেজটি ইনস্টল করুন: + +``` +npm install dotenv --save +``` + +তারপর, আমাদের প্রজেক্টের রুট ডিরেক্টরিতে একটি `.env` ফাইল তৈরি করুন, এবং এতে আপনার MetaMask ব্যক্তিগত কী এবং HTTP Alchemy API URL যোগ করুন। + +- আপনার প্রাইভেট কী এক্সপোর্ট করতে [এই নির্দেশাবলী](https://support.metamask.io/configure/accounts/how-to-export-an-accounts-private-key/) অনুসরণ করুন +- HTTP Alchemy API URL পেতে নিচে দেখুন + +![alchemy এপিআই কি পান](./get-alchemy-api-key.png) + +Alchemy API URL কপি করুন + +আপনার `.env` ফাইলটি এইরকম দেখতে হবে: + +``` +API_URL = "https://eth-sepolia.g.alchemy.com/v2/আপনার-এপিআই-কি" +PRIVATE_KEY = "আপনার-মেটামাস্ক-প্রাইভেট-কি" +``` + +এগুলিকে আমাদের কোডের সাথে সংযোগ করতে, আমরা ধাপ 13-এ আমাদের `hardhat.config.js` ফাইলে এই ভেরিয়েবলগুলিকে রেফারেন্স করব। + + + + +.env কমিট করবেন না! অনুগ্রহ করে নিশ্চিত করুন যে আপনার .env ফাইলটি কারো সাথে শেয়ার বা প্রকাশ করবেন না, কারণ এটি করার মাধ্যমে আপনি আপনার গোপনীয়তাগুলির সাথে আপস করছেন। আপনি যদি সংস্করণ নিয়ন্ত্রণ ব্যবহার করেন, তাহলে আপনার .env একটি gitignore ফাইলে যোগ করুন। + + + + +## ধাপ 12: Ethers.js ইনস্টল করুন {#step-12-install-ethersjs} + +Ethers.js একটি লাইব্রেরি যা [স্ট্যান্ডার্ড JSON-RPC পদ্ধতিগুলিকে](/developers/docs/apis/json-rpc/) আরও ব্যবহারকারী-বান্ধব পদ্ধতির সাথে র‍্যাপ করে Ethereum-এর সাথে ইন্টারঅ্যাক্ট করা এবং অনুরোধ করা সহজ করে তোলে। + +Hardhat অতিরিক্ত টুলিং এবং বর্ধিত কার্যকারিতার জন্য [প্লাগইন](https://hardhat.org/plugins/) একীভূত করা খুব সহজ করে তোলে। আমরা কন্ট্র্যাক্ট স্থাপনার জন্য [Ethers প্লাগইন](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers) এর সুবিধা নেব ([Ethers.js](https://github.com/ethers-io/ethers.js/)-এর কিছু খুব পরিষ্কার কন্ট্র্যাক্ট স্থাপন পদ্ধতি রয়েছে)। + +আপনার প্রজেক্ট ডিরেক্টরিতে টাইপ করুন: + +``` +npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0" +``` + +আমরা পরবর্তী ধাপে আমাদের `hardhat.config.js`-এ ethers এর প্রয়োজনীয়তাও যোগ করব। + +## ধাপ 13: hardhat.config.js আপডেট করুন {#step-13-update-hardhatconfigjs} + +আমরা এখন পর্যন্ত বেশ কিছু নির্ভরতা এবং প্লাগইন যোগ করেছি, এখন আমাদের `hardhat.config.js` আপডেট করতে হবে যাতে আমাদের প্রজেক্ট তাদের সকলের সম্পর্কে জানতে পারে। + +আপনার `hardhat.config.js` আপডেট করে এইরকম করুন: + +``` +require('dotenv').config(); + +require("@nomiclabs/hardhat-ethers"); +const { API_URL, PRIVATE_KEY } = process.env; + +/** +* @type import('hardhat/config').HardhatUserConfig +*/ +module.exports = { + solidity: "0.7.3", + defaultNetwork: "sepolia", + networks: { + hardhat: {}, + sepolia: { + url: API_URL, + accounts: [`0x${PRIVATE_KEY}`] + } + }, +} +``` + +## ধাপ 14: আমাদের কন্ট্র্যাক্ট কম্পাইল করুন {#step-14-compile-our-contracts} + +সবকিছু ঠিকঠাক কাজ করছে কিনা তা নিশ্চিত করতে, আসুন আমাদের কন্ট্র্যাক্ট কম্পাইল করি। `compile` টাস্কটি অন্তর্নির্মিত হার্ডহ্যাট টাস্কগুলির মধ্যে একটি। + +কমান্ড লাইন থেকে রান করুন: + +``` +npx hardhat compile +``` + +আপনি `SPDX license identifier not provided in source file` সম্পর্কে একটি সতর্কবার্তা পেতে পারেন, কিন্তু সে সম্পর্কে চিন্তা করার দরকার নেই — আশা করি বাকি সব ঠিক আছে! যদি না হয়, আপনি সবসময় [Alchemy ডিসকর্ড](https://discord.gg/u72VCg3)-এ বার্তা দিতে পারেন। + +## ধাপ 15: আমাদের ডিপ্লয় স্ক্রিপ্ট লিখুন {#step-15-write-our-deploy-scripts} + +এখন যেহেতু আমাদের কন্ট্র্যাক্ট লেখা হয়ে গেছে এবং আমাদের কনফিগারেশন ফাইল প্রস্তুত, এখন আমাদের কন্ট্র্যাক্ট ডেপ্লয় স্ক্রিপ্ট লেখার সময়। + +`scripts/` ফোল্ডারে যান এবং `deploy.js` নামে একটি নতুন ফাইল তৈরি করুন, এতে নিম্নলিখিত বিষয়বস্তু যোগ করুন: + +``` +async function main() { + const HelloWorld = await ethers.getContractFactory("HelloWorld"); + + // ডিপ্লয়মেন্ট শুরু করুন, একটি প্রতিশ্রুতি প্রদান করে যা একটি কন্ট্র্যাক্ট অবজেক্টে সমাধান করে + const hello_world = await HelloWorld.deploy("Hello World!"); + console.log("কন্ট্র্যাক্টটি এই ঠিকানায় স্থাপন করা হয়েছে:", hello_world.address);} + +main() + .then(() => process.exit(0)) + .catch(error => { + console.error(error); + process.exit(1); + }); +``` + +Hardhat তাদের [কন্ট্র্যাক্টস টিউটোরিয়াল](https://hardhat.org/tutorial/testing-contracts.html#writing-tests)-এ এই কোডের প্রতিটি লাইন কী করে তা চমৎকারভাবে ব্যাখ্যা করেছে, আমরা এখানে তাদের ব্যাখ্যাগুলি গ্রহণ করেছি। + +``` +const HelloWorld = await ethers.getContractFactory("HelloWorld"); +``` + +ethers.js-এ একটি `ContractFactory` হল একটি অ্যাবস্ট্রাকশন যা নতুন স্মার্ট কন্ট্র্যাক্ট স্থাপন করতে ব্যবহৃত হয়, তাই এখানে `HelloWorld` হল আমাদের হ্যালো ওয়ার্ল্ড কন্ট্র্যাক্টের ইনস্ট্যান্সের জন্য একটি ফ্যাক্টরি। `hardhat-ethers` প্লাগইন ব্যবহার করার সময় `ContractFactory` এবং `Contract` ইনস্ট্যান্সগুলি ডিফল্টরূপে প্রথম সাইনারের সাথে সংযুক্ত থাকে। + +``` +const hello_world = await HelloWorld.deploy(); +``` + +একটি `ContractFactory`-এ `deploy()` কল করা ডিপ্লয়মেন্ট শুরু করবে, এবং একটি `Promise` প্রদান করবে যা একটি `Contract`-এ সমাধান করে। এটি সেই অবজেক্ট যার আমাদের প্রতিটি স্মার্ট কন্ট্র্যাক্ট ফাংশনের জন্য একটি পদ্ধতি রয়েছে। + +## ধাপ 16: আমাদের কন্ট্র্যাক্ট স্থাপন করুন {#step-16-deploy-our-contract} + +আমরা অবশেষে আমাদের স্মার্ট কন্ট্র্যাক্ট স্থাপন করার জন্য প্রস্তুত! কমান্ড লাইনে যান এবং চালান: + +``` +npx hardhat run scripts/deploy.js --network sepolia +``` + +তারপরে আপনার এইরকম কিছু দেখা উচিত: + +``` +কন্ট্র্যাক্টটি এই ঠিকানায় স্থাপন করা হয়েছে: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 +``` + +যদি আমরা [Sepolia etherscan](https://sepolia.etherscan.io/) এ যাই এবং আমাদের কন্ট্র্যাক্টের ঠিকানা অনুসন্ধান করি তবে আমরা দেখতে পাব যে এটি সফলভাবে স্থাপন করা হয়েছে। লেনদেনটি এইরকম কিছু দেখাবে: + +![ইথারস্ক্যান কন্ট্র্যাক্ট](./etherscan-contract.png) + +`From` ঠিকানাটি আপনার MetaMask অ্যাকাউন্টের ঠিকানার সাথে মিলবে এবং To ঠিকানায় “Contract Creation” লেখা থাকবে কিন্তু যদি আমরা লেনদেনে ক্লিক করি তবে আমরা `To` ক্ষেত্রে আমাদের কন্ট্র্যাক্টের ঠিকানা দেখতে পাব: + +![ইথারস্ক্যান লেনদেন](./etherscan-transaction.png) + +অভিনন্দন! আপনি এইমাত্র Ethereum চেইনে একটি স্মার্ট কন্ট্র্যাক্ট স্থাপন করেছেন 🎉 + +নেপথ্যে কী ঘটছে তা বোঝার জন্য, আসুন আমাদের [Alchemy ড্যাশবোর্ড](https://dashboard.alchemyapi.io/explorer)-এর এক্সপ্লোরার ট্যাবে নেভিগেট করি। আপনার যদি একাধিক Alchemy অ্যাপ থাকে তবে অ্যাপ দ্বারা ফিল্টার করতে এবং “Hello World” নির্বাচন করতে ভুলবেন না। +![হ্যালো ওয়ার্ল্ড এক্সপ্লোরার](./hello-world-explorer.png) + +এখানে আপনি কয়েকটি JSON-RPC কল দেখতে পাবেন যা Hardhat/Ethers আমাদের জন্য হুডের নিচে তৈরি করেছে যখন আমরা `.deploy()` ফাংশনটি কল করেছি। এখানে দুটি গুরুত্বপূর্ণ বিষয় হল [`eth_sendRawTransaction`](https://www.alchemy.com/docs/node/abstract/abstract-api-endpoints/eth-send-raw-transaction), যা আমাদের কন্ট্র্যাক্টকে Sepolia চেইনে লেখার অনুরোধ, এবং [`eth_getTransactionByHash`](https://www.alchemy.com/docs/node/abstract/abstract-api-endpoints/eth-get-transaction-by-hash) যা হ্যাস দেওয়া হলে আমাদের লেনদেন সম্পর্কে তথ্য পড়ার অনুরোধ (লেনদেনের সময় একটি সাধারণ প্যাটার্ন)। লেনদেন পাঠানো সম্পর্কে আরও জানতে, [Web3 ব্যবহার করে লেনদেন পাঠানো](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) এই টিউটোরিয়ালটি দেখুন। + +এই টিউটোরিয়ালের পার্ট 1 এর জন্য এটুকুই, পার্ট 2-এ আমরা আমাদের প্রাথমিক বার্তা আপডেট করে আমাদের [স্মার্ট কন্ট্র্যাক্টের সাথে ইন্টারঅ্যাক্ট করব](https://www.alchemy.com/docs/interacting-with-a-smart-contract), এবং পার্ট 3-এ আমরা [আমাদের স্মার্ট কন্ট্র্যাক্ট Etherscan-এ প্রকাশ করব](https://www.alchemy.com/docs/submitting-your-smart-contract-to-etherscan) যাতে সবাই জানতে পারে কিভাবে এর সাথে ইন্টারঅ্যাক্ট করতে হয়। + +**Alchemy সম্পর্কে আরও জানতে চান? আমাদের [ওয়েবসাইট](https://www.alchemy.com/eth) দেখুন। কখনো কোনো আপডেট মিস করতে চান না? [এখানে](https://www.alchemy.com/newsletter) আমাদের নিউজলেটারে সাবস্ক্রাইব করুন! আমাদের [Discord](https://discord.gg/u72VCg3)-এও যোগ দিতে ভুলবেন না।**. diff --git a/public/content/translations/bn/developers/tutorials/how-to-implement-an-erc721-market/index.md b/public/content/translations/bn/developers/tutorials/how-to-implement-an-erc721-market/index.md new file mode 100644 index 00000000000..4afbaf5d6b6 --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/how-to-implement-an-erc721-market/index.md @@ -0,0 +1,145 @@ +--- +title: "কিভাবে একটি ERC-721 বাজার প্রয়োগ করবেন" +description: "কিভাবে একটি বিকেন্দ্রীভূত ক্লাসিফায়েড বোর্ডে টোকেনাইজড আইটেম বিক্রির জন্য রাখবেন" +author: "Alberto Cuesta Cañada" +tags: [ "স্মার্ট কন্ট্র্যাক্ট", "erc-721", "সলিডিটি", "টোকেন" ] +skill: intermediate +lang: bn +published: 2020-03-19 +source: Hackernoon +sourceUrl: https://hackernoon.com/how-to-implement-an-erc721-market-1e1a32j9 +--- + +এই আর্টিকেলে, আমি আপনাকে দেখাতে যাচ্ছি কিভাবে Ethereum ব্লকচেইনের জন্য Craigslist কোড করতে হয়। + +Gumtree, Ebay এবং Craigslist-এর আগে, ক্লাসিফায়েড বোর্ডগুলি বেশিরভাগই কর্ক বা কাগজ দিয়ে তৈরি ছিল। স্কুলের করিডোরে, সংবাদপত্রে, রাস্তার আলোতে, দোকানের সামনে ক্লাসিফায়েড বোর্ড ছিল। + +ইন্টারনেটের সাথে সাথে এই সবকিছু বদলে গেছে। একটি নির্দিষ্ট ক্লাসিফায়েড বোর্ড দেখতে পারা মানুষের সংখ্যা বহুগুণে বেড়ে গেছে। এর সাথে, তারা যে বাজারগুলির প্রতিনিধিত্ব করে তা অনেক বেশি দক্ষ হয়ে ওঠে এবং বিশ্বব্যাপী আকারে স্কেল করা হয়েছিল। Ebay একটি বিশাল ব্যবসা যার উৎপত্তি এই শারীরিক ক্লাসিফায়েড বোর্ডগুলি থেকে হয়েছে। + +ব্লকচেইনের সাথে এই বাজারগুলি আরও একবার পরিবর্তনের জন্য প্রস্তুত, আমি আপনাকে দেখাই কিভাবে। + +## নগদীকরণ {#monetization} + +একটি পাবলিক ব্লকচেইন ক্লাসিফায়েড বোর্ডের ব্যবসায়িক মডেল Ebay এবং কোম্পানির থেকে ভিন্ন হতে হবে। + +প্রথমত, এখানে [বিকেন্দ্রীকরণের দৃষ্টিকোণ](/developers/docs/web2-vs-web3/) রয়েছে। বিদ্যমান প্ল্যাটফর্মগুলিকে তাদের নিজস্ব সার্ভার বজায় রাখতে হবে। একটি বিকেন্দ্রীভূত প্ল্যাটফর্ম তার ব্যবহারকারীদের দ্বারা রক্ষণাবেক্ষণ করা হয়, তাই প্ল্যাটফর্মের মালিকের জন্য মূল প্ল্যাটফর্মটি চালানোর খরচ শূন্যে নেমে আসে। + +তারপর রয়েছে ফ্রন্ট এন্ড, ওয়েবসাইট বা ইন্টারফেস যা প্ল্যাটফর্মে অ্যাক্সেস দেয়। এখানে অনেক বিকল্প আছে। প্ল্যাটফর্মের মালিকরা অ্যাক্সেস সীমাবদ্ধ করতে পারে এবং প্রত্যেককে তাদের ইন্টারফেস ব্যবহার করতে বাধ্য করতে পারে, একটি ফি চার্জ করে। প্ল্যাটফর্মের মালিকরা অ্যাক্সেস খোলার সিদ্ধান্তও নিতে পারে (জনগণের হাতে ক্ষমতা!) এবং যে কাউকে প্ল্যাটফর্মে ইন্টারফেস তৈরি করতে দিন। অথবা মালিকরা সেই চরম পর্যায়গুলোর মাঝখানে যেকোনো পদ্ধতির সিদ্ধান্ত নিতে পারেন। + +_আমার চেয়ে বেশি দূরদৃষ্টিসম্পন্ন ব্যবসায়িক নেতারা জানবেন কিভাবে এটিকে নগদীকরণ করা যায়। আমি শুধু দেখছি যে এটি স্থিতাবস্থা থেকে আলাদা এবং সম্ভবত লাভজনক।_ + +উপরন্তু, এখানে অটোমেশন এবং পেমেন্টের দৃষ্টিকোণ রয়েছে। কিছু জিনিস খুব [কার্যকরভাবে টোকেনাইজড](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com) করা যেতে পারে এবং একটি ক্লাসিফায়েড বোর্ডে ট্রেড করা যেতে পারে। টোকেনাইজড অ্যাসেটগুলি একটি ব্লকচেইনে সহজেই স্থানান্তর করা যায়। অত্যন্ত জটিল পেমেন্ট পদ্ধতি একটি ব্লকচেইনে সহজেই প্রয়োগ করা যেতে পারে। + +আমি এখানে শুধু একটি ব্যবসায়িক সুযোগের গন্ধ পাচ্ছি। কোনো চলমান খরচ ছাড়াই একটি ক্লাসিফায়েড বোর্ড সহজেই প্রয়োগ করা যেতে পারে, প্রতিটি লেনদেনে অন্তর্ভুক্ত জটিল পেমেন্ট পাথ সহ। আমি নিশ্চিত যে কেউ এটি কিসের জন্য ব্যবহার করতে হবে সে সম্পর্কে একটি ধারণা নিয়ে আসবে। + +আমি শুধু এটি তৈরি করে খুশি। চলুন কোডটি একবার দেখে নেওয়া যাক। + +## বাস্তবায়ন {#implementation} + +কিছু সময় আগে আমরা ব্যবসায়িক ক্ষেত্রের উদাহরণ বাস্তবায়ন এবং অন্যান্য ভাল জিনিস সহ একটি [ওপেন সোর্স রিপোজিটরি](https://github.com/HQ20/contracts?ref=hackernoon.com) শুরু করেছি, অনুগ্রহ করে একবার দেখুন। + +এই [Ethereum ক্লাসিফায়েড বোর্ড](https://github.com/HQ20/contracts/tree/master/contracts/classifieds?ref=hackernoon.com) এর কোডটি সেখানে আছে, দয়া করে এটি ব্যবহার করুন এবং যথেচ্ছভাবে ব্যবহার করুন। শুধু সচেতন থাকুন যে কোডটি অডিট করা হয়নি এবং এতে টাকা লাগানোর আগে আপনাকে নিজের যথাযথ সতর্কতা অবলম্বন করতে হবে। + +বোর্ডের মূল বিষয়গুলি জটিল নয়। বোর্ডের সমস্ত বিজ্ঞাপনগুলি কয়েকটি ফিল্ড সহ একটি স্ট্রাকট হবে: + +```solidity +struct Trade { + address poster; + uint256 item; + uint256 price; + bytes32 status; // খোলা, কার্যকর, বাতিল +} +``` + +সুতরাং বিজ্ঞাপন পোস্ট করার জন্য কেউ একজন আছেন। বিক্রির জন্য একটি আইটেম। আইটেমটির জন্য একটি মূল্য। ট্রেডের স্ট্যাটাস যা খোলা, কার্যকর বা বাতিল হতে পারে। + +এই সমস্ত ট্রেড একটি ম্যাপিংয়ে রাখা হবে। কারণ Solidity-তে সবকিছুই একটি ম্যাপিং বলে মনে হয়। এছাড়াও কারণ এটি সুবিধাজনক। + +```solidity +mapping(uint256 => Trade) public trades; +``` + +একটি ম্যাপিং ব্যবহার করার অর্থ হল, এটি পোস্ট করার আগে আমাদের প্রতিটি বিজ্ঞাপনের জন্য একটি আইডি তৈরি করতে হবে এবং এটিতে কাজ করার আগে আমাদের একটি বিজ্ঞাপনের আইডি জানতে হবে। স্মার্ট কন্ট্র্যাক্ট বা ফ্রন্ট-এন্ডে এর সাথে মোকাবিলা করার একাধিক উপায় রয়েছে। আপনার যদি কিছু দিকনির্দেশনার প্রয়োজন হয় তবে জিজ্ঞাসা করুন। + +এরপরে প্রশ্ন আসে যে আমরা যে আইটেমগুলি নিয়ে কাজ করি সেগুলি কী, এবং লেনদেনের জন্য অর্থ প্রদানের জন্য ব্যবহৃত এই মুদ্রাটি কী। + +আইটেমগুলির জন্য, আমরা কেবল জিজ্ঞাসা করতে যাচ্ছি যে তারা [ERC-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721.sol?ref=hackernoon.com) ইন্টারফেসটি প্রয়োগ করে, যা প্রকৃতপক্ষে ব্লকচেইনে বাস্তব বিশ্বের আইটেমগুলিকে উপস্থাপন করার একটি উপায়, যদিও এটি [ডিজিটাল অ্যাসেটগুলির সাথে সবচেয়ে ভাল কাজ করে](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com)। আমরা কনস্ট্রাক্টরে আমাদের নিজস্ব ERC721 কন্ট্র্যাক্ট নির্দিষ্ট করতে যাচ্ছি, যার অর্থ আমাদের ক্লাসিফায়েড বোর্ডের যেকোনো অ্যাসেটকে আগে থেকে টোকেনাইজড করতে হবে। + +পেমেন্টের জন্য, আমরা একই ধরনের কিছু করতে যাচ্ছি। বেশিরভাগ ব্লকচেইন প্রকল্প তাদের নিজস্ব [ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol?ref=hackernoon.com) ক্রিপটোকারেন্সি সংজ্ঞায়িত করে। অন্য কেউ কেউ DAI-এর মতো একটি মূলধারার মুদ্রা ব্যবহার করতে পছন্দ করেন। এই ক্লাসিফায়েড বোর্ডে, আপনার মুদ্রা কী হবে তা নির্মাণের সময় আপনাকে সিদ্ধান্ত নিতে হবে। সহজ। + +```solidity +constructor ( + address _currencyTokenAddress, address _itemTokenAddress +) public { + currencyToken = IERC20(_currencyTokenAddress); + itemToken = IERC721(_itemTokenAddress); + tradeCounter = 0; +} +``` + +আমরা প্রায় সেখানে পৌঁছে গেছি। আমরা বিজ্ঞাপন, ট্রেডের জন্য আইটেম এবং পেমেন্টের জন্য একটি মুদ্রা পেয়েছি। একটি বিজ্ঞাপন তৈরি করার অর্থ হল একটি আইটেম এসক্রোতে রাখা, এটি দেখানোর জন্য যে এটি আপনার কাছে আছে এবং আপনি এটি দুবার পোস্ট করেননি, সম্ভবত একটি ভিন্ন বোর্ডে। + +নীচের কোডটি ঠিক তাই করে। আইটেমটি এসক্রোতে রাখে, বিজ্ঞাপন তৈরি করে, কিছু হাউসকিপিং করে। + +```solidity +function openTrade(uint256 _item, uint256 _price) + public +{ + itemToken.transferFrom(msg.sender, address(this), _item); + trades[tradeCounter] = Trade({ + poster: msg.sender, + item: _item, + price: _price, + status: "Open" + }); + tradeCounter += 1; + emit TradeStatusChange(tradeCounter - 1, "Open"); +} +``` + +ট্রেডটি গ্রহণ করার অর্থ হল একটি বিজ্ঞাপন (ট্রেড) বেছে নেওয়া, মূল্য পরিশোধ করা, আইটেমটি গ্রহণ করা। নীচের কোডটি একটি ট্রেড পুনরুদ্ধার করে। এটি উপলব্ধ কিনা তা পরীক্ষা করে। আইটেমটির জন্য অর্থ প্রদান করে। আইটেমটি পুনরুদ্ধার করে। বিজ্ঞাপন আপডেট করে। + +```solidity +function executeTrade(uint256 _trade) + public +{ + Trade memory trade = trades[_trade]; + require(trade.status == "Open", "ট্রেডটি খোলা নেই।"); + currencyToken.transferFrom(msg.sender, trade.poster, trade.price); + itemToken.transferFrom(address(this), msg.sender, trade.item); + trades[_trade].status = "Executed"; + emit TradeStatusChange(_trade, "Executed"); +} +``` + +অবশেষে, আমাদের কাছে বিক্রেতাদের জন্য একটি বিকল্প রয়েছে ক্রেতা গ্রহণ করার আগে একটি ট্রেড থেকে সরে আসার। কিছু মডেলে, বিজ্ঞাপনগুলি মেয়াদ শেষ হওয়ার আগে কিছু সময়ের জন্য লাইভ থাকে। আপনার বাজারের ডিজাইনের উপর নির্ভর করে এটি আপনার পছন্দ। + +কোডটি একটি ট্রেড কার্যকর করার জন্য ব্যবহৃত কোডের সাথে খুব সাদৃশ্যপূর্ণ, শুধুমাত্র এখানে কোনো মুদ্রা হাতবদল হয় না এবং আইটেমটি বিজ্ঞাপন পোস্টারের কাছে ফিরে যায়। + +```solidity +function cancelTrade(uint256 _trade) + public +{ + Trade memory trade = trades[_trade]; + require( + msg.sender == trade.poster, + "ট্রেড শুধুমাত্র পোস্টার দ্বারা বাতিল করা যেতে পারে।" + ); + require(trade.status == "Open", "ট্রেডটি খোলা নেই।"); + itemToken.transferFrom(address(this), trade.poster, trade.item); + trades[_trade].status = "Cancelled"; + emit TradeStatusChange(_trade, "Cancelled"); +} +``` + +এই পর্যন্তই। আপনি বাস্তবায়নের শেষ পর্যন্ত পৌঁছেছেন। কোডে প্রকাশ করা হলে কিছু ব্যবসায়িক ধারণা কতটা সংক্ষিপ্ত হয় তা বেশ আশ্চর্যজনক, এবং এটি সেইসব ক্ষেত্রের মধ্যে একটি। সম্পূর্ণ কন্ট্র্যাক্টটি [আমাদের রেপোতে](https://github.com/HQ20/contracts/blob/master/contracts/classifieds/Classifieds.sol) দেখুন। + +## উপসংহার {#conclusion} + +ক্লাসিফায়েড বোর্ডগুলি একটি সাধারণ বাজার কনফিগারেশন যা ইন্টারনেটের সাথে ব্যাপকভাবে প্রসারিত হয়েছে, এবং কিছু একচেটিয়া বিজয়ীর সাথে এটি একটি অত্যন্ত জনপ্রিয় ব্যবসায়িক মডেল হয়ে উঠেছে। + +ক্লাসিফায়েড বোর্ডগুলি ব্লকচেইন পরিবেশে নকল করার জন্য একটি সহজ টুল, যার কিছু নির্দিষ্ট বৈশিষ্ট্য রয়েছে যা বিদ্যমান জায়ান্টদের জন্য একটি চ্যালেঞ্জ তৈরি করা সম্ভব করবে। + +এই আর্টিকেলে, আমি একটি ক্লাসিফায়েড বোর্ড ব্যবসার ব্যবসায়িক বাস্তবতা এবং প্রযুক্তিগত বাস্তবায়নের মধ্যে একটি সেতুবন্ধন করার চেষ্টা করেছি। আপনার যদি সঠিক দক্ষতা থাকে তবে এই জ্ঞান আপনাকে বাস্তবায়নের জন্য একটি দৃষ্টিভঙ্গি এবং একটি রোডম্যাপ তৈরি করতে সাহায্য করবে। + +বরাবরের মতো, আপনি যদি মজাদার কিছু তৈরি করতে চান এবং কিছু পরামর্শ চান, তাহলে অনুগ্রহ করে [আমার সাথে যোগাযোগ করুন](https://albertocuesta.es/)! আমি সাহায্য করতে সবসময় খুশি। diff --git a/public/content/translations/bn/developers/tutorials/how-to-mint-an-nft/index.md b/public/content/translations/bn/developers/tutorials/how-to-mint-an-nft/index.md new file mode 100644 index 00000000000..ddf3326fa85 --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/how-to-mint-an-nft/index.md @@ -0,0 +1,335 @@ +--- +title: "কীভাবে একটি NFT মিন্ট করবেন (NFT টিউটোরিয়াল সিরিজের পার্ট 2/3)" +description: "এই টিউটোরিয়ালে আমাদের স্মার্ট কন্ট্র্যাক্ট এবং Web3 ব্যবহার করে Ethereum ব্লকচেইনে কীভাবে একটি NFT মিন্ট করতে হয় তা বর্ণনা করা হয়েছে।" +author: "Sumi Mudgil" +tags: + [ + "ERC-721", + "alchemy", + "সলিডিটি", + "স্মার্ট কন্ট্র্যাক্ট" + ] +skill: beginner +lang: bn +published: 2021-04-22 +--- + +[Beeple](https://www.nytimes.com/2021/03/11/arts/design/nft-auction-christies-beeple.html): $69 মিলিয়ন +[3LAU](https://www.forbes.com/sites/abrambrown/2021/03/03/3lau-nft-nonfungible-tokens-justin-blau/?sh=5f72ef64643b): $11 মিলিয়ন +[Grimes](https://www.theguardian.com/music/2021/mar/02/grimes-sells-digital-art-collection-non-fungible-tokens): $6 মিলিয়ন + +তারা সবাই Alchemy-এর শক্তিশালী API ব্যবহার করে তাদের NFT মিন্ট করেছে। এই টিউটোরিয়ালে, আমরা আপনাকে \<10 মিনিটের মধ্যে একই কাজটি কীভাবে করতে হয় তা শেখাব। + +“একটি NFT মিন্ট করা” হল ব্লকচেইনে আপনার ERC-721 টোকেনের একটি অনন্য ইনস্ট্যান্স প্রকাশ করার কাজ। [এই NFT টিউটোরিয়াল সিরিজের পার্ট 1](/developers/tutorials/how-to-write-and-deploy-an-nft/) থেকে আমাদের স্মার্ট কন্ট্র্যাক্ট ব্যবহার করে, আসুন আমাদের Web3 দক্ষতা দিয়ে একটি NFT মিন্ট করি। এই টিউটোরিয়াল শেষে, আপনি আপনার মন (এবং ওয়ালেট) যতগুলো চায় ততগুলো NFT মিন্ট করতে পারবেন! + +চলুন শুরু করা যাক! + +## ধাপ 1: Web3 ইনস্টল করুন {#install-web3} + +আপনি যদি আপনার NFT স্মার্ট কন্ট্র্যাক্ট তৈরির প্রথম টিউটোরিয়ালটি অনুসরণ করে থাকেন, তাহলে আপনার ইতিমধ্যেই Ethers.js ব্যবহারের অভিজ্ঞতা আছে। Web3 হল Ethers-এর মতোই, কারণ এটি একটি লাইব্রেরি যা Ethereum ব্লকচেইনে রিকোয়েস্ট তৈরি করা সহজ করতে ব্যবহৃত হয়। এই টিউটোরিয়ালে আমরা [Alchemy Web3](https://docs.alchemyapi.io/alchemy/documentation/alchemy-web3) ব্যবহার করব, যা একটি উন্নত Web3 লাইব্রেরি যা অটোম্যাটিক পুনরায় চেষ্টা এবং শক্তিশালী WebSocket সাপোর্ট প্রদান করে। + +আপনার প্রজেক্টের হোম ডিরেক্টরিতে রান করুন: + +``` +npm install @alch/alchemy-web3 +``` + +## ধাপ 2: একটি `mint-nft.js` ফাইল তৈরি করুন {#create-mintnftjs} + +আপনার স্ক্রিপ্ট ডিরেক্টরির ভিতরে, একটি `mint-nft.js` ফাইল তৈরি করুন এবং কোডের নিম্নলিখিত লাইনগুলি যোগ করুন: + +```js +require("dotenv").config() +const API_URL = process.env.API_URL +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(API_URL) +``` + +## ধাপ 3: আপনার কন্ট্র্যাক্টের ABI নিন {#contract-abi} + +আমাদের কন্ট্র্যাক্টের ABI (অ্যাপ্লিকেশন বাইনারি ইন্টারফেস) হল আমাদের স্মার্ট কন্ট্র্যাক্টের সাথে ইন্টারঅ্যাক্ট করার ইন্টারফেস। আপনি [এখানে](https://docs.alchemyapi.io/alchemy/guides/eth_getlogs#what-are-ab-is) কন্ট্র্যাক্ট ABI সম্পর্কে আরও জানতে পারেন। Hardhat স্বয়ংক্রিয়ভাবে আমাদের জন্য একটি ABI তৈরি করে এবং `MyNFT.json` ফাইলে সেভ করে। এটি ব্যবহার করার জন্য আমাদের `mint-nft.js` ফাইলে নিম্নলিখিত কোডের লাইনগুলি যোগ করে বিষয়বস্তু পার্স করতে হবে: + +```js +const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json") +``` + +আপনি যদি ABI দেখতে চান তবে আপনি এটি আপনার কনসোলে প্রিন্ট করতে পারেন: + +```js +console.log(JSON.stringify(contract.abi)) +``` + +`mint-nft.js` রান করতে এবং আপনার ABI কনসোলে প্রিন্ট করা দেখতে, আপনার টার্মিনালে নেভিগেট করুন এবং রান করুন: + +```js +node scripts/mint-nft.js +``` + +## ধাপ 4: IPFS ব্যবহার করে আপনার NFT-এর জন্য মেটাডেটা কনফিগার করুন {#config-meta} + +আপনার যদি পার্ট 1-এ আমাদের টিউটোরিয়াল থেকে মনে থাকে, আমাদের `mintNFT` স্মার্ট কন্ট্র্যাক্ট ফাংশন একটি tokenURI প্যারামিটার নেয় যা NFT-এর মেটাডেটা বর্ণনাকারী একটি JSON ডকুমেন্টে রিসলভ হওয়া উচিত — যা সত্যিই NFT-কে জীবন্ত করে তোলে, এটিকে কনফিগারযোগ্য বৈশিষ্ট্য, যেমন নাম, বর্ণনা, ছবি এবং অন্যান্য অ্যাট্রিবিউট পেতে দেয়। + +> _ইন্টারপ্ল্যানেটারি ফাইল সিস্টেম (IPFS) হল একটি ডিস্ট্রিবিউটেড ফাইল সিস্টেমে ডেটা সংরক্ষণ এবং শেয়ার করার জন্য একটি ডিসেন্ট্রালাইজড প্রোটোকল এবং পিয়ার-টু-পিয়ার নেটওয়ার্ক।_ + +আমরা Pinata ব্যবহার করব, একটি সুবিধাজনক IPFS API এবং টুলকিট, আমাদের NFT অ্যাসেট এবং মেটাডেটা সংরক্ষণ করার জন্য যাতে আমাদের NFT সত্যিই ডিসেন্ট্রালাইজড হয়। আপনার যদি Pinata অ্যাকাউন্ট না থাকে, তাহলে [এখানে](https://app.pinata.cloud) একটি বিনামূল্যের অ্যাকাউন্টের জন্য সাইন আপ করুন এবং আপনার ইমেল যাচাই করার পদক্ষেপগুলি সম্পূর্ণ করুন। + +আপনি একটি অ্যাকাউন্ট তৈরি করার পরে: + +- “Files” পেজে নেভিগেট করুন এবং পেজের উপরের বাম দিকে নীল "Upload" বোতামে ক্লিক করুন। + +- Pinata-তে একটি ছবি আপলোড করুন — এটি আপনার NFT-এর জন্য ছবির অ্যাসেট হবে। আপনার ইচ্ছামত অ্যাসেটের নাম দিন + +- আপলোড করার পরে, আপনি "Files" পেজের টেবিলে ফাইলের তথ্য দেখতে পাবেন। আপনি একটি CID কলামও দেখতে পাবেন। আপনি এটির পাশের কপি বোতামে ক্লিক করে CID কপি করতে পারেন। আপনি আপনার আপলোড দেখতে পারেন এখানে: `https://gateway.pinata.cloud/ipfs/`। উদাহরণস্বরূপ, আমরা IPFS-এ যে ছবিটি ব্যবহার করেছি তা [এখানে](https://gateway.pinata.cloud/ipfs/QmZdd5KYdCFApWn7eTZJ1qgJu18urJrP9Yh1TZcZrZxxB5) পেতে পারেন। + +যারা দেখে শিখতে বেশি পছন্দ করে, তাদের জন্য উপরের পদক্ষেপগুলি এখানে সংক্ষিপ্ত করা হয়েছে: + +![কীভাবে Pinata-তে আপনার ছবি আপলোড করবেন](./instructionsPinata.gif) + +এখন, আমরা Pinata-তে আরও একটি ডকুমেন্ট আপলোড করতে চাই। কিন্তু তা করার আগে, আমাদের এটি তৈরি করতে হবে! + +আপনার রুট ডিরেক্টরিতে, `nft-metadata.json` নামে একটি নতুন ফাইল তৈরি করুন এবং নিম্নলিখিত json কোড যোগ করুন: + +```json +{ + "attributes": [ + { + "trait_type": "Breed", + "value": "Maltipoo" + }, + { + "trait_type": "Eye color", + "value": "Mocha" + } + ], + "description": "বিশ্বের সবচেয়ে আদুরে এবং সংবেদনশীল কুকুরছানা।", + "image": "ipfs://QmWmvTJmJU3pozR9ZHFmQC2DNDwi2XJtf3QGyYiiagFSWb", + "name": "Ramses" +} +``` + +json-এর ডেটা পরিবর্তন করতে দ্বিধা করবেন না। আপনি অ্যাট্রিবিউট বিভাগ থেকে সরাতে বা যোগ করতে পারেন। সবচেয়ে গুরুত্বপূর্ণ, নিশ্চিত করুন যে ইমেজ ফিল্ডটি আপনার IPFS ছবির লোকেশনে পয়েন্ট করে — অন্যথায়, আপনার NFT-তে একটি (খুব সুন্দর!) কুকুরের ছবি থাকবে। + +JSON ফাইলটি এডিট করা হয়ে গেলে, এটি সেভ করুন এবং ছবিটি আপলোড করার জন্য আমরা যে পদক্ষেপগুলি অনুসরণ করেছিলাম সেগুলি অনুসরণ করে Pinata-তে আপলোড করুন। + +![কীভাবে আপনার nft-metadata.json Pinata-তে আপলোড করবেন](./uploadPinata.gif) + +## ধাপ 5: আপনার কন্ট্র্যাক্টের একটি ইনস্ট্যান্স তৈরি করুন {#instance-contract} + +এখন, আমাদের কন্ট্র্যাক্টের সাথে ইন্টারঅ্যাক্ট করার জন্য, আমাদের কোডে এটির একটি ইনস্ট্যান্স তৈরি করতে হবে। এটি করার জন্য আমাদের কন্ট্র্যাক্ট অ্যাড্রেসের প্রয়োজন হবে যা আমরা ডিপ্লয়মেন্ট থেকে অথবা [Blockscout](https://eth-sepolia.blockscout.com/) থেকে কন্ট্র্যাক্ট ডিপ্লয় করতে ব্যবহৃত অ্যাড্রেসটি খুঁজে পেতে পারি। + +![Etherscan-এ আপনার কন্ট্র্যাক্ট অ্যাড্রেস দেখুন](./view-contract-etherscan.png) + +উপরের উদাহরণে, আমাদের কন্ট্র্যাক্ট অ্যাড্রেস হল 0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778। + +এরপরে আমরা ABI এবং অ্যাড্রেস ব্যবহার করে আমাদের কন্ট্র্যাক্ট তৈরি করতে Web3 [কন্ট্র্যাক্ট মেথড](https://docs.web3js.org/api/web3-eth-contract/class/Contract) ব্যবহার করব। আপনার `mint-nft.js` ফাইলে, নিম্নলিখিতগুলি যোগ করুন: + +```js +const contractAddress = "0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778" + +const nftContract = new web3.eth.Contract(contract.abi, contractAddress) +``` + +## ধাপ 6: `.env` ফাইলটি আপডেট করুন {#update-env} + +এখন, Ethereum চেইনে ট্রানজ্যাকশন তৈরি এবং পাঠানোর জন্য, আমরা অ্যাকাউন্ট নন্স (নিচে ব্যাখ্যা করা হবে) পেতে আপনার পাবলিক Ethereum অ্যাকাউন্ট অ্যাড্রেস ব্যবহার করব। + +আপনার পাবলিক কী আপনার `.env` ফাইলে যোগ করুন — আপনি যদি টিউটোরিয়ালের পার্ট 1 সম্পন্ন করে থাকেন, তাহলে আমাদের `.env` ফাইলটি এখন এইরকম দেখতে হবে: + +```js +API_URL = "https://eth-sepolia.g.alchemy.com/v2/আপনার-এপিআই-কী" +PRIVATE_KEY = "আপনার-প্রাইভেট-অ্যাকাউন্ট-অ্যাড্রেস" +PUBLIC_KEY = "আপনার-পাবলিক-অ্যাকাউন্ট-অ্যাড্রেস" +``` + +## ধাপ 7: আপনার ট্রানজ্যাকশন তৈরি করুন {#create-txn} + +প্রথমে, আসুন `mintNFT(tokenData)` নামে একটি ফাংশন সংজ্ঞায়িত করি এবং নিম্নলিখিতগুলি করে আমাদের ট্রানজ্যাকশন তৈরি করি: + +1. `.env` ফাইল থেকে আপনার _PRIVATE_KEY_ এবং _PUBLIC_KEY_ নিন। + +2. এরপরে, আমাদের অ্যাকাউন্ট নন্স বের করতে হবে। নন্স স্পেসিফিকেশনটি আপনার অ্যাড্রেস থেকে পাঠানো ট্রানজ্যাকশনের সংখ্যা ট্র্যাক করতে ব্যবহৃত হয় — যা আমাদের নিরাপত্তার উদ্দেশ্যে এবং [রিপ্লে অ্যাটাক](https://docs.alchemyapi.io/resources/blockchain-glossary#account-nonce) প্রতিরোধ করার জন্য প্রয়োজন। আপনার অ্যাড্রেস থেকে পাঠানো ট্রানজ্যাকশনের সংখ্যা পেতে, আমরা [getTransactionCount](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc#eth_gettransactioncount) ব্যবহার করি। + +3. অবশেষে আমরা নিম্নলিখিত তথ্য দিয়ে আমাদের ট্রানজ্যাকশন সেট আপ করব: + +- `'from': PUBLIC_KEY` — আমাদের ট্রানজ্যাকশনের উৎস হল আমাদের পাবলিক অ্যাড্রেস + +- `'to': contractAddress` — যে কন্ট্র্যাক্টের সাথে আমরা ইন্টারঅ্যাক্ট করতে এবং ট্রানজ্যাকশন পাঠাতে চাই + +- `'nonce': nonce` — আমাদের অ্যাড্রেস থেকে পাঠানো ট্রানজ্যাকশনের সংখ্যা সহ অ্যাকাউন্ট নন্স + +- `'gas': estimatedGas` — ট্রানজ্যাকশনটি সম্পূর্ণ করার জন্য প্রয়োজনীয় আনুমানিক গ্যাস + +- `'data': nftContract.methods.mintNFT(PUBLIC_KEY, md).encodeABI()` — এই ট্রানজ্যাকশনে আমরা যে গণনাটি করতে চাই — যা এই ক্ষেত্রে একটি NFT মিন্ট করা + +আপনার `mint-nft.js` ফাইলটি এখন এইরকম দেখতে হবে: + +```js + require('dotenv').config(); + const API_URL = process.env.API_URL; + const PUBLIC_KEY = process.env.PUBLIC_KEY; + const PRIVATE_KEY = process.env.PRIVATE_KEY; + + const { createAlchemyWeb3 } = require("@alch/alchemy-web3"); + const web3 = createAlchemyWeb3(API_URL); + + const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json"); + const contractAddress = "0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778"; + const nftContract = new web3.eth.Contract(contract.abi, contractAddress); + + async function mintNFT(tokenURI) { + const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //সর্বশেষ নন্স পান + + //ট্রানজ্যাকশন + const tx = { + 'from': PUBLIC_KEY, + 'to': contractAddress, + 'nonce': nonce, + 'gas': 500000, + 'data': nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI() + }; + }​ +``` + +## ধাপ 8: ট্রানজ্যাকশন সাইন করুন {#sign-txn} + +এখন যেহেতু আমরা আমাদের ট্রানজ্যাকশন তৈরি করেছি, আমাদের এটি পাঠানোর জন্য সাইন করতে হবে। এখানে আমরা আমাদের প্রাইভেট কী ব্যবহার করব। + +`web3.eth.sendSignedTransaction` আমাদের ট্রানজ্যাকশন হ্যাস দেবে, যা আমরা আমাদের ট্রানজ্যাকশনটি মাইনিং হয়েছে এবং নেটওয়ার্ক দ্বারা ড্রপ করা হয়নি তা নিশ্চিত করতে ব্যবহার করতে পারি। আপনি লক্ষ্য করবেন ট্রানজ্যাকশন সাইনিং বিভাগে, আমরা কিছু এরর চেকিং যোগ করেছি যাতে আমরা জানতে পারি আমাদের ট্রানজ্যাকশনটি সফলভাবে হয়েছে কিনা। + +```js +require("dotenv").config() +const API_URL = process.env.API_URL +const PUBLIC_KEY = process.env.PUBLIC_KEY +const PRIVATE_KEY = process.env.PRIVATE_KEY + +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(API_URL) + +const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json") +const contractAddress = "0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778" +const nftContract = new web3.eth.Contract(contract.abi, contractAddress) + +async function mintNFT(tokenURI) { + const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest") //সর্বশেষ নন্স পান + + //ট্রানজ্যাকশন + const tx = { + from: PUBLIC_KEY, + to: contractAddress, + nonce: nonce, + gas: 500000, + data: nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI(), + } + + const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY) + signPromise + .then((signedTx) => { + web3.eth.sendSignedTransaction( + signedTx.rawTransaction, + function (err, hash) { + if (!err) { + console.log( + "আপনার ট্রানজ্যাকশনের হ্যাস হল: ", + hash, + "\nআপনার ট্রানজ্যাকশনের স্ট্যাটাস দেখতে Alchemy-র মেমপুল দেখুন!" + ) + } else { + console.log( + "আপনার ট্রানজ্যাকশন জমা দেওয়ার সময় কিছু ভুল হয়েছে:", + err + ) + } + } + ) + }) + .catch((err) => { + console.log(" Promise ব্যর্থ হয়েছে:", err) + }) +} +``` + +## ধাপ 9: `mintNFT` কল করুন এবং node `mint-nft.js` রান করুন {#call-mintnft-fn} + +Pinata-তে আপলোড করা `metadata.json`-এর কথা মনে আছে? Pinata থেকে এর হ্যাসকোড নিন এবং `mintNFT` ফাংশনে নিম্নলিখিতটি প্যারামিটার হিসাবে পাস করুন `https://gateway.pinata.cloud/ipfs/` + +হ্যাসকোড কীভাবে পাবেন তা এখানে দেওয়া হল: + +![Pinata-তে আপনার এনএফটি মেটাডেটা হ্যাসকোড কীভাবে পাবেন](./metadataPinata.gif)_Pinata-তে আপনার এনএফটি মেটাডেটা হ্যাসকোড কীভাবে পাবেন_ + +> আলাদা উইন্ডোতে `https://gateway.pinata.cloud/ipfs/` লোড করে ডাবল-চেক করুন যে আপনার কপি করা হ্যাসকোডটি আপনার **metadata.json**-এর সাথে লিঙ্ক করে। পেজটি নিচের স্ক্রিনশটের মতো দেখতে হবে: + +![আপনার পেজে json মেটাডেটা প্রদর্শন করা উচিত](./metadataJSON.png)_আপনার পেজে json মেটাডেটা প্রদর্শন করা উচিত_ + +সব মিলিয়ে, আপনার কোডটি এইরকম দেখতে হবে: + +```js +require("dotenv").config() +const API_URL = process.env.API_URL +const PUBLIC_KEY = process.env.PUBLIC_KEY +const PRIVATE_KEY = process.env.PRIVATE_KEY + +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(API_URL) + +const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json") +const contractAddress = "0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778" +const nftContract = new web3.eth.Contract(contract.abi, contractAddress) + +async function mintNFT(tokenURI) { + const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest") //সর্বশেষ নন্স পান + + //ট্রানজ্যাকশন + const tx = { + from: PUBLIC_KEY, + to: contractAddress, + nonce: nonce, + gas: 500000, + data: nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI(), + } + + const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY) + signPromise + .then((signedTx) => { + web3.eth.sendSignedTransaction( + signedTx.rawTransaction, + function (err, hash) { + if (!err) { + console.log( + "আপনার ট্রানজ্যাকশনের হ্যাস হল: ", + hash, + "\nআপনার ট্রানজ্যাকশনের স্ট্যাটাস দেখতে Alchemy-র মেমপুল দেখুন!" + ) + } else { + console.log( + "আপনার ট্রানজ্যাকশন জমা দেওয়ার সময় কিছু ভুল হয়েছে:", + err + ) + } + } + ) + }) + .catch((err) => { + console.log("Promise ব্যর্থ হয়েছে:", err) + }) +} + +mintNFT("ipfs://QmYueiuRNmL4MiA2GwtVMm6ZagknXnSpQnB3z2gWbz36hP") +``` + +এখন, আপনার NFT ডিপ্লয় করতে `node scripts/mint-nft.js` রান করুন। কয়েক সেকেন্ড পরে, আপনার টার্মিনালে এইরকম একটি প্রতিক্রিয়া দেখতে পাওয়া উচিত: + + ``` + আপনার ট্রানজ্যাকশনের হ্যাস হল: 0x301791fdf492001fcd9d5e5b12f3aa1bbbea9a88ed24993a8ab2cdae2d06e1e8 + + আপনার ট্রানজ্যাকশনের স্থিতি দেখতে Alchemy-র মেমপুল দেখুন! + ``` + +এরপরে, আপনার ট্রানজ্যাকশনের স্থিতি দেখতে (এটি পেন্ডিং, মাইনিং করা বা নেটওয়ার্ক দ্বারা ড্রপ করা হয়েছে কিনা) আপনার [Alchemy mempool](https://dashboard.alchemyapi.io/mempool) ভিজিট করুন। যদি আপনার ট্রানজ্যাকশনটি ড্রপ হয়ে যায়, তবে [Blockscout](https://eth-sepolia.blockscout.com/) চেক করা এবং আপনার ট্রানজ্যাকশন হ্যাস অনুসন্ধান করাও সহায়ক। + +![Etherscan-এ আপনার NFT ট্রানজ্যাকশন হ্যাস দেখুন](./view-nft-etherscan.png)_Etherscan-এ আপনার NFT ট্রানজ্যাকশন হ্যাস দেখুন_ + +এবং এটাই সব! আপনি এখন Ethereum ব্লকচেইনে একটি NFT ডিপ্লয় এবং মিন্ট করেছেন + +`mint-nft.js` ব্যবহার করে আপনি আপনার মন (এবং ওয়ালেট) যতগুলো চায় ততগুলো NFT মিন্ট করতে পারবেন! শুধু NFT-এর মেটাডেটা বর্ণনাকারী একটি নতুন tokenURI পাস করতে ভুলবেন না (অন্যথায়, আপনি কেবল বিভিন্ন ID সহ একগুচ্ছ অভিন্ন NFT তৈরি করবেন)। + +সম্ভবত, আপনি আপনার ওয়ালেটে আপনার NFT দেখাতে সক্ষম হতে চাইবেন — তাই [পার্ট 3: আপনার ওয়ালেটে আপনার NFT কীভাবে দেখবেন](/developers/tutorials/how-to-view-nft-in-metamask/) দেখতে ভুলবেন না! diff --git a/public/content/translations/bn/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md b/public/content/translations/bn/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md new file mode 100644 index 00000000000..dd7053184b0 --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md @@ -0,0 +1,102 @@ +--- +title: "পরীক্ষার জন্য সলিডিটি স্মার্ট কন্ট্র্যাক্টগুলিকে কীভাবে মক করা যায়" +description: "পরীক্ষার সময় আপনার কন্ট্র্যাক্টগুলো নিয়ে কেন মজা করা উচিত" +author: Markus Waas +lang: bn +tags: [ "সলিডিটি", "স্মার্ট কন্ট্র্যাক্ট", "পরীক্ষা", "মকিং" ] +skill: intermediate +published: 2020-05-02 +source: soliditydeveloper.com +sourceUrl: https://soliditydeveloper.com/mocking-contracts +--- + +[মক অবজেক্ট](https://wikipedia.org/wiki/Mock_object) অবজেক্ট-ওরিয়েন্টেড প্রোগ্রামিং-এর একটি সাধারণ ডিজাইন প্যাটার্ন। পুরাতন ফরাসি শব্দ 'mocquer' থেকে এসেছে যার অর্থ 'মজা করা', এটি 'বাস্তব কিছু অনুকরণ করা'-তে বিকশিত হয়েছে যা আমরা আসলে প্রোগ্রামিংয়ে করে থাকি। অনুগ্রহ করে শুধুমাত্র আপনার স্মার্ট কন্ট্র্যাক্টগুলো নিয়ে মজা করুন যদি আপনি চান, কিন্তু যখনই পারেন সেগুলোকে মক করুন। এটি আপনার জীবনকে সহজ করে তোলে। + +## মকস দিয়ে কন্ট্র্যাক্টের ইউনিট-টেস্টিং {#unit-testing-contracts-with-mocks} + +একটি কন্ট্র্যাক্ট মক করার অর্থ হল সেই কন্ট্র্যাক্টের একটি দ্বিতীয় সংস্করণ তৈরি করা যা মূলটির মতোই আচরণ করে, কিন্তু এমনভাবে যা ডেভেলপার দ্বারা সহজেই নিয়ন্ত্রণ করা যায়। আপনি প্রায়শই জটিল কন্ট্র্যাক্টের সম্মুখীন হন যেখানে আপনি কেবল [কন্ট্র্যাক্টের ছোট অংশ ইউনিট-টেস্ট](/developers/docs/smart-contracts/testing/) করতে চান। সমস্যা হল, যদি এই ছোট অংশটি পরীক্ষা করার জন্য একটি খুব নির্দিষ্ট কন্ট্র্যাক্ট স্টেটের প্রয়োজন হয় যেখানে পৌঁছানো কঠিন? + +আপনি প্রতিবার জটিল টেস্ট সেটআপ লজিক লিখতে পারেন যা কন্ট্র্যাক্টটিকে প্রয়োজনীয় স্টেটে নিয়ে আসে অথবা আপনি একটি মক লিখুন। ইনহেরিটেন্সের মাধ্যমে একটি কন্ট্র্যাক্ট মক করা সহজ। কেবলমাত্র একটি দ্বিতীয় মক কন্ট্র্যাক্ট তৈরি করুন যা মূলটি থেকে ইনহেরিট করে। এখন আপনি আপনার মকের ফাংশনগুলি ওভাররাইড করতে পারেন। আসুন একটি উদাহরণ দিয়ে এটি দেখি। + +## উদাহরণ: প্রাইভেট ERC20 {#example-private-erc20} + +আমরা একটি উদাহরণ ERC-20 কন্ট্র্যাক্ট ব্যবহার করি যার একটি প্রাথমিক প্রাইভেট সময় রয়েছে। মালিক ব্যক্তিগত ব্যবহারকারীদের পরিচালনা করতে পারেন এবং শুধুমাত্র তাদেরকেই শুরুতে টোকেন গ্রহণ করার অনুমতি দেওয়া হবে। একটি নির্দিষ্ট সময় অতিবাহিত হয়ে গেলে, প্রত্যেককে টোকেন ব্যবহার করার অনুমতি দেওয়া হবে। আপনি যদি কৌতূহলী হন, আমরা নতুন OpenZeppelin contracts v3 থেকে [`_beforeTokenTransfer`](https://docs.openzeppelin.com/contracts/5.x/extending-contracts#using-hooks) হুক ব্যবহার করছি। + +```solidity +pragma solidity ^0.6.0; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract PrivateERC20 is ERC20, Ownable { + mapping (address => bool) public isPrivateUser; + uint256 private publicAfterTime; + + constructor(uint256 privateERC20timeInSec) ERC20("PrivateERC20", "PRIV") public { + publicAfterTime = now + privateERC20timeInSec; + } + + function addUser(address user) external onlyOwner { + isPrivateUser[user] = true; + } + + function isPublic() public view returns (bool) { + return now >= publicAfterTime; + } + + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override { + super._beforeTokenTransfer(from, to, amount); + + require(_validRecipient(to), "PrivateERC20: অবৈধ প্রাপক"); + } + + function _validRecipient(address to) private view returns (bool) { + if (isPublic()) { + return true; + } + + return isPrivateUser[to]; + } +} +``` + +এবং এখন আসুন এটি মক করি। + +```solidity +pragma solidity ^0.6.0; +import "../PrivateERC20.sol"; + +contract PrivateERC20Mock is PrivateERC20 { + bool isPublicConfig; + + constructor() public PrivateERC20(0) {} + + function setIsPublic(bool isPublic) external { + isPublicConfig = isPublic; + } + + function isPublic() public view returns (bool) { + return isPublicConfig; + } +} +``` + +আপনি নিম্নলিখিত ত্রুটি বার্তাগুলির মধ্যে একটি পাবেন: + +- `PrivateERC20Mock.sol: TypeError: Overriding function is missing "override" specifier.` +- `PrivateERC20.sol: TypeError: Trying to override non-virtual function. Did you forget to add "virtual"?.` + +যেহেতু আমরা নতুন 0.6 সলিডিটি সংস্করণ ব্যবহার করছি, তাই যে ফাংশনগুলি ওভাররাইড করা যেতে পারে তার জন্য আমাদের `virtual` কীওয়ার্ড এবং ওভাররাইডিং ফাংশনের জন্য override যোগ করতে হবে। সুতরাং আসুন আমরা উভয় `isPublic` ফাংশনে সেগুলি যোগ করি। + +এখন আপনার ইউনিট টেস্টে, আপনি পরিবর্তে `PrivateERC20Mock` ব্যবহার করতে পারেন। আপনি যখন প্রাইভেট ব্যবহারের সময় আচরণ পরীক্ষা করতে চান, তখন `setIsPublic(false)` ব্যবহার করুন এবং একইভাবে পাবলিক ব্যবহারের সময় পরীক্ষা করার জন্য `setIsPublic(true)` ব্যবহার করুন। অবশ্যই আমাদের উদাহরণে, আমরা সময় অনুযায়ী পরিবর্তন করার জন্য [টাইম হেল্পার](https://docs.openzeppelin.com/test-helpers/0.5/api#increase) ব্যবহার করতে পারতাম। কিন্তু মকিং-এর ধারণা এখন স্পষ্ট হওয়া উচিত এবং আপনি এমন পরিস্থিতি কল্পনা করতে পারেন যেখানে সময় এগিয়ে দেওয়াটা এত সহজ নয়। + +## অনেক কন্ট্র্যাক্ট মক করা {#mocking-many-contracts} + +প্রতিটি মকের জন্য যদি আপনাকে আরেকটি কন্ট্র্যাক্ট তৈরি করতে হয় তবে এটি অগোছালো হয়ে যেতে পারে। যদি এটি আপনাকে বিরক্ত করে, আপনি [MockContract](https://github.com/gnosis/mock-contract) লাইব্রেরিটি দেখতে পারেন। এটি আপনাকে তৎক্ষণাৎ কন্ট্র্যাক্টগুলির আচরণ ওভাররাইড এবং পরিবর্তন করতে দেয়। তবে, এটি কেবল অন্য একটি কন্ট্র্যাক্টে কল মক করার জন্য কাজ করে, তাই এটি আমাদের উদাহরণের জন্য কাজ করবে না। + +## মকিং আরও শক্তিশালী হতে পারে {#mocking-can-be-even-more-powerful} + +মকিংয়ের ক্ষমতা এখানেই শেষ নয়। + +- ফাংশন যোগ করা: কেবল একটি নির্দিষ্ট ফাংশন ওভাররাইড করাই কার্যকর নয়, অতিরিক্ত ফাংশন যোগ করাও কার্যকর। টোকেনের জন্য একটি ভাল উদাহরণ হল একটি অতিরিক্ত `mint` ফাংশন থাকা যা যেকোনো ব্যবহারকারীকে বিনামূল্যে নতুন টোকেন পেতে দেয়। +- টেস্টনেটে ব্যবহার: যখন আপনি আপনার ডিএ্যাপ-এর সাথে টেস্টনেটে আপনার কন্ট্র্যাক্টগুলি স্থাপন এবং পরীক্ষা করেন, তখন একটি মকড সংস্করণ ব্যবহার করার কথা বিবেচনা করুন। ফাংশন ওভাররাইড করা এড়িয়ে চলুন যদি না আপনার সত্যিই প্রয়োজন হয়। আপনি সর্বোপরি আসল লজিক পরীক্ষা করতে চান। কিন্তু উদাহরণস্বরূপ একটি রিসেট ফাংশন যোগ করা কার্যকর হতে পারে যা কেবল কন্ট্র্যাক্টের স্টেটকে শুরুতে রিসেট করে, কোনো নতুন স্থাপনার প্রয়োজন নেই। স্পষ্টতই আপনি এটি একটি মেইননেট কন্ট্র্যাক্টে রাখতে চাইবেন না। diff --git a/public/content/translations/bn/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md b/public/content/translations/bn/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md new file mode 100644 index 00000000000..911b0c274aa --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md @@ -0,0 +1,708 @@ +--- +title: "স্মার্ট কন্ট্র্যাক্ট পরীক্ষা করার জন্য Echidna কীভাবে ব্যবহার করবেন" +description: "স্বয়ংক্রিয়ভাবে স্মার্ট কন্ট্র্যাক্ট পরীক্ষা করার জন্য Echidna কীভাবে ব্যবহার করবেন" +author: "Trailofbits" +lang: bn +tags: + [ + "সলিডিটি", + "স্মার্ট কন্ট্র্যাক্ট", + "নিরাপত্তা", + "পরীক্ষা", + "ফাজিং" + ] +skill: advanced +published: 2020-04-10 +source: Building secure contracts +sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/echidna +--- + +## ইনস্টলেশন {#installation} + +ডকারের মাধ্যমে অথবা প্রি-কম্পাইলড বাইনারি ব্যবহার করে Echidna ইনস্টল করা যেতে পারে। + +### ডকারের মাধ্যমে Echidna {#echidna-through-docker} + +```bash +docker pull trailofbits/eth-security-toolbox +docker run -it -v "$PWD":/home/training trailofbits/eth-security-toolbox +``` + +_শেষ কমান্ডটি একটি docker-এ eth-security-toolbox চালায় যেটির আপনার বর্তমান ডিরেক্টরিতে অ্যাক্সেস আছে। আপনি আপনার হোস্ট থেকে ফাইলগুলো পরিবর্তন করতে পারেন, এবং docker থেকে ফাইলগুলোতে টুলস চালাতে পারেন_ + +ডকারের ভিতরে, চালান: + +```bash +solc-select 0.5.11 +cd /home/training +``` + +### বাইনারি {#binary} + +[https://github.com/crytic/echidna/releases/tag/v1.4.0.0](https://github.com/crytic/echidna/releases/tag/v1.4.0.0) + +## প্রপার্টি-ভিত্তিক ফাজিং এর ভূমিকা {#introduction-to-property-based-fuzzing} + +Echidna হলো একটি প্রপার্টি-ভিত্তিক ফাজার, যা আমরা আমাদের আগের ব্লগপোস্টগুলোতে বর্ণনা করেছি ([1](https://blog.trailofbits.com/2018/03/09/echidna-a-smart-fuzzer-for-ethereum/), [2](https://blog.trailofbits.com/2018/05/03/state-machine-testing-with-echidna/), [3](https://blog.trailofbits.com/2020/03/30/an-echidna-for-all-seasons/)). + +### ফাজিং {#fuzzing} + +[ফাজিং](https://wikipedia.org/wiki/Fuzzing) নিরাপত্তা কমিউনিটিতে একটি সুপরিচিত কৌশল। এটিতে প্রোগ্রামের মধ্যে বাগ খুঁজে বের করার জন্য কম-বেশি র‍্যান্ডম ইনপুট তৈরি করা হয়। প্রচলিত সফটওয়্যারের জন্য ফাজার (যেমন [AFL](http://lcamtuf.coredump.cx/afl/) বা [LibFuzzer](https://llvm.org/docs/LibFuzzer.html)) বাগ খুঁজে বের করার জন্য দক্ষ টুলস হিসেবে পরিচিত। + +শুধুমাত্র র‍্যান্ডম ইনপুট তৈরি করার বাইরেও, ভালো ইনপুট তৈরি করার জন্য অনেক কৌশল এবং স্ট্র্যাটেজি রয়েছে, যার মধ্যে রয়েছে: + +- প্রতিটি এক্সিকিউশন থেকে ফিডব্যাক পাওয়া এবং তা ব্যবহার করে জেনারেশনকে গাইড করা। উদাহরণস্বরূপ, যদি একটি নতুন জেনারেটেড ইনপুট একটি নতুন পথ আবিষ্কার করে, তবে তার কাছাকাছি নতুন ইনপুট জেনারেট করা অর্থপূর্ণ হতে পারে। +- একটি কাঠামোগত সীমাবদ্ধতাকে সম্মান করে ইনপুট তৈরি করা। উদাহরণস্বরূপ, যদি আপনার ইনপুটে একটি চেক্সামসহ একটি হেডার থাকে, তাহলে ফাজারকে চেক্সাম ভ্যালিডেট করে এমন ইনপুট তৈরি করতে দেওয়াটা যুক্তিযুক্ত হবে। +- নতুন ইনপুট তৈরি করার জন্য পরিচিত ইনপুট ব্যবহার করা: যদি আপনার কাছে বৈধ ইনপুটের একটি বড় ডেটাসেটের অ্যাক্সেস থাকে, তাহলে আপনার ফাজার স্ক্র্যাচ থেকে জেনারেশন শুরু করার পরিবর্তে সেগুলো থেকে নতুন ইনপুট তৈরি করতে পারে। এগুলোকে সাধারণত _সিড_ বলা হয়। + +### প্রপার্টি-ভিত্তিক ফাজিং {#property-based-fuzzing} + +Echidna একটি নির্দিষ্ট ফাজার পরিবারের অন্তর্গত: এটি প্রপার্টি-ভিত্তিক ফাজিং যা [QuickCheck](https://wikipedia.org/wiki/QuickCheck) দ্বারা ব্যাপকভাবে অনুপ্রাণিত। ক্লাসিক ফাজারের বিপরীতে, যা ক্র্যাশ খুঁজে বের করার চেষ্টা করে, Echidna ব্যবহারকারী-সংজ্ঞায়িত ইনভেরিয়েন্টগুলো ভাঙার চেষ্টা করবে। + +স্মার্ট কন্ট্র্যাক্টে, ইনভেরিয়েন্টগুলো হলো সলিডিটি ফাংশন, যা কন্ট্র্যাক্ট পৌঁছাতে পারে এমন যেকোনো ভুল বা অবৈধ স্টেটকে উপস্থাপন করতে পারে, যার মধ্যে রয়েছে: + +- ভুল অ্যাক্সেস কন্ট্রোল: আক্রমণকারী কন্ট্র্যাক্টের মালিক হয়ে গিয়েছে। +- ভুল স্টেট মেশিন: কন্ট্র্যাক্ট পজ করা অবস্থায় টোকেনগুলো ট্রান্সফার করা যেতে পারে। +- ভুল অ্যারিথমেটিক: ব্যবহারকারী তার ব্যালেন্স আন্ডারফ্লো করতে পারে এবং সীমাহীন ফ্রি টোকেন পেতে পারে। + +### Echidna দিয়ে একটি প্রপার্টি পরীক্ষা করা {#testing-a-property-with-echidna} + +আমরা দেখব কীভাবে Echidna দিয়ে একটি স্মার্ট কন্ট্র্যাক্ট পরীক্ষা করা যায়। টার্গেট হলো নিম্নলিখিত স্মার্ট কন্ট্র্যাক্ট [`token.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/example/token.sol): + +```solidity +contract Token{ + mapping(address => uint) public balances; + function airdrop() public{ + balances[msg.sender] = 1000; + } + function consume() public{ + require(balances[msg.sender]>0); + balances[msg.sender] -= 1; + } + function backdoor() public{ + balances[msg.sender] += 1; + } +} +``` + +আমরা এই অনুমানটি করব যে এই টোকেনের নিম্নলিখিত বৈশিষ্ট্যগুলো অবশ্যই থাকতে হবে: + +- যেকোনো ব্যক্তির কাছে সর্বোচ্চ 1000টি টোকেন থাকতে পারে +- টোকেনটি ট্রান্সফার করা যাবে না (এটি কোনো ERC20 টোকেন নয়) + +### একটি প্রপার্টি লিখুন {#write-a-property} + +Echidna প্রপার্টিগুলো হলো সলিডিটি ফাংশন। একটি প্রপার্টিতে অবশ্যই থাকতে হবে: + +- কোনো আর্গুমেন্ট থাকবে না +- সফল হলে `true` রিটার্ন করবে +- এর নাম `echidna` দিয়ে শুরু হতে হবে + +Echidna করবে: + +- প্রপার্টি পরীক্ষা করার জন্য স্বয়ংক্রিয়ভাবে আর্বিট্রারি ট্রানজ্যাকশন জেনারেট করবে। +- কোনো প্রপার্টি `false` রিটার্ন করলে বা কোনো এরর থ্রো করলে সেই ট্রানজ্যাকশনগুলো রিপোর্ট করবে। +- একটি প্রপার্টি কল করার সময় সাইড-ইফেক্ট বাতিল করবে (অর্থাৎ, যদি প্রপার্টি কোনো স্টেট ভ্যারিয়েবল পরিবর্তন করে, পরীক্ষার পরে তা বাতিল করা হয়) + +নিম্নলিখিত প্রপার্টিটি পরীক্ষা করে যে কলারের কাছে 1000টির বেশি টোকেন নেই: + +```solidity +function echidna_balance_under_1000() public view returns(bool){ + return balances[msg.sender] <= 1000; +} +``` + +আপনার কন্ট্র্যাক্টকে আপনার প্রপার্টি থেকে আলাদা করতে ইনহেরিটেন্স ব্যবহার করুন: + +```solidity +contract TestToken is Token{ + function echidna_balance_under_1000() public view returns(bool){ + return balances[msg.sender] <= 1000; + } + } +``` + +[`token.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/example/token.sol) প্রপার্টিটি প্রয়োগ করে এবং টোকেন থেকে ইনহেরিট করে। + +### একটি কন্ট্র্যাক্ট শুরু করুন {#initiate-a-contract} + +Echidna-এর জন্য আর্গুমেন্ট ছাড়া একটি [কন্সট্রাক্টর](/developers/docs/smart-contracts/anatomy/#constructor-functions) প্রয়োজন। যদি আপনার কন্ট্র্যাক্টের জন্য একটি নির্দিষ্ট ইনিশিয়ালাইজেশন প্রয়োজন হয়, তবে আপনাকে তা কন্সট্রাক্টরের মধ্যে করতে হবে। + +Echidna-তে কিছু নির্দিষ্ট অ্যাড্রেস রয়েছে: + +- `0x00a329c0648769A73afAc7F9381E08FB43dBEA72` যা কন্সট্রাক্টরকে কল করে। +- `0x10000`, `0x20000`, এবং `0x00a329C0648769a73afAC7F9381e08fb43DBEA70` যা র‍্যান্ডমভাবে অন্য ফাংশনগুলোকে কল করে। + +আমাদের বর্তমান উদাহরণে কোনো বিশেষ ইনিশিয়ালাইজেশনের প্রয়োজন নেই, ফলস্বরূপ আমাদের কন্সট্রাক্টরটি খালি। + +### Echidna চালান {#run-echidna} + +Echidna চালু করা হয় এভাবে: + +```bash +echidna-test contract.sol +``` + +যদি contract.sol-এ একাধিক কন্ট্র্যাক্ট থাকে, আপনি টার্গেট নির্দিষ্ট করতে পারেন: + +```bash +echidna-test contract.sol --contract MyContract +``` + +### সারসংক্ষেপ: একটি প্রপার্টি পরীক্ষা করা {#summary-testing-a-property} + +নিম্নলিখিতটি আমাদের উদাহরণের উপর echidna চালানোর সারসংক্ষেপ: + +```solidity +contract TestToken is Token{ + constructor() public {} + function echidna_balance_under_1000() public view returns(bool){ + return balances[msg.sender] <= 1000; + } + } +``` + +```bash +echidna-test testtoken.sol --contract TestToken +... + +echidna_balance_under_1000: failed!💥 + Call sequence, shrinking (1205/5000): + airdrop() + backdoor() + +... +``` + +Echidna খুঁজে পেয়েছে যে `backdoor` কল করা হলে প্রপার্টিটি লঙ্ঘিত হয়। + +## ফাজিং ক্যাম্পেইনের সময় কল করার জন্য ফাংশন ফিল্টার করা {#filtering-functions-to-call-during-a-fuzzing-campaign} + +আমরা দেখব ফাজ করা হবে এমন ফাংশনগুলো কীভাবে ফিল্টার করতে হয়। +টার্গেট হলো নিম্নলিখিত স্মার্ট কন্ট্র্যাক্ট: + +```solidity +contract C { + bool state1 = false; + bool state2 = false; + bool state3 = false; + bool state4 = false; + + function f(uint x) public { + require(x == 12); + state1 = true; + } + + function g(uint x) public { + require(state1); + require(x == 8); + state2 = true; + } + + function h(uint x) public { + require(state2); + require(x == 42); + state3 = true; + } + + function i() public { + require(state3); + state4 = true; + } + + function reset1() public { + state1 = false; + state2 = false; + state3 = false; + return; + } + + function reset2() public { + state1 = false; + state2 = false; + state3 = false; + return; + } + + function echidna_state4() public returns (bool) { + return (!state4); + } +} +``` + +এই ছোট উদাহরণটি Echidna-কে একটি স্টেট ভ্যারিয়েবল পরিবর্তন করার জন্য একটি নির্দিষ্ট ট্রানজ্যাকশন সিকোয়েন্স খুঁজে বের করতে বাধ্য করে। +এটি একটি ফাজারের জন্য কঠিন (এর জন্য [Manticore](https://github.com/trailofbits/manticore)-এর মতো একটি সিম্বলিক এক্সিকিউশন টুল ব্যবহার করার পরামর্শ দেওয়া হয়)। +এটি যাচাই করার জন্য আমরা Echidna চালাতে পারি: + +```bash +echidna-test multi.sol +... +echidna_state4: passed! 🎉 +Seed: -3684648582249875403 +``` + +### ফাংশন ফিল্টার করা {#filtering-functions} + +এই কন্ট্র্যাক্টটি পরীক্ষা করার জন্য সঠিক সিকোয়েন্স খুঁজে পেতে Echidna-এর সমস্যা হচ্ছে কারণ দুটি রিসেট ফাংশন (`reset1` এবং `reset2`) সমস্ত স্টেট ভ্যারিয়েবলকে `false`-এ সেট করে দেবে। +তবে, আমরা রিসেট ফাংশনকে ব্ল্যাকলিস্ট করতে বা শুধুমাত্র `f`, `g`, +`h` এবং `i` ফাংশনগুলোকে হোয়াইটলিস্ট করতে একটি বিশেষ Echidna ফিচার ব্যবহার করতে পারি। + +ফাংশন ব্ল্যাকলিস্ট করতে, আমরা এই কনফিগারেশন ফাইলটি ব্যবহার করতে পারি: + +```yaml +filterBlacklist: true +filterFunctions: ["reset1", "reset2"] +``` + +ফাংশন ফিল্টার করার আরেকটি উপায় হলো হোয়াইটলিস্টেড ফাংশনগুলোকে তালিকাভুক্ত করা। এটি করার জন্য, আমরা এই কনফিগারেশন ফাইলটি ব্যবহার করতে পারি: + +```yaml +filterBlacklist: false +filterFunctions: ["f", "g", "h", "i"] +``` + +- `filterBlacklist` ডিফল্টভাবে `true` থাকে। +- ফিল্টারিং শুধুমাত্র নামের উপর ভিত্তি করে করা হবে (প্যারামিটার ছাড়া)। আপনার যদি `f()` এবং `f(uint256)` থাকে, তাহলে `"f"` ফিল্টারটি উভয় ফাংশনের সাথেই মিলবে। + +### Echidna চালান {#run-echidna-1} + +`blacklist.yaml` কনফিগারেশন ফাইল দিয়ে Echidna চালাতে: + +```bash +echidna-test multi.sol --config blacklist.yaml +... +echidna_state4: failed!💥 + Call sequence: + f(12) + g(8) + h(42) + i() +``` + +Echidna প্রায় সঙ্গে সঙ্গেই প্রপার্টিটি ভুল প্রমাণ করার জন্য ট্রানজ্যাকশনের সিকোয়েন্স খুঁজে বের করবে। + +### সারসংক্ষেপ: ফাংশন ফিল্টার করা {#summary-filtering-functions} + +Echidna একটি ফাজিং ক্যাম্পেইনের সময় কল করার জন্য ফাংশনগুলোকে ব্ল্যাকলিস্ট বা হোয়াইটলিস্ট করতে পারে, এটি ব্যবহার করে: + +```yaml +filterBlacklist: true +filterFunctions: ["f1", "f2", "f3"] +``` + +```bash +echidna-test contract.sol --config config.yaml +... +``` + +`filterBlacklist` বুলিয়ানের মানের উপর ভিত্তি করে Echidna `f1`, `f2` এবং `f3` কে ব্ল্যাকলিস্ট করে বা শুধুমাত্র এগুলোকে কল করে একটি ফাজিং ক্যাম্পেইন শুরু করে। + +## Echidna দিয়ে Solidity-এর অ্যাসার্ট কীভাবে পরীক্ষা করবেন {#how-to-test-soliditys-assert-with-echidna} + +এই সংক্ষিপ্ত টিউটোরিয়ালে, আমরা দেখাব কীভাবে কন্ট্র্যাক্টে অ্যাসারশন চেকিং পরীক্ষা করতে Echidna ব্যবহার করা যায়। ধরা যাক আমাদের কাছে এইরকম একটি কন্ট্র্যাক্ট আছে: + +```solidity +contract Incrementor { + uint private counter = 2**200; + + function inc(uint val) public returns (uint){ + uint tmp = counter; + counter += val; + // tmp <= counter + return (counter - tmp); + } +} +``` + +### একটি অ্যাসারশন লিখুন {#write-an-assertion} + +আমরা নিশ্চিত করতে চাই যে `tmp` এর পার্থক্য রিটার্ন করার পরে `counter`-এর চেয়ে কম বা সমান। আমরা একটি +Echidna প্রপার্টি লিখতে পারতাম, কিন্তু আমাদের `tmp` মানটি কোথাও সংরক্ষণ করতে হবে। পরিবর্তে, আমরা এইরকম একটি অ্যাসারশন ব্যবহার করতে পারি: + +```solidity +contract Incrementor { + uint private counter = 2**200; + + function inc(uint val) public returns (uint){ + uint tmp = counter; + counter += val; + assert (tmp <= counter); + return (counter - tmp); + } +} +``` + +### Echidna চালান {#run-echidna-2} + +অ্যাসারশন ফেইলিওর টেস্টিং সক্ষম করতে, একটি [Echidna কনফিগারেশন ফাইল](https://github.com/crytic/echidna/wiki/Config) `config.yaml` তৈরি করুন: + +```yaml +checkAsserts: true +``` + +যখন আমরা Echidna-তে এই কন্ট্র্যাক্টটি চালাই, আমরা প্রত্যাশিত ফলাফল পাই: + +```bash +echidna-test assert.sol --config config.yaml +Analyzing contract: assert.sol:Incrementor +assertion in inc: failed!💥 + Call sequence, shrinking (2596/5000): + inc(21711016731996786641919559689128982722488122124807605757398297001483711807488) + inc(7237005577332262213973186563042994240829374041602535252466099000494570602496) + inc(86844066927987146567678238756515930889952488499230423029593188005934847229952) + +Seed: 1806480648350826486 +``` + +আপনি দেখতে পাচ্ছেন, Echidna `inc` ফাংশনে কিছু অ্যাসারশন ফেইলিওর রিপোর্ট করছে। প্রতিটি ফাংশনে একাধিক অ্যাসারশন যোগ করা সম্ভব, কিন্তু Echidna বলতে পারে না কোন অ্যাসারশনটি ব্যর্থ হয়েছে। + +### কখন এবং কীভাবে অ্যাসারশন ব্যবহার করবেন {#when-and-how-use-assertions} + +অ্যাসারশনগুলো সুস্পষ্ট প্রপার্টির বিকল্প হিসেবে ব্যবহার করা যেতে পারে, বিশেষ করে যদি পরীক্ষা করার শর্তগুলো কোনো `f` অপারেশনের সঠিক ব্যবহারের সাথে সরাসরি সম্পর্কিত হয়। কিছু কোডের পরে অ্যাসারশন যোগ করলে তা নিশ্চিত করবে যে এটি এক্সিকিউট হওয়ার ঠিক পরেই চেকটি ঘটবে: + +```solidity +function f(..) public { + // কিছু জটিল কোড + ... + assert (condition); + ... +} + +``` + +বিপরীতে, একটি সুস্পষ্ট echidna প্রপার্টি ব্যবহার করলে র‍্যান্ডমভাবে ট্রানজ্যাকশন এক্সিকিউট হবে এবং এটি ঠিক কখন পরীক্ষা করা হবে তা নিশ্চিত করার কোনো সহজ উপায় নেই। এই ওয়ার্কঅ্যারাউন্ডটি করা এখনও সম্ভব: + +```solidity +function echidna_assert_after_f() public returns (bool) { + f(..); + return(condition); +} +``` + +তবে, কিছু সমস্যা আছে: + +- যদি `f`-কে `internal` বা `external` হিসেবে ডিক্লেয়ার করা হয় তবে এটি ব্যর্থ হয়। +- `f`-কে কল করার জন্য কোন আর্গুমেন্ট ব্যবহার করা উচিত তা স্পষ্ট নয়। +- যদি `f` রিভার্ট করে, প্রপার্টিটি ব্যর্থ হবে। + +সাধারণভাবে, আমরা অ্যাসারশন কীভাবে ব্যবহার করতে হয় সে সম্পর্কে [জন রেগেরের সুপারিশ](https://blog.regehr.org/archives/1091) অনুসরণ করার পরামর্শ দিই: + +- অ্যাসারশন চেকিংয়ের সময় কোনো সাইড এফেক্ট জোর করে প্রয়োগ করবেন না। উদাহরণস্বরূপ: `assert(ChangeStateAndReturn() == 1)` +- স্পষ্ট স্টেটমেন্ট অ্যাসার্ট করবেন না। উদাহরণস্বরূপ, `assert(var >= 0)` যেখানে `var`-কে `uint` হিসেবে ডিক্লেয়ার করা হয়েছে। + +অবশেষে, অনুগ্রহ করে `assert`-এর পরিবর্তে `require` **ব্যবহার করবেন না**, কারণ Echidna এটি সনাক্ত করতে পারবে না (কিন্তু কন্ট্র্যাক্টটি যাইহোক রিভার্ট করবে)। + +### সারসংক্ষেপ: অ্যাসারশন চেকিং {#summary-assertion-checking} + +নিম্নলিখিতটি আমাদের উদাহরণের উপর echidna চালানোর সারসংক্ষেপ: + +```solidity +contract Incrementor { + uint private counter = 2**200; + + function inc(uint val) public returns (uint){ + uint tmp = counter; + counter += val; + assert (tmp <= counter); + return (counter - tmp); + } +} +``` + +```bash +echidna-test assert.sol --config config.yaml +Analyzing contract: assert.sol:Incrementor +assertion in inc: failed!💥 + Call sequence, shrinking (2596/5000): + inc(21711016731996786641919559689128982722488122124807605757398297001483711807488) + inc(7237005577332262213973186563042994240829374041602535252466099000494570602496) + inc(86844066927987146567678238756515930889952488499230423029593188005934847229952) + +Seed: 1806480648350826486 +``` + +Echidna খুঁজে পেয়েছে যে `inc`-এর মধ্যে অ্যাসারশনটি ব্যর্থ হতে পারে যদি এই ফাংশনটিকে বড় আর্গুমেন্ট সহ একাধিকবার কল করা হয়। + +## একটি Echidna কর্পাস সংগ্রহ এবং পরিবর্তন করা {#collecting-and-modifying-an-echidna-corpus} + +আমরা দেখব Echidna-এর সাথে একটি ট্রানজ্যাকশন কর্পাস কীভাবে সংগ্রহ এবং ব্যবহার করতে হয়। টার্গেট হলো নিম্নলিখিত স্মার্ট কন্ট্র্যাক্ট [`magic.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/example/magic.sol): + +```solidity +contract C { + bool value_found = false; + function magic(uint magic_1, uint magic_2, uint magic_3, uint magic_4) public { + require(magic_1 == 42); + require(magic_2 == 129); + require(magic_3 == magic_4+333); + value_found = true; + return; + } + + function echidna_magic_values() public returns (bool) { + return !value_found; + } + +} +``` + +এই ছোট উদাহরণটি Echidna-কে একটি স্টেট ভ্যারিয়েবল পরিবর্তন করার জন্য নির্দিষ্ট মান খুঁজে বের করতে বাধ্য করে। এটি একটি ফাজারের জন্য কঠিন +(এর জন্য [Manticore](https://github.com/trailofbits/manticore)-এর মতো একটি সিম্বলিক এক্সিকিউশন টুল ব্যবহার করার পরামর্শ দেওয়া হয়)। +এটি যাচাই করার জন্য আমরা Echidna চালাতে পারি: + +```bash +echidna-test magic.sol +... + +echidna_magic_values: passed! 🎉 + +Seed: 2221503356319272685 +``` + +তবে, আমরা এই ফাজিং ক্যাম্পেইন চালানোর সময় কর্পাস সংগ্রহ করতে Echidna ব্যবহার করতে পারি। + +### একটি কর্পাস সংগ্রহ করা {#collecting-a-corpus} + +কর্পাস সংগ্রহ সক্ষম করতে, একটি কর্পাস ডিরেক্টরি তৈরি করুন: + +```bash +mkdir corpus-magic +``` + +এবং একটি [Echidna কনফিগারেশন ফাইল](https://github.com/crytic/echidna/wiki/Config) `config.yaml`: + +```yaml +coverage: true +corpusDir: "corpus-magic" +``` + +এখন আমরা আমাদের টুলটি চালাতে পারি এবং সংগৃহীত কর্পাস পরীক্ষা করতে পারি: + +```bash +echidna-test magic.sol --config config.yaml +``` + +Echidna এখনও সঠিক ম্যাজিক ভ্যালু খুঁজে পাচ্ছে না, তবে আমরা এর সংগৃহীত কর্পাসটি দেখতে পারি। +উদাহরণস্বরূপ, এই ফাইলগুলোর মধ্যে একটি ছিল: + +```json +[ + { + "_gas'": "0xffffffff", + "_delay": ["0x13647", "0xccf6"], + "_src": "00a329c0648769a73afac7f9381e08fb43dbea70", + "_dst": "00a329c0648769a73afac7f9381e08fb43dbea72", + "_value": "0x0", + "_call": { + "tag": "SolCall", + "contents": [ + "magic", + [ + { + "contents": [ + 256, + "93723985220345906694500679277863898678726808528711107336895287282192244575836" + ], + "tag": "AbiUInt" + }, + { + "contents": [256, "334"], + "tag": "AbiUInt" + }, + { + "contents": [ + 256, + "68093943901352437066264791224433559271778087297543421781073458233697135179558" + ], + "tag": "AbiUInt" + }, + { + "tag": "AbiUInt", + "contents": [256, "332"] + } + ] + ] + }, + "_gasprice'": "0xa904461f1" + } +] +``` + +স্পষ্টতই, এই ইনপুটটি আমাদের প্রপার্টিতে ফেইলিওর ট্রিগার করবে না। তবে, পরবর্তী ধাপে, আমরা দেখব এর জন্য কীভাবে এটি পরিবর্তন করতে হয়। + +### একটি কর্পাস সিডিং করা {#seeding-a-corpus} + +`magic` ফাংশনটির সাথে মোকাবিলা করার জন্য Echidna-এর কিছু সাহায্য প্রয়োজন। আমরা এর জন্য উপযুক্ত +প্যারামিটার ব্যবহার করার জন্য ইনপুটটি কপি এবং মডিফাই করতে যাচ্ছি: + +```bash +cp corpus/2712688662897926208.txt corpus/new.txt +``` + +আমরা `magic(42,129,333,0)` কল করার জন্য `new.txt` মডিফাই করব। এখন, আমরা Echidna পুনরায় চালাতে পারি: + +```bash +echidna-test magic.sol --config config.yaml +... +echidna_magic_values: failed!💥 + Call sequence: + magic(42,129,333,0) + + +Unique instructions: 142 +Unique codehashes: 1 +Seed: -7293830866560616537 + +``` + +এবার, এটি খুঁজে পেয়েছে যে প্রপার্টিটি সঙ্গে সঙ্গে লঙ্ঘিত হয়েছে। + +## উচ্চ গ্যাস খরচের ট্রানজ্যাকশন খোঁজা {#finding-transactions-with-high-gas-consumption} + +আমরা দেখব Echidna দিয়ে কীভাবে উচ্চ গ্যাস খরচের ট্রানজ্যাকশন খুঁজে বের করা যায়। টার্গেট হলো নিম্নলিখিত স্মার্ট কন্ট্র্যাক্ট: + +```solidity +contract C { + uint state; + + function expensive(uint8 times) internal { + for(uint8 i=0; i < times; i++) + state = state + i; + } + + function f(uint x, uint y, uint8 times) public { + if (x == 42 && y == 123) + expensive(times); + else + state = 0; + } + + function echidna_test() public returns (bool) { + return true; + } + +} +``` + +এখানে `expensive`-এর একটি বড় গ্যাস খরচ থাকতে পারে। + +বর্তমানে, Echidna-এর পরীক্ষা করার জন্য সবসময় একটি প্রপার্টি প্রয়োজন: এখানে `echidna_test` সবসময় `true` রিটার্ন করে। +এটি যাচাই করার জন্য আমরা Echidna চালাতে পারি: + +``` +echidna-test gas.sol +... +echidna_test: passed! 🎉 + +Seed: 2320549945714142710 +``` + +### গ্যাস খরচ পরিমাপ করা {#measuring-gas-consumption} + +Echidna-এর সাথে গ্যাস খরচ সক্ষম করতে, একটি কনফিগারেশন ফাইল `config.yaml` তৈরি করুন: + +```yaml +estimateGas: true +``` + +এই উদাহরণে, ফলাফলগুলো সহজে বোঝার জন্য আমরা ট্রানজ্যাকশন সিকোয়েন্সের আকারও কমিয়ে দেব: + +```yaml +seqLen: 2 +estimateGas: true +``` + +### Echidna চালান {#run-echidna-3} + +কনফিগারেশন ফাইল তৈরি হয়ে গেলে, আমরা এইভাবে Echidna চালাতে পারি: + +```bash +echidna-test gas.sol --config config.yaml +... +echidna_test: passed! 🎉 + +f used a maximum of 1333608 gas + Call sequence: + f(42,123,249) Gas price: 0x10d5733f0a Time delay: 0x495e5 Block delay: 0x88b2 + +Unique instructions: 157 +Unique codehashes: 1 +Seed: -325611019680165325 + +``` + +- [HEVM](https://github.com/dapphub/dapptools/tree/master/src/hevm#hevm-) দ্বারা প্রদত্ত একটি অনুমান হলো দেখানো গ্যাস। + +### গ্যাস-কমানো কলগুলো ফিল্টার করা {#filtering-out-gas-reducing-calls} + +উপরে **ফাজিং ক্যাম্পেইনের সময় কল করার জন্য ফাংশন ফিল্টার করা** বিষয়ক টিউটোরিয়ালটি দেখায় কীভাবে আপনার টেস্টিং থেকে কিছু ফাংশন সরানো যায়। +একটি সঠিক গ্যাস অনুমান পাওয়ার জন্য এটি অত্যন্ত গুরুত্বপূর্ণ হতে পারে। +নিম্নলিখিত উদাহরণটি বিবেচনা করুন: + +```solidity +contract C { + address [] addrs; + function push(address a) public { + addrs.push(a); + } + function pop() public { + addrs.pop(); + } + function clear() public{ + addrs.length = 0; + } + function check() public{ + for(uint256 i = 0; i < addrs.length; i++) + for(uint256 j = i+1; j < addrs.length; j++) + if (addrs[i] == addrs[j]) + addrs[j] = address(0x0); + } + function echidna_test() public returns (bool) { + return true; + } +} +``` + +যদি Echidna সমস্ত ফাংশন কল করতে পারে, তবে এটি সহজে উচ্চ গ্যাস খরচের ট্রানজ্যাকশন খুঁজে পাবে না: + +``` +echidna-test pushpop.sol --config config.yaml +... +pop used a maximum of 10746 gas +... +check used a maximum of 23730 gas +... +clear used a maximum of 35916 gas +... +push used a maximum of 40839 gas +``` + +এর কারণ হলো খরচ `addrs`-এর আকারের উপর নির্ভর করে এবং র‍্যান্ডম কলগুলো অ্যারেটিকে প্রায় খালি রেখে দেয়। +তবে, `pop` এবং `clear`-কে ব্ল্যাকলিস্ট করা আমাদের অনেক ভালো ফলাফল দেয়: + +```yaml +filterBlacklist: true +filterFunctions: ["pop", "clear"] +``` + +``` +echidna-test pushpop.sol --config config.yaml +... +push used a maximum of 40839 gas +... +check used a maximum of 1484472 gas +``` + +### সারসংক্ষেপ: উচ্চ গ্যাস খরচের ট্রানজ্যাকশন খোঁজা {#summary-finding-transactions-with-high-gas-consumption} + +`estimateGas` কনফিগারেশন অপশন ব্যবহার করে Echidna উচ্চ গ্যাস খরচের ট্রানজ্যাকশন খুঁজে বের করতে পারে: + +```yaml +estimateGas: true +``` + +```bash +echidna-test contract.sol --config config.yaml +... +``` + +ফাজিং ক্যাম্পেইন শেষ হয়ে গেলে Echidna প্রতিটি ফাংশনের জন্য সর্বোচ্চ গ্যাস খরচের একটি সিকোয়েন্স রিপোর্ট করবে। diff --git a/public/content/translations/bn/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md b/public/content/translations/bn/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md new file mode 100644 index 00000000000..8c506dc8609 --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md @@ -0,0 +1,526 @@ +--- +title: "স্মার্ট কন্ট্র্যাক্টে বাগ খুঁজে বের করতে Manticore কিভাবে ব্যবহার করবেন" +description: "স্মার্ট কন্ট্র্যাক্টে স্বয়ংক্রিয়ভাবে বাগ খুঁজে বের করতে Manticore কিভাবে ব্যবহার করবেন" +author: Trailofbits +lang: bn +tags: + [ + "সলিডিটি", + "স্মার্ট কন্ট্র্যাক্ট", + "নিরাপত্তা", + "পরীক্ষা", + "প্রথাগত যাচাইকরণ" + ] +skill: advanced +published: 2020-01-13 +source: Building secure contracts +sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/manticore +--- + +এই টিউটোরিয়ালের লক্ষ্য হল কিভাবে Manticore ব্যবহার করে স্মার্ট কন্ট্র্যাক্টে স্বয়ংক্রিয়ভাবে বাগ খুঁজে বের করা যায় তা দেখানো। + +## ইনস্টলেশন {#installation} + +Manticore-এর জন্য >= python 3.6 প্রয়োজন। এটি pip-এর মাধ্যমে বা docker ব্যবহার করে ইনস্টল করা যেতে পারে। + +### docker-এর মাধ্যমে Manticore {#manticore-through-docker} + +```bash +docker pull trailofbits/eth-security-toolbox +docker run -it -v "$PWD":/home/training trailofbits/eth-security-toolbox +``` + +_শেষ কমান্ডটি একটি docker-এ eth-security-toolbox চালায় যেটির আপনার বর্তমান ডিরেক্টরিতে অ্যাক্সেস আছে। আপনি আপনার হোস্ট থেকে ফাইলগুলো পরিবর্তন করতে পারেন, এবং docker থেকে ফাইলগুলোতে টুলস চালাতে পারেন_ + +docker-এর ভিতরে, চালান: + +```bash +solc-select 0.5.11 +cd /home/trufflecon/ +``` + +### pip-এর মাধ্যমে Manticore {#manticore-through-pip} + +```bash +pip3 install --user manticore +``` + +solc 0.5.11 সুপারিশ করা হয়। + +### একটি স্ক্রিপ্ট চালানো {#running-a-script} + +python 3 দিয়ে একটি python স্ক্রিপ্ট চালাতে: + +```bash +python3 script.py +``` + +## ডাইনামিক সিম্বলিক এক্সিকিউশন-এর ভূমিকা {#introduction-to-dynamic-symbolic-execution} + +### সংক্ষেপে ডাইনামিক সিম্বলিক এক্সিকিউশন {#dynamic-symbolic-execution-in-a-nutshell} + +ডাইনামিক সিম্বলিক এক্সিকিউশন (DSE) হল একটি প্রোগ্রাম অ্যানালিসিস কৌশল যা উচ্চ মাত্রার সিমেন্টিক সচেতনতার সাথে একটি স্টেট স্পেস এক্সপ্লোর করে। এই কৌশলটি "প্রোগ্রাম পাথ" আবিষ্কারের উপর ভিত্তি করে তৈরি, যা `path predicates` নামক গাণিতিক সূত্র হিসাবে উপস্থাপিত হয়। ধারণাগতভাবে, এই কৌশলটি দুটি ধাপে পাথ প্রেডিকেটগুলির উপর কাজ করে: + +1. এগুলি প্রোগ্রামের ইনপুটের উপর সীমাবদ্ধতা ব্যবহার করে তৈরি করা হয়। +2. এগুলি এমন প্রোগ্রাম ইনপুট তৈরি করতে ব্যবহৃত হয় যা সংশ্লিষ্ট পাথগুলিকে এক্সিকিউট করাবে। + +এই পদ্ধতিটি কোনো ফলস পজিটিভ তৈরি করে না, এই অর্থে যে সমস্ত চিহ্নিত প্রোগ্রাম স্টেট কংক্রিট এক্সিকিউশনের সময় ট্রিগার করা যেতে পারে। উদাহরণস্বরূপ, যদি অ্যানালিসিস একটি ইন্টিজার ওভারফ্লো খুঁজে পায়, তবে এটি পুনরুৎপাদনযোগ্য হওয়ার নিশ্চয়তা দেওয়া হয়। + +### পাথ প্রেডিকেটের উদাহরণ {#path-predicate-example} + +DSE কীভাবে কাজ করে তার একটি ধারণা পেতে, নিম্নলিখিত উদাহরণটি বিবেচনা করুন: + +```solidity +function f(uint a){ + + if (a == 65) { + // একটি বাগ উপস্থিত + } + +} +``` + +যেহেতু `f()`-এ দুটি পাথ রয়েছে, একটি DSE দুটি ভিন্ন পাথ প্রেডিকেট তৈরি করবে: + +- পাথ 1: `a == 65` +- পাথ 2: `Not (a == 65)` + +প্রতিটি পাথ প্রেডিকেট একটি গাণিতিক সূত্র যা একটি তথাকথিত [SMT solver](https://wikipedia.org/wiki/Satisfiability_modulo_theories)-কে দেওয়া যেতে পারে, যা সমীকরণটি সমাধান করার চেষ্টা করবে। `পাথ 1`-এর জন্য, সলভার বলবে যে `a = 65` দিয়ে পাথটি এক্সপ্লোর করা যাবে। `পাথ 2`-এর জন্য, সলভার `a`-কে 65 ছাড়া অন্য যেকোনো মান দিতে পারে, উদাহরণস্বরূপ `a = 0`। + +### প্রপার্টি যাচাই করা {#verifying-properties} + +Manticore প্রতিটি পাথের সমস্ত এক্সিকিউশনের উপর সম্পূর্ণ নিয়ন্ত্রণ দেয়। ফলস্বরূপ, এটি আপনাকে প্রায় যেকোনো কিছুতে যথেচ্ছ সীমাবদ্ধতা যোগ করতে দেয়। এই নিয়ন্ত্রণটি কন্ট্র্যাক্টে প্রপার্টি তৈরি করার অনুমতি দেয়। + +নিম্নলিখিত উদাহরণটি বিবেচনা করুন: + +```solidity +function unsafe_add(uint a, uint b) returns(uint c){ + c = a + b; // কোনো ওভারফ্লো সুরক্ষা নেই + return c; +} +``` + +এখানে ফাংশনটিতে এক্সপ্লোর করার জন্য কেবল একটি পাথ রয়েছে: + +- পাথ 1: `c = a + b` + +Manticore ব্যবহার করে, আপনি ওভারফ্লো পরীক্ষা করতে পারেন এবং পাথ প্রেডিকেটে সীমাবদ্ধতা যোগ করতে পারেন: + +- `c = a + b AND (c < a OR c < b)` + +যদি `a` এবং `b`-এর এমন একটি মূল্যায়ন খুঁজে পাওয়া সম্ভব হয় যার জন্য উপরের পাথ প্রেডিকেটটি সম্ভব, এর মানে হল আপনি একটি ওভারফ্লো খুঁজে পেয়েছেন। উদাহরণস্বরূপ সলভার `a = 10 , b = MAXUINT256` ইনপুটটি তৈরি করতে পারে। + +আপনি যদি একটি ফিক্সড সংস্করণ বিবেচনা করেন: + +```solidity +function safe_add(uint a, uint b) returns(uint c){ + c = a + b; + require(c>=a); + require(c>=b); + return c; +} +``` + +ওভারফ্লো পরীক্ষাসহ সংশ্লিষ্ট সূত্রটি হবে: + +- `c = a + b AND (c >= a) AND (c=>b) AND (c < a OR c < b)` + +এই সূত্রটি সমাধান করা যায় না; অন্য কথায় এটি একটি **প্রমাণ** যে `safe_add`-এ `c` সর্বদা বৃদ্ধি পাবে। + +সুতরাং DSE একটি শক্তিশালী টুল, যা আপনার কোডে যথেচ্ছ সীমাবদ্ধতা যাচাই করতে পারে। + +## Manticore-এর অধীনে চালানো {#running-under-manticore} + +আমরা দেখব কিভাবে Manticore API দিয়ে একটি স্মার্ট কন্ট্র্যাক্ট এক্সপ্লোর করতে হয়। লক্ষ্য হল নিম্নলিখিত স্মার্ট কন্ট্র্যাক্ট [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol): + +```solidity +pragma solidity >=0.4.24 <0.6.0; + +contract Simple { + function f(uint a) payable public{ + if (a == 65) { + revert(); + } + } +} +``` + +### একটি স্বতন্ত্র এক্সপ্লোরেশন চালান {#run-a-standalone-exploration} + +আপনি নিম্নলিখিত কমান্ড দ্বারা সরাসরি স্মার্ট কন্ট্র্যাক্টে Manticore চালাতে পারেন (`প্রজেক্ট` একটি সলিডিটি ফাইল, বা একটি প্রজেক্ট ডিরেক্টরি হতে পারে): + +```bash +$ manticore project +``` + +আপনি এইরকম টেস্টকেসগুলির আউটপুট পাবেন (ক্রম পরিবর্তন হতে পারে): + +``` +... +... m.c.manticore:INFO: Generated testcase No. 0 - STOP +... m.c.manticore:INFO: Generated testcase No. 1 - REVERT +... m.c.manticore:INFO: Generated testcase No. 2 - RETURN +... m.c.manticore:INFO: Generated testcase No. 3 - REVERT +... m.c.manticore:INFO: Generated testcase No. 4 - STOP +... m.c.manticore:INFO: Generated testcase No. 5 - REVERT +... m.c.manticore:INFO: Generated testcase No. 6 - REVERT +... m.c.manticore:INFO: Results in /home/ethsec/workshops/Automated Smart Contracts Audit - TruffleCon 2018/manticore/examples/mcore_t6vi6ij3 +... +``` + +অতিরিক্ত তথ্য ছাড়া, Manticore নতুন সিম্বলিক +লেনদেনের মাধ্যমে কন্ট্র্যাক্টটি অন্বেষণ করবে যতক্ষণ না এটি কন্ট্র্যাক্টে নতুন পথ অন্বেষণ করা বন্ধ করে। Manticore একটি ব্যর্থ লেনদেনের পরে নতুন লেনদেন চালায় না (যেমন: একটি রিভার্টের পরে)। + +Manticore একটি `mcore_*` ডিরেক্টরিতে তথ্য আউটপুট করবে। অন্যান্য জিনিসের মধ্যে, আপনি এই ডিরেক্টরিতে পাবেন: + +- `global.summary`: কভারেজ এবং কম্পাইলার সতর্কতা +- `test_XXXXX.summary`: কভারেজ, শেষ নির্দেশনা, প্রতি টেস্ট কেস অনুযায়ী অ্যাকাউন্ট ব্যালেন্স +- `test_XXXXX.tx`: প্রতি টেস্ট কেস অনুযায়ী লেনদেনের বিস্তারিত তালিকা + +এখানে Manticore ৭টি টেস্ট কেস খুঁজে পেয়েছে, যা নিম্নরূপ (ফাইলের নামের ক্রম পরিবর্তন হতে পারে): + +| | লেনদেন 0 | লেনদেন 1 | লেনদেন 2 | ফলাফল | +| :-------------------------------------------------------: | :---------------: | :------------------------: | -------------------------- | :----: | +| **test_00000000.tx** | কন্ট্র্যাক্ট তৈরি | f(!=65) | f(!=65) | STOP | +| **test_00000001.tx** | কন্ট্র্যাক্ট তৈরি | ফলব্যাক ফাংশন | | REVERT | +| **test_00000002.tx** | কন্ট্র্যাক্ট তৈরি | | | RETURN | +| **test_00000003.tx** | কন্ট্র্যাক্ট তৈরি | f(65) | | REVERT | +| **test_00000004.tx** | কন্ট্র্যাক্ট তৈরি | f(!=65) | | STOP | +| **test_00000005.tx** | কন্ট্র্যাক্ট তৈরি | f(!=65) | f(65) | REVERT | +| **test_00000006.tx** | কন্ট্র্যাক্ট তৈরি | f(!=65) | ফলব্যাক ফাংশন | REVERT | + +_এক্সপ্লোরেশন সারাংশ f(!=65) বোঝায় যে f-কে 65 থেকে ভিন্ন যেকোনো মান দিয়ে কল করা হয়েছে।_ + +আপনি যেমন লক্ষ্য করতে পারেন, Manticore প্রতিটি সফল বা রিভার্ট হওয়া লেনদেনের জন্য একটি অনন্য টেস্ট কেস তৈরি করে। + +আপনি যদি দ্রুত কোড এক্সপ্লোরেশন চান তবে `--quick-mode` ফ্ল্যাগটি ব্যবহার করুন (এটি বাগ ডিটেক্টর, গ্যাস গণনা ইত্যাদি নিষ্ক্রিয় করে) + +### API-এর মাধ্যমে একটি স্মার্ট কন্ট্র্যাক্ট ম্যানিপুলেট করা {#manipulate-a-smart-contract-through-the-api} + +এই বিভাগে Manticore Python API-এর মাধ্যমে কীভাবে একটি স্মার্ট কন্ট্র্যাক্ট ম্যানিপুলেট করতে হয় তা বিস্তারিতভাবে বর্ণনা করা হয়েছে। আপনি `*.py` এক্সটেনশন সহ নতুন ফাইল তৈরি করতে পারেন এবং এই ফাইলে API কমান্ডগুলি (যার মূল বিষয়গুলি নীচে বর্ণনা করা হবে) যোগ করে প্রয়োজনীয় কোড লিখতে পারেন এবং তারপরে `$ python3 *.py` কমান্ড দিয়ে এটি চালাতে পারেন। এছাড়াও আপনি নীচের কমান্ডগুলি সরাসরি পাইথন কনসোলে এক্সিকিউট করতে পারেন, কনসোলটি চালানোর জন্য `$ python3` কমান্ডটি ব্যবহার করুন। + +### অ্যাকাউন্ট তৈরি করা {#creating-accounts} + +আপনার প্রথম যা করা উচিত তা হল নিম্নলিখিত কমান্ডগুলি দিয়ে একটি নতুন ব্লকচেইন শুরু করা: + +```python +from manticore.ethereum import ManticoreEVM + +m = ManticoreEVM() +``` + +একটি নন-কন্ট্র্যাক্ট অ্যাকাউন্ট [m.create_account](https://manticore.readthedocs.io/en/latest/evm.html?highlight=create_account#manticore.ethereum.ManticoreEVM.create_account) ব্যবহার করে তৈরি করা হয়: + +```python +user_account = m.create_account(balance=1000) +``` + +একটি সলিডিটি কন্ট্র্যাক্ট [m.solidity_create_contract](https://manticore.readthedocs.io/en/latest/evm.html?highlight=solidity_create#manticore.ethereum.ManticoreEVM.create_contract) ব্যবহার করে ডিপ্লয় করা যেতে পারে: + +```solidity +source_code = ''' +pragma solidity >=0.4.24 <0.6.0; +contract Simple { + function f(uint a) payable public{ + if (a == 65) { + revert(); + } + } +} +''' +# কন্ট্র্যাক্টটি শুরু করুন +contract_account = m.solidity_create_contract(source_code, owner=user_account) +``` + +#### সারসংক্ষেপ {#summary} + +- আপনি [m.create_account](https://manticore.readthedocs.io/en/latest/evm.html?highlight=create_account#manticore.ethereum.ManticoreEVM.create_account) এবং [m.solidity_create_contract](https://manticore.readthedocs.io/en/latest/evm.html?highlight=solidity_create#manticore.ethereum.ManticoreEVM.create_contract) দিয়ে ব্যবহারকারী এবং কন্ট্র্যাক্ট অ্যাকাউন্ট তৈরি করতে পারেন। + +### লেনদেন এক্সিকিউট করা {#executing-transactions} + +Manticore দুই ধরনের লেনদেন সমর্থন করে: + +- র' লেনদেন: সমস্ত ফাংশন এক্সপ্লোর করা হয় +- নামযুক্ত লেনদেন: শুধুমাত্র একটি ফাংশন এক্সপ্লোর করা হয় + +#### র' লেনদেন {#raw-transaction} + +একটি র' লেনদেন [m.transaction](https://manticore.readthedocs.io/en/latest/evm.html?highlight=transaction#manticore.ethereum.ManticoreEVM.transaction) ব্যবহার করে এক্সিকিউট করা হয়: + +```python +m.transaction(caller=user_account, + address=contract_account, + data=data, + value=value) +``` + +লেনদেনের কলার, ঠিকানা, ডেটা, বা মান কংক্রিট বা সিম্বলিক উভয়ই হতে পারে: + +- [m.make_symbolic_value](https://manticore.readthedocs.io/en/latest/evm.html?highlight=make_symbolic_value#manticore.ethereum.ManticoreEVM.make_symbolic_value) একটি সিম্বলিক মান তৈরি করে। +- [m.make_symbolic_buffer(size)](https://manticore.readthedocs.io/en/latest/evm.html?highlight=make_symbolic_buffer#manticore.ethereum.ManticoreEVM.make_symbolic_buffer) একটি সিম্বলিক বাইট অ্যারে তৈরি করে। + +উদাহরণস্বরূপ: + +```python +symbolic_value = m.make_symbolic_value() +symbolic_data = m.make_symbolic_buffer(320) +m.transaction(caller=user_account, + address=contract_address, + data=symbolic_data, + value=symbolic_value) +``` + +যদি ডেটা সিম্বলিক হয়, Manticore লেনদেন এক্সিকিউশনের সময় কন্ট্র্যাক্টের সমস্ত ফাংশন এক্সপ্লোর করবে। ফাংশন সিলেকশন কীভাবে কাজ করে তা বোঝার জন্য [Hands on the Ethernaut CTF](https://blog.trailofbits.com/2017/11/06/hands-on-the-ethernaut-ctf/) নিবন্ধে ফলব্যাক ফাংশনের ব্যাখ্যা দেখা সহায়ক হবে। + +#### নামযুক্ত লেনদেন {#named-transaction} + +ফাংশনগুলি তাদের নামের মাধ্যমে এক্সিকিউট করা যেতে পারে। +`f(uint var)`-কে একটি সিম্বলিক মান দিয়ে, user_account থেকে, এবং 0 ইথার সহ এক্সিকিউট করতে, ব্যবহার করুন: + +```python +symbolic_var = m.make_symbolic_value() +contract_account.f(symbolic_var, caller=user_account, value=0) +``` + +যদি লেনদেনের `value` নির্দিষ্ট করা না থাকে, তবে এটি ডিফল্টরূপে 0 হয়। + +#### সারাংশ {#summary-1} + +- একটি লেনদেনের আর্গুমেন্ট কংক্রিট বা সিম্বলিক হতে পারে +- একটি র' লেনদেন সমস্ত ফাংশন এক্সপ্লোর করবে +- ফাংশনকে তাদের নাম দিয়ে কল করা যেতে পারে + +### ওয়ার্কস্পেস {#workspace} + +`m.workspace` হল জেনারেট করা সমস্ত ফাইলের জন্য আউটপুট ডিরেক্টরি হিসাবে ব্যবহৃত ডিরেক্টরি: + +```python +print("Results are in {}".format(m.workspace)) +``` + +### এক্সপ্লোরেশন বন্ধ করুন {#terminate-the-exploration} + +এক্সপ্লোরেশন থামাতে [m.finalize()](https://manticore.readthedocs.io/en/latest/evm.html?highlight=finalize#manticore.ethereum.ManticoreEVM.finalize) ব্যবহার করুন। এই পদ্ধতিটি কল করার পরে আর কোনো লেনদেন পাঠানো উচিত নয় এবং Manticore প্রতিটি এক্সপ্লোর করা পাথের জন্য টেস্ট কেস তৈরি করে। + +### সারাংশ: Manticore-এর অধীনে চালানো {#summary-running-under-manticore} + +পূর্ববর্তী সমস্ত ধাপ একসাথে রাখলে, আমরা পাই: + +```python +from manticore.ethereum import ManticoreEVM + +m = ManticoreEVM() + +with open('example.sol') as f: + source_code = f.read() + +user_account = m.create_account(balance=1000) +contract_account = m.solidity_create_contract(source_code, owner=user_account) + +symbolic_var = m.make_symbolic_value() +contract_account.f(symbolic_var) + +print("Results are in {}".format(m.workspace)) +m.finalize() # এক্সপ্লোরেশন বন্ধ করুন +``` + +উপরের সমস্ত কোড আপনি [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) এর মধ্যে খুঁজে পেতে পারেন + +## থ্রোয়িং পাথ পাওয়া {#getting-throwing-paths} + +আমরা এখন `f()`-এ একটি এক্সেপশন উত্থাপনকারী পাথগুলির জন্য নির্দিষ্ট ইনপুট তৈরি করব। লক্ষ্য এখনও নিম্নলিখিত স্মার্ট কন্ট্র্যাক্ট [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol): + +```solidity +pragma solidity >=0.4.24 <0.6.0; +contract Simple { + function f(uint a) payable public{ + if (a == 65) { + revert(); + } + } +} +``` + +### স্টেটের তথ্য ব্যবহার করা {#using-state-information} + +প্রতিটি এক্সিকিউট করা পাথের নিজস্ব ব্লকচেইনের স্টেট রয়েছে। একটি স্টেট হয় রেডি থাকে অথবা কিল হয়ে যায়, যার অর্থ এটি একটি THROW বা REVERT নির্দেশনায় পৌঁছেছে: + +- [m.ready_states](https://manticore.readthedocs.io/en/latest/states.html#accessing): রেডি থাকা স্টেটগুলির তালিকা (তারা একটি REVERT/INVALID এক্সিকিউট করেনি) +- [m.killed_states](https://manticore.readthedocs.io/en/latest/states.html#accessings): কিল হয়ে যাওয়া স্টেটগুলির তালিকা +- [m.all_states](https://manticore.readthedocs.io/en/latest/states.html#accessings): সমস্ত স্টেট + +```python +for state in m.all_states: + # স্টেট নিয়ে কিছু করুন +``` + +আপনি স্টেটের তথ্য অ্যাক্সেস করতে পারেন। উদাহরণস্বরূপ: + +- `state.platform.get_balance(account.address)`: অ্যাকাউন্টের ব্যালেন্স +- `state.platform.transactions`: লেনদেনের তালিকা +- `state.platform.transactions[-1].return_data`: শেষ লেনদেন দ্বারা ফেরত দেওয়া ডেটা + +শেষ লেনদেন দ্বারা ফেরত দেওয়া ডেটা একটি অ্যারে, যা ABI.deserialize দিয়ে একটি মানে রূপান্তরিত করা যেতে পারে, উদাহরণস্বরূপ: + +```python +data = state.platform.transactions[0].return_data +data = ABI.deserialize("uint", data) +``` + +### কীভাবে টেস্টকেস জেনারেট করবেন {#how-to-generate-testcase} + +টেস্টকেস জেনারেট করতে [m.generate_testcase(state, name)](https://manticore.readthedocs.io/en/latest/evm.html?highlight=generate_testcase#manticore.ethereum.ManticoreEVM.generate_testcase) ব্যবহার করুন: + +```python +m.generate_testcase(state, 'BugFound') +``` + +### সারাংশ {#summary-2} + +- আপনি m.all_states দিয়ে স্টেটের উপর ইটারেট করতে পারেন +- `state.platform.get_balance(account.address)` অ্যাকাউন্টের ব্যালেন্স ফেরত দেয় +- `state.platform.transactions` লেনদেনের তালিকা ফেরত দেয় +- `transaction.return_data` হল ফেরত দেওয়া ডেটা +- `m.generate_testcase(state, name)` স্টেটের জন্য ইনপুট জেনারেট করে + +### সারাংশ: থ্রোয়িং পাথ পাওয়া {#summary-getting-throwing-path} + +```python +from manticore.ethereum import ManticoreEVM + +m = ManticoreEVM() + +with open('example.sol') as f: + source_code = f.read() + +user_account = m.create_account(balance=1000) +contract_account = m.solidity_create_contract(source_code, owner=user_account) + +symbolic_var = m.make_symbolic_value() +contract_account.f(symbolic_var) + +## একটি এক্সিকিউশন REVERT বা INVALID দিয়ে শেষ হয় কিনা তা পরীক্ষা করুন + +for state in m.terminated_states: + last_tx = state.platform.transactions[-1] + if last_tx.result in ['REVERT', 'INVALID']: + print('Throw found {}'.format(m.workspace)) + m.generate_testcase(state, 'ThrowFound') +``` + +উপরের সমস্ত কোড আপনি [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) এর মধ্যে খুঁজে পেতে পারেন + +_দ্রষ্টব্য আমরা একটি অনেক সহজ স্ক্রিপ্ট তৈরি করতে পারতাম, কারণ terminated_state দ্বারা ফেরত দেওয়া সমস্ত স্টেটের ফলাফলে REVERT বা INVALID থাকে: এই উদাহরণটি শুধুমাত্র API কীভাবে ম্যানিপুলেট করতে হয় তা প্রদর্শনের জন্য ছিল।_ + +## সীমাবদ্ধতা যোগ করা {#adding-constraints} + +আমরা দেখব কীভাবে এক্সপ্লোরেশনকে সীমাবদ্ধ করা যায়। আমরা এই ধারণাটি ধরে নেব যে `f()`-এর +ডকুমেন্টেশন অনুযায়ী ফাংশনটি কখনও `a == 65` দিয়ে কল করা হয় না, তাই `a == 65` সহ যেকোনো বাগ একটি আসল বাগ নয়। লক্ষ্য এখনও নিম্নলিখিত স্মার্ট কন্ট্র্যাক্ট [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol): + +```solidity +pragma solidity >=0.4.24 <0.6.0; +contract Simple { + function f(uint a) payable public{ + if (a == 65) { + revert(); + } + } +} +``` + +### অপারেটর {#operators} + +[অপারেটর](https://github.com/trailofbits/manticore/blob/master/manticore/core/smtlib/operators.py) মডিউলটি সীমাবদ্ধতার ম্যানিপুলেশনকে সহজ করে, অন্যান্য জিনিসের মধ্যে এটি প্রদান করে: + +- Operators.AND, +- Operators.OR, +- Operators.UGT (আনসাইন্ড গ্রেটার দ্যান), +- Operators.UGE (আনসাইন্ড গ্রেটার দ্যান অর ইকুয়াল টু), +- Operators.ULT (আনসাইন্ড লোয়ার দ্যান), +- Operators.ULE (আনসাইন্ড লোয়ার দ্যান অর ইকুয়াল টু)। + +মডিউলটি ইম্পোর্ট করতে নিম্নলিখিতটি ব্যবহার করুন: + +```python +from manticore.core.smtlib import Operators +``` + +`Operators.CONCAT` একটি অ্যারের সাথে একটি মান কনক্যাটেনেট করতে ব্যবহৃত হয়। উদাহরণস্বরূপ, একটি লেনদেনের return_data-কে অন্য একটি মানের সাথে পরীক্ষা করার জন্য একটি মানে পরিবর্তন করতে হবে: + +```python +last_return = Operators.CONCAT(256, *last_return) +``` + +### সীমাবদ্ধতা {#state-constraint} + +আপনি বিশ্বব্যাপী বা একটি নির্দিষ্ট স্টেটের জন্য সীমাবদ্ধতা ব্যবহার করতে পারেন। + +#### গ্লোবাল সীমাবদ্ধতা {#state-constraint} + +একটি গ্লোবাল সীমাবদ্ধতা যোগ করতে `m.constrain(constraint)` ব্যবহার করুন। +উদাহরণস্বরূপ, আপনি একটি সিম্বলিক ঠিকানা থেকে একটি কন্ট্র্যাক্ট কল করতে পারেন, এবং এই ঠিকানাটিকে নির্দিষ্ট মানে সীমাবদ্ধ করতে পারেন: + +```python +symbolic_address = m.make_symbolic_value() +m.constraint(Operators.OR(symbolic == 0x41, symbolic_address == 0x42)) +m.transaction(caller=user_account, + address=contract_account, + data=m.make_symbolic_buffer(320), + value=0) +``` + +#### স্টেট সীমাবদ্ধতা {#state-constraint} + +একটি নির্দিষ্ট স্টেটে সীমাবদ্ধতা যোগ করতে [state.constrain(constraint)](https://manticore.readthedocs.io/en/latest/states.html?highlight=StateBase#manticore.core.state.StateBase.constrain) ব্যবহার করুন। +এটি এক্সপ্লোরেশনের পরে স্টেটের উপর কিছু প্রপার্টি পরীক্ষা করার জন্য স্টেটকে সীমাবদ্ধ করতে ব্যবহার করা যেতে পারে। + +### সীমাবদ্ধতা পরীক্ষা করা {#checking-constraint} + +একটি সীমাবদ্ধতা এখনও সম্ভব কিনা তা জানতে `solver.check(state.constraints)` ব্যবহার করুন। +উদাহরণস্বরূপ, নিম্নলিখিতটি symbolic_value-কে 65 থেকে ভিন্ন হতে সীমাবদ্ধ করবে এবং স্টেটটি এখনও সম্ভব কিনা তা পরীক্ষা করবে: + +```python +state.constrain(symbolic_var != 65) +if solver.check(state.constraints): + # স্টেটটি সম্ভব +``` + +### সারাংশ: সীমাবদ্ধতা যোগ করা {#summary-adding-constraints} + +পূর্ববর্তী কোডে সীমাবদ্ধতা যোগ করে, আমরা পাই: + +```python +from manticore.ethereum import ManticoreEVM +from manticore.core.smtlib.solver import Z3Solver + +solver = Z3Solver.instance() + +m = ManticoreEVM() + +with open("example.sol") as f: + source_code = f.read() + +user_account = m.create_account(balance=1000) +contract_account = m.solidity_create_contract(source_code, owner=user_account) + +symbolic_var = m.make_symbolic_value() +contract_account.f(symbolic_var) + +no_bug_found = True + +## একটি এক্সিকিউশন REVERT বা INVALID দিয়ে শেষ হয় কিনা তা পরীক্ষা করুন + +for state in m.terminated_states: + last_tx = state.platform.transactions[-1] + if last_tx.result in ['REVERT', 'INVALID']: + # আমরা সেই পাথ বিবেচনা করি না যেখানে a == 65 + condition = symbolic_var != 65 + if m.generate_testcase(state, name="BugFound", only_if=condition): + print(f'বাগ পাওয়া গেছে, ফলাফলগুলো {m.workspace}-এ আছে') + no_bug_found = False + +if no_bug_found: + print(f'কোনো বাগ পাওয়া যায়নি') +``` + +উপরের সমস্ত কোড আপনি [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) এর মধ্যে খুঁজে পেতে পারেন diff --git a/public/content/translations/bn/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md b/public/content/translations/bn/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md new file mode 100644 index 00000000000..b65980a88d9 --- /dev/null +++ b/public/content/translations/bn/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md @@ -0,0 +1,239 @@ +--- +title: "স্মার্ট কন্ট্র্যাক্ট বাগ খুঁজে বের করতে কীভাবে স্লিদার ব্যবহার করবেন" +description: "স্মার্ট কন্ট্র্যাক্টে স্বয়ংক্রিয়ভাবে বাগ খুঁজে পেতে কীভাবে স্লিদার ব্যবহার করবেন" +author: Trailofbits +lang: bn +tags: + [ + "সলিডিটি", + "স্মার্ট কন্ট্র্যাক্ট", + "নিরাপত্তা", + "পরীক্ষা" + ] +skill: advanced +published: 2020-06-09 +source: Building secure contracts +sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/slither +--- + +## কীভাবে স্লিদার ব্যবহার করবেন {#how-to-use-slither} + +এই টিউটোরিয়ালের উদ্দেশ্য হলো স্মার্ট কন্ট্র্যাক্টে স্বয়ংক্রিয়ভাবে বাগ খুঁজে বের করতে কীভাবে স্লিদার ব্যবহার করা যায় তা দেখানো। + +- [ইনস্টলেশন](#installation) +- [কমান্ড লাইন ব্যবহার](#command-line) +- [স্ট্যাটিক অ্যানালাইসিসের ভূমিকা](#static-analysis): স্ট্যাটিক অ্যানালাইসিসের সংক্ষিপ্ত ভূমিকা +- [API](#api-basics): পাইথন API-এর বর্ণনা + +## ইনস্টলেশন {#installation} + +স্লিদারের জন্য পাইথন >= 3.6 প্রয়োজন। এটি pip-এর মাধ্যমে বা docker ব্যবহার করে ইনস্টল করা যেতে পারে। + +pip-এর মাধ্যমে স্লিদার: + +```bash +pip3 install --user slither-analyzer +``` + +ডকারের মাধ্যমে স্লিদার: + +```bash +docker pull trailofbits/eth-security-toolbox +docker run -it -v "$PWD":/home/trufflecon trailofbits/eth-security-toolbox +``` + +_শেষ কমান্ডটি একটি docker-এ eth-security-toolbox চালায় যেটির আপনার বর্তমান ডিরেক্টরিতে অ্যাক্সেস আছে। আপনি আপনার হোস্ট থেকে ফাইলগুলো পরিবর্তন করতে পারেন, এবং docker থেকে ফাইলগুলোতে টুলস চালাতে পারেন_ + +docker-এর ভিতরে, চালান: + +```bash +solc-select 0.5.11 +cd /home/trufflecon/ +``` + +### একটি স্ক্রিপ্ট চালানো {#running-a-script} + +python 3 দিয়ে একটি python স্ক্রিপ্ট চালাতে: + +```bash +python3 script.py +``` + +### কমান্ড লাইন {#command-line} + +**কমান্ড লাইন বনাম ব্যবহারকারী-সংজ্ঞায়িত স্ক্রিপ্ট।** স্লিদার পূর্ব-নির্ধারিত ডিটেক্টরের একটি সেট নিয়ে আসে যা অনেক সাধারণ বাগ খুঁজে পায়। কমান্ড লাইন থেকে স্লিদার কল করলে সমস্ত ডিটেক্টর চলবে, স্ট্যাটিক অ্যানালাইসিস সম্পর্কে বিস্তারিত জ্ঞানের প্রয়োজন নেই: + +```bash +slither project_paths +``` + +ডিটেক্টর ছাড়াও, স্লিদারের [প্রিন্টার](https://github.com/crytic/slither#printers) এবং [টুলস](https://github.com/crytic/slither#tools)-এর মাধ্যমে কোড পর্যালোচনার ক্ষমতা রয়েছে। + +প্রাইভেট ডিটেক্টর এবং GitHub ইন্টিগ্রেশনে অ্যাক্সেস পেতে [crytic.io](https://github.com/crytic) ব্যবহার করুন। + +## স্ট্যাটিক বিশ্লেষণ {#static-analysis} + +স্লিদার স্ট্যাটিক অ্যানালাইসিস ফ্রেমওয়ার্কের ক্ষমতা এবং ডিজাইন ব্লগ পোস্টে ([1](https://blog.trailofbits.com/2018/10/19/slither-a-solidity-static-analysis-framework/), [2](https://blog.trailofbits.com/2019/05/27/slither-the-leading-static-analyzer-for-smart-contracts/)) এবং একটি [একাডেমিক পেপারে](https://github.com/trailofbits/publications/blob/master/papers/wetseb19.pdf) বর্ণনা করা হয়েছে। + +স্ট্যাটিক অ্যানালাইসিস বিভিন্ন ধরনের হয়ে থাকে। আপনি সম্ভবত বুঝতে পারছেন যে [clang](https://clang-analyzer.llvm.org/) এবং [gcc](https://lwn.net/Articles/806099/)-এর মতো কম্পাইলারগুলি এই গবেষণা কৌশলগুলির উপর নির্ভর করে, কিন্তু এটি ([Infer](https://fbinfer.com/), [CodeClimate](https://codeclimate.com/), [FindBugs](http://findbugs.sourceforge.net/) এবং ফর্মাল পদ্ধতির উপর ভিত্তি করে [Frama-C](https://frama-c.com/) এবং [Polyspace](https://www.mathworks.com/products/polyspace.html) -এর মতো টুলসগুলিকেও ভিত্তি করে। + +আমরা এখানে স্ট্যাটিক অ্যানালাইসিস কৌশল এবং গবেষকদের পুঙ্খানুপুঙ্খভাবে পর্যালোচনা করব না। পরিবর্তে, আমরা স্লিদার কীভাবে কাজ করে তা বোঝার জন্য যা প্রয়োজন তার উপর ফোকাস করব যাতে আপনি বাগ খুঁজে পেতে এবং কোড বুঝতে এটি আরও কার্যকরভাবে ব্যবহার করতে পারেন। + +- [কোড রিপ্রেজেন্টেশন](#code-representation) +- [কোড অ্যানালাইসিস](#analysis) +- [ইন্টারমিডিয়েট রিপ্রেজেন্টেশন](#intermediate-representation) + +### কোড রিপ্রেজেন্টেশন {#code-representation} + +ডাইনামিক অ্যানালাইসিসের বিপরীতে, যা একটি একক এক্সিকিউশন পথ সম্পর্কে যুক্তি দেয়, স্ট্যাটিক অ্যানালাইসিস একবারে সমস্ত পথ সম্পর্কে যুক্তি দেয়। এটি করার জন্য, এটি একটি ভিন্ন কোড রিপ্রেজেন্টেশনের উপর নির্ভর করে। দুটি সবচেয়ে সাধারণ হলো অ্যাবস্ট্র্যাক্ট সিনট্যাক্স ট্রি (AST) এবং কন্ট্রোল ফ্লো গ্রাফ (CFG)। + +### অ্যাবস্ট্র্যাক্ট সিনট্যাক্স ট্রি (AST) {#abstract-syntax-trees-ast} + +কম্পাইলার যখনই কোড পার্স করে তখনই AST ব্যবহার করা হয়। এটি সম্ভবত সবচেয়ে মৌলিক কাঠামো যার উপর স্ট্যাটিক অ্যানালাইসিস করা যেতে পারে। + +সংক্ষেপে, একটি AST হলো একটি স্ট্রাকচার্ড ট্রি যেখানে, সাধারণত, প্রতিটি লিফ একটি ভেরিয়েবল বা একটি কনস্ট্যান্ট ধারণ করে এবং ইন্টারনাল নোডগুলি হলো অপারেন্ড বা কন্ট্রোল ফ্লো অপারেশন। নিম্নলিখিত কোডটি বিবেচনা করুন: + +```solidity +function safeAdd(uint a, uint b) pure internal returns(uint){ + if(a + b <= a){ + revert(); + } + return a + b; +} +``` + +সংশ্লিষ্ট AST দেখানো হলো: + +![AST](./ast.png) + +স্লিদার solc দ্বারা এক্সপোর্ট করা AST ব্যবহার করে। + +তৈরি করা সহজ হলেও, AST একটি নেস্টেড কাঠামো। কখনও কখনও, এটি বিশ্লেষণ করার জন্য সবচেয়ে সহজবোধ্য নয়। উদাহরণস্বরূপ, `a + b <= a` এক্সপ্রেশন দ্বারা ব্যবহৃত অপারেশনগুলি সনাক্ত করতে, আপনাকে প্রথমে `<=` এবং তারপর `+` বিশ্লেষণ করতে হবে। একটি সাধারণ পদ্ধতি হলো তথাকথিত ভিজিটর প্যাটার্ন ব্যবহার করা, যা রিকার্সিভলি ট্রি-এর মাধ্যমে নেভিগেট করে। স্লিদার-এ [`ExpressionVisitor`](https://github.com/crytic/slither/blob/master/slither/visitors/expression/expression.py) -তে একটি জেনেরিক ভিজিটর রয়েছে। + +নিম্নলিখিত কোডটি এক্সপ্রেশনে একটি অ্যাডিশন আছে কিনা তা সনাক্ত করতে `ExpressionVisitor` ব্যবহার করে: + +```python +from slither.visitors.expression.expression import ExpressionVisitor +from slither.core.expressions.binary_operation import BinaryOperationType + +class HasAddition(ExpressionVisitor): + + def result(self): + return self._result + + def _post_binary_operation(self, expression): + if expression.type == BinaryOperationType.ADDITION: + self._result = True + +visitor = HasAddition(expression) # expression হলো সেই এক্সপ্রেশন যা পরীক্ষা করা হবে +print(f'এক্সপ্রেশন {expression}-এ একটি অ্যাডিশন আছে: {visitor.result()}') +``` + +### কন্ট্রোল ফ্লো গ্রাফ (CFG) {#control-flow-graph-cfg} + +দ্বিতীয় সবচেয়ে সাধারণ কোড রিপ্রেজেন্টেশন হলো কন্ট্রোল ফ্লো গ্রাফ (CFG)। এর নাম অনুসারে, এটি একটি গ্রাফ-ভিত্তিক রিপ্রেজেন্টেশন যা সমস্ত এক্সিকিউশন পথ প্রকাশ করে। প্রতিটি নোডে এক বা একাধিক ইন্সট্রাকশন থাকে। গ্রাফের এজগুলি কন্ট্রোল ফ্লো অপারেশন (if/then/else, loop, ইত্যাদি) উপস্থাপন করে। আমাদের আগের উদাহরণের CFG হলো: + +![CFG](./cfg.png) + +CFG হলো সেই রিপ্রেজেন্টেশন যার উপর ভিত্তি করে বেশিরভাগ অ্যানালাইসিস তৈরি করা হয়। + +আরও অনেক কোড রিপ্রেজেন্টেশন বিদ্যমান। আপনি যে অ্যানালাইসিস করতে চান সেই অনুযায়ী প্রতিটি রিপ্রেজেন্টেশনের সুবিধা এবং অসুবিধা রয়েছে। + +### অ্যানালাইসিস {#analysis} + +স্লিদার দিয়ে আপনি যে সবচেয়ে সহজ ধরনের অ্যানালাইসিস করতে পারেন তা হলো সিনট্যাকটিক অ্যানালাইসিস। + +### সিনট্যাক্স অ্যানালাইসিস {#syntax-analysis} + +স্লিদার কোডের বিভিন্ন উপাদান এবং তাদের রিপ্রেজেন্টেশনের মাধ্যমে নেভিগেট করে প্যাটার্ন ম্যাচিং-এর মতো একটি পদ্ধতি ব্যবহার করে অসামঞ্জস্যতা এবং ত্রুটি খুঁজে বের করতে পারে। + +উদাহরণস্বরূপ, নিম্নলিখিত ডিটেক্টরগুলি সিনট্যাক্স-সম্পর্কিত সমস্যাগুলি খুঁজে বের করে: + +- [স্টেট ভেরিয়েবল শ্যাডোইং](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing): সমস্ত স্টেট ভেরিয়েবলের উপর ইটারেট করে এবং ইনহেরিট করা কন্ট্র্যাক্টের কোনো ভেরিয়েবলকে শ্যাডো করছে কিনা তা পরীক্ষা করে ([state.py#L51-L62](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/shadowing/state.py#L51-L62)) + +- [ভুল ERC20 ইন্টারফেস](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface): ভুল ERC20 ফাংশন সিগনেচার খুঁজে বের করে ([incorrect_erc20_interface.py#L34-L55](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/erc/incorrect_erc20_interface.py#L34-L55)) + +### সেমান্টিক অ্যানালাইসিস {#semantic-analysis} + +সিনট্যাক্স অ্যানালাইসিসের বিপরীতে, একটি সেমান্টিক অ্যানালাইসিস আরও গভীরে গিয়ে কোডের “অর্থ” বিশ্লেষণ করে। এই পরিবারে কিছু বিস্তৃত ধরনের অ্যানালাইসিস অন্তর্ভুক্ত রয়েছে। এগুলো আরও শক্তিশালী এবং দরকারী ফলাফলের দিকে নিয়ে যায়, কিন্তু এগুলো লেখা আরও জটিল। + +সবচেয়ে উন্নত দুর্বলতা সনাক্তকরণের জন্য সেমান্টিক অ্যানালাইসিস ব্যবহার করা হয়। + +#### ডেটা ডিপেন্ডেন্সি অ্যানালাইসিস {#fixed-point-computation} + +একটি ভেরিয়েবল `variable_a`-কে `variable_b`-এর উপর ডেটা-ডিপেন্ডেন্ট বলা হয় যদি এমন একটি পথ থাকে যার জন্য `variable_a`-এর মান `variable_b` দ্বারা প্রভাবিত হয়। + +নিম্নলিখিত কোডে, `variable_a` `variable_b`-এর উপর নির্ভরশীল: + +```solidity +// ... +variable_a = variable_b + 1; +``` + +স্লিদার-এর বিল্ট-ইন [ডেটা ডিপেন্ডেন্সি](https://github.com/crytic/slither/wiki/data-dependency) ক্ষমতা রয়েছে, এর ইন্টারমিডিয়েট রিপ্রেজেন্টেশনের জন্য ধন্যবাদ (যা পরবর্তী বিভাগে আলোচনা করা হয়েছে)। + +ডেটা ডিপেন্ডেন্সি ব্যবহারের একটি উদাহরণ [বিপজ্জনক স্ট্রিক্ট ইকুয়ালিটি ডিটেক্টর](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities)-এ পাওয়া যাবে। এখানে স্লিদার একটি বিপজ্জনক মানের সাথে স্ট্রিক্ট ইকুয়ালিটি তুলনা খুঁজবে ([incorrect_strict_equality.py#L86-L87](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L86-L87)), এবং ব্যবহারকারীকে জানাবে যে `==`-এর পরিবর্তে `>=` বা `<=` ব্যবহার করা উচিত, যাতে একজন আক্রমণকারী কন্ট্র্যাক্টকে ফাঁদে ফেলতে না পারে। অন্যান্য বিষয়গুলির মধ্যে, ডিটেক্টর `balanceOf(address)`-এ একটি কলের রিটার্ন মানকে বিপজ্জনক হিসাবে বিবেচনা করবে ([incorrect_strict_equality.py#L63-L64](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L63-L64)), এবং এর ব্যবহার ট্র্যাক করতে ডেটা ডিপেন্ডেন্সি ইঞ্জিন ব্যবহার করবে। + +#### ফিক্সড-পয়েন্ট কম্পিউটেশন {#fixed-point-computation} + +যদি আপনার অ্যানালাইসিস CFG-এর মাধ্যমে নেভিগেট করে এবং এজগুলি অনুসরণ করে, তাহলে আপনি সম্ভবত ইতিমধ্যে ভিজিট করা নোডগুলি দেখতে পাবেন। উদাহরণস্বরূপ, যদি একটি লুপ নীচে দেখানো হিসাবে উপস্থাপন করা হয়: + +```solidity +for(uint i; i < range; ++){ + variable_a += 1 +} +``` + +আপনার অ্যানালাইসিসকে জানতে হবে কখন থামতে হবে। এখানে দুটি প্রধান কৌশল রয়েছে: (1) প্রতিটি নোডে একটি সসীম সংখ্যক বার ইটারেট করা, (2) একটি তথাকথিত _ফিক্সপয়েন্ট_ গণনা করা। একটি ফিক্সপয়েন্ট মূলত বোঝায় যে এই নোডটি বিশ্লেষণ করা কোনো অর্থপূর্ণ তথ্য প্রদান করে না। + +ফিক্সপয়েন্ট ব্যবহারের একটি উদাহরণ রিএন্ট্রেন্সি ডিটেক্টরে পাওয়া যায়: স্লিদার নোডগুলি এক্সপ্লোর করে, এবং এক্সটার্নাল কল, সংগ্রহস্থলে লেখা এবং পড়া খুঁজে বের করে। একবার এটি একটি ফিক্সপয়েন্টে পৌঁছালে ([reentrancy.py#L125-L131](https://github.com/crytic/slither/blob/master/slither/detectors/reentrancy/reentrancy.py#L125-L131)), এটি এক্সপ্লোরেশন বন্ধ করে দেয়, এবং বিভিন্ন রিএন্ট্রেন্সি প্যাটার্নের মাধ্যমে ([reentrancy_benign.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_benign.py), [reentrancy_read_before_write.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_read_before_write.py), [reentrancy_eth.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_eth.py)) একটি রিএন্ট্রেন্সি আছে কিনা তা দেখতে ফলাফল বিশ্লেষণ করে। + +কার্যকর ফিক্সড পয়েন্ট কম্পিউটেশন ব্যবহার করে অ্যানালাইসিস লেখার জন্য অ্যানালাইসিসটি কীভাবে তার তথ্য প্রচার করে সে সম্পর্কে একটি ভাল বোঝার প্রয়োজন। + +### ইন্টারমিডিয়েট রিপ্রেজেন্টেশন {#intermediate-representation} + +একটি ইন্টারমিডিয়েট রিপ্রেজেন্টেশন (IR) হলো এমন একটি ভাষা যা মূল ভাষার চেয়ে স্ট্যাটিক অ্যানালাইসিসের জন্য বেশি উপযোগী। স্লিদার Solidity-কে তার নিজস্ব IR: [SlithIR](https://github.com/crytic/slither/wiki/SlithIR)-এ অনুবাদ করে। + +আপনি যদি শুধুমাত্র বেসিক চেক লিখতে চান তবে SlithIR বোঝা প্রয়োজন নয়। তবে, আপনি যদি উন্নত সেমান্টিক অ্যানালাইসিস লেখার পরিকল্পনা করেন তবে এটি কাজে আসবে। [SlithIR](https://github.com/crytic/slither/wiki/Printer-documentation#slithir) এবং [SSA](https://github.com/crytic/slither/wiki/Printer-documentation#slithir-ssa) প্রিন্টারগুলি আপনাকে কোডটি কীভাবে অনুবাদ করা হয় তা বুঝতে সাহায্য করবে। + +## API বেসিকস {#api-basics} + +স্লিদারের একটি API আছে যা আপনাকে কন্ট্র্যাক্ট এবং তার ফাংশনগুলির মৌলিক বৈশিষ্ট্যগুলি এক্সপ্লোর করতে দেয়। + +একটি কোডবেস লোড করতে: + +```python +from slither import Slither +slither = Slither('/path/to/project') + +``` + +### কন্ট্র্যাক্ট এবং ফাংশন এক্সপ্লোর করা {#exploring-contracts-and-functions} + +একটি `Slither` অবজেক্টের আছে: + +- `contracts (list(Contract)`: কন্ট্র্যাক্টের তালিকা +- `contracts_derived (list(Contract)`: এমন কন্ট্র্যাক্টের তালিকা যা অন্য কোনো কন্ট্র্যাক্ট দ্বারা ইনহেরিট করা হয়নি (কন্ট্র্যাক্টের উপসেট) +- `get_contract_from_name (str)`: তার নাম থেকে একটি কন্ট্র্যাক্ট রিটার্ন করুন + +একটি `Contract` অবজেক্টের আছে: + +- `name (str)`: কন্ট্র্যাক্টের নাম +- `functions (list(Function))`: ফাংশনের তালিকা +- `modifiers (list(Modifier))`: মডিফায়ারের তালিকা +- `all_functions_called (list(Function/Modifier))`: কন্ট্র্যাক্ট দ্বারা পৌঁছানো যায় এমন সমস্ত ইন্টারনাল ফাংশনের তালিকা +- `inheritance (list(Contract))`: ইনহেরিটেড কন্ট্র্যাক্টের তালিকা +- `get_function_from_signature (str)`: তার সিগনেচার থেকে একটি ফাংশন রিটার্ন করুন +- `get_modifier_from_signature (str)`: তার সিগনেচার থেকে একটি মডিফায়ার রিটার্ন করুন +- `get_state_variable_from_name (str)`: তার নাম থেকে একটি StateVariable রিটার্ন করুন + +একটি `Function` বা একটি `Modifier` অবজেক্টের আছে: + +- `name (str)`: ফাংশনের নাম +- `contract (contract)`: যে কন্ট্র্যাক্টে ফাংশনটি ডিক্লেয়ার করা হয়েছে +- `nodes (list(Node))`: ফাংশন/মডিফায়ারের CFG গঠনকারী নোডগুলির তালিকা +- `entry_point (Node)`: CFG-এর এন্ট্রি পয়েন্ট +- `variables_read (list(Variable))`: পড়া ভেরিয়েবলের তালিকা +- `variables_written (list(Variable))`: লেখা ভেরিয়েবলের তালিকা +- `state_variables_read (list(StateVariable))`: পড়া স্টেট ভেরিয়েবলের তালিকা (variables`read-এর উপসেট) +- `state_variables_written (list(StateVariable))`: লেখা স্টেট ভেরিয়েবলের তালিকা (variables`written-এর উপসেট)