স্মার্ট কন্ট্রাক্টের গঠন
পেজ সর্বশেষ আপডেট: ২৩ ফেব্রুয়ারী, ২০২৬
একটি স্মার্ট কন্ট্রাক্ট হলো এমন একটি প্রোগ্রাম যা ইথেরিয়ামের একটি এডড্রেস-এ রান করে। এগুলো ডেটা এবং ফাংশন দিয়ে তৈরি যা কোনো লেনদেন গ্রহণ করার পর এক্সিকিউট হতে পারে। নিচে একটি স্মার্ট কন্ট্রাক্টের গঠনের সংক্ষিপ্ত বিবরণ দেওয়া হলো।
পূর্বশর্ত
প্রথমে নিশ্চিত করুন যে আপনি স্মার্ট কন্ট্রাক্ট সম্পর্কে পড়েছেন। এই ডকুমেন্টটি ধরে নেয় যে আপনি ইতিমধ্যেই JavaScript বা Python-এর মতো প্রোগ্রামিং ভাষার সাথে পরিচিত।
ডেটা
যেকোনো কন্ট্রাক্ট ডেটাকে অবশ্যই একটি লোকেশনে অ্যাসাইন করতে হবে: হয় storage অথবা memory-তে। একটি স্মার্ট কন্ট্রাক্টে স্টোরেজ পরিবর্তন করা ব্যয়বহুল, তাই আপনার ডেটা কোথায় থাকা উচিত তা বিবেচনা করা প্রয়োজন।
স্টোরেজ
স্থায়ী ডেটাকে স্টোরেজ বলা হয় এবং এটি স্টেট ভেরিয়েবল দ্বারা উপস্থাপন করা হয়। এই মানগুলো ব্লকচেইন-এ স্থায়ীভাবে সংরক্ষিত হয়। আপনাকে এর ধরন (type) ঘোষণা করতে হবে যাতে কম্পাইল করার সময় কন্ট্রাক্টটি ট্র্যাক করতে পারে যে ব্লকচেইন-এ এর কতটুকু স্টোরেজ প্রয়োজন।
1// সলিডিটি উদাহরণ2contract SimpleStorage {3 uint storedData; // স্টেট ভেরিয়েবল4 // ...5}1# ভাইপার উদাহরণ2storedData: int128আপনি যদি ইতিমধ্যেই অবজেক্ট-ওরিয়েন্টেড ভাষায় প্রোগ্রামিং করে থাকেন, তবে আপনি সম্ভবত বেশিরভাগ ধরনের (types) সাথেই পরিচিত হবেন। তবে, আপনি যদি Ethereum ডেভেলপমেন্টে নতুন হয়ে থাকেন, তাহলে address আপনার কাছে নতুন মনে হতে পারে।
একটি address টাইপ একটি ইথেরিয়াম এডড্রেস ধারণ করতে পারে যা 20 বাইট বা 160 বিটের সমান। এটি 0x দিয়ে শুরু হওয়া হেক্সাডেসিমাল নোটেশনে রিটার্ন করে।
অন্যান্য টাইপগুলোর মধ্যে রয়েছে:
- বুলিয়ান (boolean)
- ইন্টিজার (integer)
- ফিক্সড পয়েন্ট নাম্বার (fixed point numbers)
- ফিক্সড-সাইজ বাইট অ্যারে (fixed-size byte arrays)
- ডায়নামিকালি সাইজড বাইট অ্যারে (dynamically sized byte arrays)
- র্যাশনাল এবং ইন্টিজার লিটারেল (rational and integer literals)
- স্ট্রিং লিটারেল (string literals)
- হেক্সাডেসিমাল লিটারেল (hexadecimal literals)
- এনাম (enums)
আরও ব্যাখ্যার জন্য, ডক্সগুলো দেখুন:
মেমরি
যেসব মান শুধুমাত্র একটি কন্ট্রাক্ট ফাংশন এক্সিকিউট হওয়ার সময়কালের জন্য সংরক্ষিত থাকে, সেগুলোকে মেমরি ভেরিয়েবল বলা হয়। যেহেতু এগুলো ব্লকচেইন-এ স্থায়ীভাবে সংরক্ষিত হয় না, তাই এগুলো ব্যবহার করা অনেক সস্তা।
ইভিএম (EVM) কীভাবে ডেটা সংরক্ষণ করে (স্টোরেজ, মেমরি এবং স্ট্যাক) সে সম্পর্কে Solidity ডক্স (opens in a new tab)-এ আরও জানুন।
এনভায়রনমেন্ট ভেরিয়েবল
আপনার কন্ট্রাক্টে সংজ্ঞায়িত ভেরিয়েবলগুলো ছাড়াও, কিছু বিশেষ গ্লোবাল ভেরিয়েবল রয়েছে। এগুলো মূলত ব্লকচেইন বা বর্তমান লেনদেন সম্পর্কে তথ্য প্রদান করতে ব্যবহৃত হয়।
উদাহরণ:
| প্রপার্টি | স্টেট ভেরিয়েবল | বিবরণ |
|---|---|---|
block.timestamp | uint256 | বর্তমান ব্লক এপোক টাইমস্ট্যাম্প |
msg.sender | address | মেসেজ প্রেরক (বর্তমান কল) |
ফাংশন
সহজ কথায়, ফাংশনগুলো আগত লেনদেন-এর প্রতিক্রিয়ায় তথ্য পেতে বা সেট করতে পারে।
দুই ধরনের ফাংশন কল রয়েছে:
internal– এগুলো কোনো EVM কল তৈরি করে না- ইন্টারনাল ফাংশন এবং স্টেট ভেরিয়েবলগুলো শুধুমাত্র অভ্যন্তরীণভাবে অ্যাক্সেস করা যায় (অর্থাৎ, বর্তমান কন্ট্রাক্ট বা এটি থেকে উদ্ভূত কন্ট্রাক্টগুলোর ভেতর থেকে)
external– এগুলো একটি EVM কল তৈরি করে- এক্সটার্নাল ফাংশনগুলো কন্ট্রাক্ট ইন্টারফেসের অংশ, যার মানে এগুলো অন্যান্য কন্ট্রাক্ট থেকে এবং লেনদেন-এর মাধ্যমে কল করা যেতে পারে। একটি এক্সটার্নাল ফাংশন
fঅভ্যন্তরীণভাবে কল করা যায় না (অর্থাৎ,f()কাজ করে না, তবেthis.f()কাজ করে)।
- এক্সটার্নাল ফাংশনগুলো কন্ট্রাক্ট ইন্টারফেসের অংশ, যার মানে এগুলো অন্যান্য কন্ট্রাক্ট থেকে এবং লেনদেন-এর মাধ্যমে কল করা যেতে পারে। একটি এক্সটার্নাল ফাংশন
এগুলো public বা private ও হতে পারে
publicফাংশনগুলো কন্ট্রাক্টের ভেতর থেকে অভ্যন্তরীণভাবে বা মেসেজের মাধ্যমে বাহ্যিকভাবে কল করা যেতে পারেprivateফাংশনগুলো শুধুমাত্র সেই কন্ট্রাক্টের জন্যই দৃশ্যমান যেখানে সেগুলো সংজ্ঞায়িত করা হয়েছে এবং উদ্ভূত কন্ট্রাক্টগুলোতে নয়
ফাংশন এবং স্টেট ভেরিয়েবল উভয়কেই পাবলিক বা প্রাইভেট করা যেতে পারে
একটি কন্ট্রাক্টে স্টেট ভেরিয়েবল আপডেট করার জন্য এখানে একটি ফাংশন দেওয়া হলো:
1// সলিডিটি উদাহরণ2function update_name(string value) public {3 dapp_name = value;4}stringটাইপের প্যারামিটারvalueফাংশনে পাস করা হয়েছে:update_name- এটি
publicহিসেবে ঘোষণা করা হয়েছে, যার মানে যে কেউ এটি অ্যাক্সেস করতে পারে - এটি
viewহিসেবে ঘোষণা করা হয়নি, তাই এটি কন্ট্রাক্ট স্টেট পরিবর্তন করতে পারে
ভিউ ফাংশন
এই ফাংশনগুলো কন্ট্রাক্টের ডেটার স্টেট পরিবর্তন না করার প্রতিশ্রুতি দেয়। সাধারণ উদাহরণ হলো "গেটার" (getter) ফাংশন – উদাহরণস্বরূপ, আপনি কোনো ব্যবহারকারীর ব্যালেন্স জানতে এটি ব্যবহার করতে পারেন।
1// সলিডিটি উদাহরণ2function balanceOf(address _owner) public view returns (uint256 _balance) {3 return ownerPizzaCount[_owner];4}1dappName: public(string)23@view4@public5def readName() -> string:6 return dappNameস্টেট পরিবর্তন হিসেবে যা বিবেচনা করা হয়:
- স্টেট ভেরিয়েবলে লেখা।
- ইভেন্ট এমিট করা (opens in a new tab)।
- অন্যান্য কন্ট্রাক্ট তৈরি করা (opens in a new tab)।
selfdestructব্যবহার করা।- কলের মাধ্যমে ইথার পাঠানো।
viewবাpureহিসেবে চিহ্নিত নয় এমন কোনো ফাংশন কল করা।- লো-লেভেল কল ব্যবহার করা।
- নির্দিষ্ট অপকোড (opcodes) ধারণকারী ইনলাইন অ্যাসেম্বলি ব্যবহার করা।
কনস্ট্রাক্টর ফাংশন
constructor ফাংশনগুলো শুধুমাত্র একবার এক্সিকিউট হয় যখন কন্ট্রাক্টটি প্রথমবার ডিপ্লয় করা হয়। অনেক ক্লাস-ভিত্তিক প্রোগ্রামিং ভাষার constructor-এর মতো, এই ফাংশনগুলো প্রায়শই স্টেট ভেরিয়েবলগুলোকে তাদের নির্দিষ্ট মান দিয়ে ইনিশিয়ালাইজ করে।
1// সলিডিটি উদাহরণ2// কন্ট্রাক্টের ডেটা ইনিশিয়ালাইজ করে, `owner` সেট করে3// কন্ট্রাক্ট তৈরি কারীর ঠিকানায়।4constructor() public {5 // সব স্মার্ট কন্ট্রাক্ট এর ফাংশনগুলো ট্রিগার করার জন্য এক্সটার্নাল ট্রানজেকশনের ওপর নির্ভর করে।6 // `msg` হলো একটি গ্লোবাল ভেরিয়েবল যা প্রদত্ত ট্রানজেকশনের প্রাসঙ্গিক ডেটা ধারণ করে,7 // যেমন প্রেরকের ঠিকানা এবং ট্রানজেকশনে অন্তর্ভুক্ত ETH এর পরিমাণ।8 // আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties9 owner = msg.sender;10}সব দেখান1# ভাইপার উদাহরণ23@external4def __init__(_beneficiary: address, _bidding_time: uint256):5 self.beneficiary = _beneficiary6 self.auctionStart = block.timestamp7 self.auctionEnd = self.auctionStart + _bidding_timeবিল্ট-ইন ফাংশন
আপনার কন্ট্রাক্টে সংজ্ঞায়িত ভেরিয়েবল এবং ফাংশনগুলো ছাড়াও, কিছু বিশেষ বিল্ট-ইন ফাংশন রয়েছে। সবচেয়ে সুস্পষ্ট উদাহরণ হলো:
address.send()– Soliditysend(address)– Vyper
এগুলো কন্ট্রাক্টগুলোকে অন্যান্য একাউন্ট-এ ETH পাঠানোর অনুমতি দেয়।
ফাংশন লেখা
আপনার ফাংশনের জন্য প্রয়োজন:
- প্যারামিটার ভেরিয়েবল এবং টাইপ (যদি এটি প্যারামিটার গ্রহণ করে)
- internal/external এর ঘোষণা
- pure/view/payable এর ঘোষণা
- রিটার্ন টাইপ (যদি এটি কোনো মান রিটার্ন করে)
1pragma solidity >=0.4.0 <=0.6.0;23contract ExampleDapp {4 string dapp_name; // স্টেট ভেরিয়েবল56 // কন্ট্রাক্ট ডিপ্লয় করার সময় কল করা হয় এবং মান ইনিশিয়ালাইজ করে7 constructor() public {8 dapp_name = "My Example dapp";9 }1011 // গেট ফাংশন12 function read_name() public view returns(string) {13 return dapp_name;14 }1516 // সেট ফাংশন17 function update_name(string value) public {18 dapp_name = value;19 }20}সব দেখানএকটি সম্পূর্ণ কন্ট্রাক্ট দেখতে অনেকটা এরকম হতে পারে। এখানে constructor ফাংশনটি dapp_name ভেরিয়েবলের জন্য একটি প্রাথমিক মান প্রদান করে।
ইভেন্ট এবং লগ
ইভেন্টগুলো আপনার স্মার্ট কন্ট্রাক্টকে আপনার ফ্রন্টএন্ড বা অন্যান্য সাবস্ক্রাইবিং অ্যাপ্লিকেশনের সাথে যোগাযোগ করতে সক্ষম করে। একবার একটি লেনদেন যাচাই হয়ে একটি ব্লক-এ যুক্ত হলে, স্মার্ট কন্ট্রাক্টগুলো ইভেন্ট এমিট করতে এবং তথ্য লগ করতে পারে, যা ফ্রন্টএন্ড পরবর্তীতে প্রসেস এবং ব্যবহার করতে পারে।
টীকাযুক্ত উদাহরণ
এগুলো Solidity-তে লেখা কিছু উদাহরণ। আপনি যদি কোড নিয়ে কাজ করতে চান, তবে আপনি Remix (opens in a new tab)-এ সেগুলোর সাথে ইন্টারঅ্যাক্ট করতে পারেন।
হ্যালো ওয়ার্ল্ড
1// সিমেন্টিক ভার্সনিং ব্যবহার করে সলিডিটির ভার্সন নির্দিষ্ট করে।2// আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma3pragma solidity ^0.5.10;45// `HelloWorld` নামের একটি কন্ট্রাক্ট ডিফাইন করে।6// একটি কন্ট্রাক্ট হলো ফাংশন এবং ডেটার (এর স্টেট) একটি সংগ্রহ।7// একবার ডিপ্লয় করা হলে, একটি কন্ট্রাক্ট ইথেরিয়াম ব্লকচেইনের একটি নির্দিষ্ট ঠিকানায় অবস্থান করে।8// আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html9contract HelloWorld {1011 // `string` টাইপের একটি স্টেট ভেরিয়েবল `message` ডিক্লেয়ার করে।12 // স্টেট ভেরিয়েবল হলো এমন ভেরিয়েবল যার মান কন্ট্রাক্ট স্টোরেজে স্থায়ীভাবে সংরক্ষিত থাকে।13 // `public` কিওয়ার্ড ভেরিয়েবলগুলোকে কন্ট্রাক্টের বাইরে থেকে অ্যাক্সেসযোগ্য করে তোলে14 // এবং এমন একটি ফাংশন তৈরি করে যা অন্য কন্ট্রাক্ট বা ক্লায়েন্টরা মান অ্যাক্সেস করতে কল করতে পারে।15 string public message;1617 // অনেক ক্লাস-ভিত্তিক অবজেক্ট-ওরিয়েন্টেড ভাষার মতো, একটি কনস্ট্রাক্টর হলো18 // একটি বিশেষ ফাংশন যা শুধুমাত্র কন্ট্রাক্ট তৈরির সময় এক্সিকিউট হয়।19 // কনস্ট্রাক্টরগুলো কন্ট্রাক্টের ডেটা ইনিশিয়ালাইজ করতে ব্যবহৃত হয়।20 // আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors21 constructor(string memory initMessage) public {22 // একটি স্ট্রিং আর্গুমেন্ট `initMessage` গ্রহণ করে এবং মান সেট করে23 // কন্ট্রাক্টের `message` স্টোরেজ ভেরিয়েবলে)।24 message = initMessage;25 }2627 // একটি পাবলিক ফাংশন যা একটি স্ট্রিং আর্গুমেন্ট গ্রহণ করে28 // এবং `message` স্টোরেজ ভেরিয়েবল আপডেট করে।29 function update(string memory newMessage) public {30 message = newMessage;31 }32}সব দেখানটোকেন
1pragma solidity ^0.5.10;23contract Token {4 // একটি `address` ইমেইল ঠিকানার সাথে তুলনীয় - এটি ইথেরিয়ামে একটি অ্যাকাউন্ট শনাক্ত করতে ব্যবহৃত হয়।5 // ঠিকানাগুলো একটি স্মার্ট কন্ট্রাক্ট বা এক্সটার্নাল (ব্যবহারকারী) অ্যাকাউন্টকে উপস্থাপন করতে পারে।6 // আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/types.html#address7 address public owner;89 // একটি `mapping` মূলত একটি হ্যাস টেবিল ডেটা স্ট্রাকচার।10 // এই `mapping` একটি ঠিকানায় (টোকেন হোল্ডার) একটি আনসাইনড ইন্টিজার (টোকেন ব্যালেন্স) অ্যাসাইন করে।11 // আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/types.html#mapping-types12 mapping (address => uint) public balances;1314 // ইভেন্টগুলো ব্লকচেইনে অ্যাক্টিভিটি লগ করার অনুমতি দেয়।15 // কন্ট্রাক্ট স্টেট পরিবর্তনের প্রতিক্রিয়া জানাতে ইথেরিয়াম ক্লায়েন্টরা ইভেন্টগুলো শুনতে পারে।16 // আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#events17 event Transfer(address from, address to, uint amount);1819 // কন্ট্রাক্টের ডেটা ইনিশিয়ালাইজ করে, `owner` সেট করে20 // কন্ট্রাক্ট তৈরি কারীর ঠিকানায়।21 constructor() public {22 // সব স্মার্ট কন্ট্রাক্ট এর ফাংশনগুলো ট্রিগার করার জন্য এক্সটার্নাল ট্রানজেকশনের ওপর নির্ভর করে।23 // `msg` হলো একটি গ্লোবাল ভেরিয়েবল যা প্রদত্ত ট্রানজেকশনের প্রাসঙ্গিক ডেটা ধারণ করে,24 // যেমন প্রেরকের ঠিকানা এবং ট্রানজেকশনে অন্তর্ভুক্ত ETH এর পরিমাণ।25 // আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties26 owner = msg.sender;27 }2829 // কিছু পরিমাণ নতুন টোকেন তৈরি করে এবং সেগুলো একটি ঠিকানায় পাঠায়।30 function mint(address receiver, uint amount) public {31 // `require` হলো একটি কন্ট্রোল স্ট্রাকচার যা নির্দিষ্ট শর্ত প্রয়োগ করতে ব্যবহৃত হয়।32 // যদি একটি `require` স্টেটমেন্ট `false` হয়, তবে একটি এক্সেপশন ট্রিগার হয়,33 // যা বর্তমান কলের সময় স্টেটে করা সমস্ত পরিবর্তন রিভার্ট করে।34 // আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions3536 // শুধুমাত্র কন্ট্রাক্ট ওনার এই ফাংশনটি কল করতে পারেন37 require(msg.sender == owner, "You are not the owner.");3839 // টোকেনের সর্বোচ্চ পরিমাণ প্রয়োগ করে40 require(amount < 1e60, "Maximum issuance exceeded");4142 // `receiver` এর ব্যালেন্স `amount` পরিমাণ বাড়ায়43 balances[receiver] += amount;44 }4546 // যেকোনো কলার থেকে একটি ঠিকানায় কিছু পরিমাণ বিদ্যমান টোকেন পাঠায়।47 function transfer(address receiver, uint amount) public {48 // প্রেরকের কাছে পাঠানোর জন্য পর্যাপ্ত টোকেন থাকতে হবে49 require(amount <= balances[msg.sender], "Insufficient balance.");5051 // দুটি ঠিকানার টোকেন ব্যালেন্স অ্যাডজাস্ট করে52 balances[msg.sender] -= amount;53 balances[receiver] += amount;5455 // আগে ডিফাইন করা ইভেন্ট এমিট করে56 emit Transfer(msg.sender, receiver, amount);57 }58}সব দেখানইউনিক ডিজিটাল অ্যাসেট
1pragma solidity ^0.5.10;23// অন্যান্য ফাইল থেকে বর্তমান কন্ট্রাক্টে সিম্বল ইমপোর্ট করে।4// এই ক্ষেত্রে, OpenZeppelin থেকে হেল্পার কন্ট্রাক্টের একটি সিরিজ।5// আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#importing-other-source-files67import "../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol";8import "../node_modules/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";9import "../node_modules/@openzeppelin/contracts/introspection/ERC165.sol";10import "../node_modules/@openzeppelin/contracts/math/SafeMath.sol";1112// `is` কিওয়ার্ডটি এক্সটার্নাল কন্ট্রাক্ট থেকে ফাংশন এবং কিওয়ার্ড ইনহেরিট করতে ব্যবহৃত হয়।13// এই ক্ষেত্রে, `CryptoPizza` `IERC721` এবং `ERC165` কন্ট্রাক্ট থেকে ইনহেরিট করে।14// আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#inheritance15contract CryptoPizza is IERC721, ERC165 {16 // নিরাপদে গাণিতিক অপারেশন করতে OpenZeppelin এর SafeMath লাইব্রেরি ব্যবহার করে।17 // আরও জানুন: https://docs.openzeppelin.com/contracts/2.x/api/math#SafeMath18 using SafeMath for uint256;1920 // সলিডিটিতে কনস্ট্যান্ট স্টেট ভেরিয়েবলগুলো অন্যান্য ভাষার মতোই21 // তবে আপনাকে এমন একটি এক্সপ্রেশন থেকে অ্যাসাইন করতে হবে যা কম্পাইল টাইমে কনস্ট্যান্ট থাকে।22 // আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constant-state-variables23 uint256 constant dnaDigits = 10;24 uint256 constant dnaModulus = 10 ** dnaDigits;25 bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;2627 // স্ট্রাক্ট টাইপ আপনাকে আপনার নিজস্ব টাইপ ডিফাইন করতে দেয়28 // আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/types.html#structs29 struct Pizza {30 string name;31 uint256 dna;32 }3334 // Pizza স্ট্রাক্টের একটি খালি অ্যারে তৈরি করে35 Pizza[] public pizzas;3637 // পিজা আইডি থেকে এর ওনারের ঠিকানায় ম্যাপিং38 mapping(uint256 => address) public pizzaToOwner;3940 // ওনারের ঠিকানা থেকে মালিকানাধীন টোকেনের সংখ্যায় ম্যাপিং41 mapping(address => uint256) public ownerPizzaCount;4243 // টোকেন আইডি থেকে অনুমোদিত ঠিকানায় ম্যাপিং44 mapping(uint256 => address) pizzaApprovals;4546 // আপনি ম্যাপিং নেস্ট করতে পারেন, এই উদাহরণটি ওনার থেকে অপারেটর অ্যাপ্রুভালে ম্যাপ করে47 mapping(address => mapping(address => bool)) private operatorApprovals;4849 // স্ট্রিং (নাম) এবং ডিএনএ থেকে একটি র্যান্ডম পিজা তৈরি করার ইন্টারনাল ফাংশন50 function _createPizza(string memory _name, uint256 _dna)51 // `internal` কিওয়ার্ডের মানে হলো এই ফাংশনটি শুধুমাত্র দৃশ্যমান52 // এই কন্ট্রাক্ট এবং এই কন্ট্রাক্ট থেকে ডিরাইভ করা কন্ট্রাক্টগুলোর মধ্যে53 // আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#visibility-and-getters54 internal55 // `isUnique` হলো একটি ফাংশন মডিফায়ার যা পিজাটি আগে থেকেই আছে কিনা তা চেক করে56 // আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html#function-modifiers57 isUnique(_name, _dna)58 {59 // পিজাদের অ্যারেতে পিজা যোগ করে এবং আইডি পায়60 uint256 id = SafeMath.sub(pizzas.push(Pizza(_name, _dna)), 1);6162 // পিজার ওনার বর্তমান ব্যবহারকারীর সমান কিনা তা চেক করে63 // আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions6465 // খেয়াল রাখবেন যে address(0) হলো জিরো অ্যাড্রেস,66 // যা নির্দেশ করে যে pizza[id] এখনও কোনো নির্দিষ্ট ব্যবহারকারীকে বরাদ্দ করা হয়নি।6768 assert(pizzaToOwner[id] == address(0));6970 // পিজাকে ওনারের সাথে ম্যাপ করে71 pizzaToOwner[id] = msg.sender;72 ownerPizzaCount[msg.sender] = SafeMath.add(73 ownerPizzaCount[msg.sender],74 175 );76 }7778 // স্ট্রিং (নাম) থেকে একটি র্যান্ডম পিজা তৈরি করে79 function createRandomPizza(string memory _name) public {80 uint256 randDna = generateRandomDna(_name, msg.sender);81 _createPizza(_name, randDna);82 }8384 // স্ট্রিং (নাম) এবং ওনারের (তৈরি কারী) ঠিকানা থেকে র্যান্ডম ডিএনএ জেনারেট করে85 function generateRandomDna(string memory _str, address _owner)86 public87 // `pure` হিসেবে চিহ্নিত ফাংশনগুলো স্টেট থেকে রিড বা মডিফাই না করার প্রতিশ্রুতি দেয়88 // আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#pure-functions89 pure90 returns (uint256)91 {92 // স্ট্রিং (নাম) + ঠিকানা (ওনার) থেকে র্যান্ডম uint জেনারেট করে93 uint256 rand = uint256(keccak256(abi.encodePacked(_str))) +94 uint256(_owner);95 rand = rand % dnaModulus;96 return rand;97 }9899 // ওনারের পাওয়া পিজাদের অ্যারে রিটার্ন করে100 function getPizzasByOwner(address _owner)101 public102 // `view` হিসেবে চিহ্নিত ফাংশনগুলো স্টেট মডিফাই না করার প্রতিশ্রুতি দেয়103 // আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#view-functions104 view105 returns (uint256[] memory)106 {107 // মান সংরক্ষণ করতে `memory` স্টোরেজ লোকেশন ব্যবহার করে শুধুমাত্র108 // এই ফাংশন কলের লাইফসাইকেলের জন্য।109 // আরও জানুন: https://solidity.readthedocs.io/en/v0.5.10/introduction-to-smart-contracts.html#storage-memory-and-the-stack110 uint256[] memory result = new uint256[](ownerPizzaCount[_owner]);111 uint256 counter = 0;112 for (uint256 i = 0; i < pizzas.length; i++) {113 if (pizzaToOwner[i] == _owner) {114 result[counter] = i;115 counter++;116 }117 }118 return result;119 }120121 // অন্য ঠিকানায় পিজা এবং মালিকানা ট্রান্সফার করে122 function transferFrom(address _from, address _to, uint256 _pizzaId) public {123 require(_from != address(0) && _to != address(0), "Invalid address.");124 require(_exists(_pizzaId), "Pizza does not exist.");125 require(_from != _to, "Cannot transfer to the same address.");126 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Address is not approved.");127128 ownerPizzaCount[_to] = SafeMath.add(ownerPizzaCount[_to], 1);129 ownerPizzaCount[_from] = SafeMath.sub(ownerPizzaCount[_from], 1);130 pizzaToOwner[_pizzaId] = _to;131132 // ইমপোর্ট করা IERC721 কন্ট্রাক্টে ডিফাইন করা ইভেন্ট এমিট করে133 emit Transfer(_from, _to, _pizzaId);134 _clearApproval(_to, _pizzaId);135 }136137 /* *138 * একটি প্রদত্ত টোকেন আইডির মালিকানা নিরাপদে অন্য ঠিকানায় ট্রান্সফার করে139 * যদি টার্গেট ঠিকানাটি একটি কন্ট্রাক্ট হয়, তবে এটিকে অবশ্যই `onERC721Received` ইমপ্লিমেন্ট করতে হবে,140 * যা একটি নিরাপদ ট্রান্সফারের সময় কল করা হয়, এবং ম্যাজিক ভ্যালু রিটার্ন করে141 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;142 * অন্যথায়, ট্রান্সফারটি রিভার্ট করা হয়। */143 function safeTransferFrom(address from, address to, uint256 pizzaId)144 public145 {146 // solium-disable-next-line arg-overflow147 this.safeTransferFrom(from, to, pizzaId, "");148 }149150 /* *151 * একটি প্রদত্ত টোকেন আইডির মালিকানা নিরাপদে অন্য ঠিকানায় ট্রান্সফার করে152 * যদি টার্গেট ঠিকানাটি একটি কন্ট্রাক্ট হয়, তবে এটিকে অবশ্যই `onERC721Received` ইমপ্লিমেন্ট করতে হবে,153 * যা একটি নিরাপদ ট্রান্সফারের সময় কল করা হয়, এবং ম্যাজিক ভ্যালু রিটার্ন করে154 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;155 * অন্যথায়, ট্রান্সফারটি রিভার্ট করা হয়। */156 function safeTransferFrom(157 address from,158 address to,159 uint256 pizzaId,160 bytes memory _data161 ) public {162 this.transferFrom(from, to, pizzaId);163 require(_checkOnERC721Received(from, to, pizzaId, _data), "Must implement onERC721Received.");164 }165166 /* *167 * একটি টার্গেট ঠিকানায় `onERC721Received` ইনভোক করার ইন্টারনাল ফাংশন168 * টার্গেট ঠিকানাটি কন্ট্রাক্ট না হলে কলটি এক্সিকিউট হয় না */169 function _checkOnERC721Received(170 address from,171 address to,172 uint256 pizzaId,173 bytes memory _data174 ) internal returns (bool) {175 if (!isContract(to)) {176 return true;177 }178179 bytes4 retval = IERC721Receiver(to).onERC721Received(180 msg.sender,181 from,182 pizzaId,183 _data184 );185 return (retval == _ERC721_RECEIVED);186 }187188 // একটি পিজা বার্ন করে - টোকেনটি সম্পূর্ণ ধ্বংস করে189 // `external` ফাংশন মডিফায়ারের মানে হলো এই ফাংশনটি190 // কন্ট্রাক্ট ইন্টারফেসের অংশ এবং অন্যান্য কন্ট্রাক্ট এটি কল করতে পারে191 function burn(uint256 _pizzaId) external {192 require(msg.sender != address(0), "Invalid address.");193 require(_exists(_pizzaId), "Pizza does not exist.");194 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Address is not approved.");195196 ownerPizzaCount[msg.sender] = SafeMath.sub(197 ownerPizzaCount[msg.sender],198 1199 );200 pizzaToOwner[_pizzaId] = address(0);201 }202203 // ঠিকানা অনুযায়ী পিজার সংখ্যা রিটার্ন করে204 function balanceOf(address _owner) public view returns (uint256 _balance) {205 return ownerPizzaCount[_owner];206 }207208 // আইডি দ্বারা পাওয়া পিজার ওনার রিটার্ন করে209 function ownerOf(uint256 _pizzaId) public view returns (address _owner) {210 address owner = pizzaToOwner[_pizzaId];211 require(owner != address(0), "Invalid Pizza ID.");212 return owner;213 }214215 // পিজার মালিকানা ট্রান্সফার করতে অন্য ঠিকানাকে অনুমোদন দেয়216 function approve(address _to, uint256 _pizzaId) public {217 require(msg.sender == pizzaToOwner[_pizzaId], "Must be the Pizza owner.");218 pizzaApprovals[_pizzaId] = _to;219 emit Approval(msg.sender, _to, _pizzaId);220 }221222 // নির্দিষ্ট পিজার জন্য অনুমোদিত ঠিকানা রিটার্ন করে223 function getApproved(uint256 _pizzaId)224 public225 view226 returns (address operator)227 {228 require(_exists(_pizzaId), "Pizza does not exist.");229 return pizzaApprovals[_pizzaId];230 }231232 /* *233 * একটি প্রদত্ত টোকেন আইডির বর্তমান অনুমোদন ক্লিয়ার করার প্রাইভেট ফাংশন234 * প্রদত্ত ঠিকানাটি টোকেনের প্রকৃত ওনার না হলে রিভার্ট করে */235 function _clearApproval(address owner, uint256 _pizzaId) private {236 require(pizzaToOwner[_pizzaId] == owner, "Must be pizza owner.");237 require(_exists(_pizzaId), "Pizza does not exist.");238 if (pizzaApprovals[_pizzaId] != address(0)) {239 pizzaApprovals[_pizzaId] = address(0);240 }241 }242243 /* * একটি প্রদত্ত অপারেটরের অনুমোদন সেট বা আনসেট করে244 * একজন অপারেটর প্রেরকের পক্ষে তার সমস্ত টোকেন ট্রান্সফার করার অনুমতি পায় */245 function setApprovalForAll(address to, bool approved) public {246 require(to != msg.sender, "Cannot approve own address");247 operatorApprovals[msg.sender][to] = approved;248 emit ApprovalForAll(msg.sender, to, approved);249 }250251 // একজন অপারেটর একটি প্রদত্ত ওনার দ্বারা অনুমোদিত কিনা তা বলে252 function isApprovedForAll(address owner, address operator)253 public254 view255 returns (bool)256 {257 return operatorApprovals[owner][operator];258 }259260 // পিজার মালিকানা নেয় - শুধুমাত্র অনুমোদিত ব্যবহারকারীদের জন্য261 function takeOwnership(uint256 _pizzaId) public {262 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Address is not approved.");263 address owner = this.ownerOf(_pizzaId);264 this.transferFrom(owner, msg.sender, _pizzaId);265 }266267 // পিজা আছে কিনা তা চেক করে268 function _exists(uint256 pizzaId) internal view returns (bool) {269 address owner = pizzaToOwner[pizzaId];270 return owner != address(0);271 }272273 // ঠিকানাটি ওনার কিনা বা পিজা ট্রান্সফার করার জন্য অনুমোদিত কিনা তা চেক করে274 function _isApprovedOrOwner(address spender, uint256 pizzaId)275 internal276 view277 returns (bool)278 {279 address owner = pizzaToOwner[pizzaId];280 // solium চেক নিষ্ক্রিয় করুন কারণ281 // https://github.com/duaraghav8/Solium/issues/175282 // solium-disable-next-line operator-whitespace283 return (spender == owner ||284 this.getApproved(pizzaId) == spender ||285 this.isApprovedForAll(owner, spender));286 }287288 // পিজাটি ইউনিক কিনা এবং এখনও বিদ্যমান নেই কিনা তা চেক করুন289 modifier isUnique(string memory _name, uint256 _dna) {290 bool result = true;291 for (uint256 i = 0; i < pizzas.length; i++) {292 if (293 keccak256(abi.encodePacked(pizzas[i].name)) ==294 keccak256(abi.encodePacked(_name)) &&295 pizzas[i].dna == _dna296 ) {297 result = false;298 }299 }300 require(result, "Pizza with such name already exists.");301 _;302 }303304 // টার্গেট ঠিকানাটি একটি কন্ট্রাক্ট কিনা তা রিটার্ন করে305 function isContract(address account) internal view returns (bool) {306 uint256 size;307 // বর্তমানে কোনো ঠিকানায় কন্ট্রাক্ট আছে কিনা তা চেক করার এর চেয়ে ভালো কোনো উপায় নেই308 // সেই ঠিকানায় কোডের সাইজ চেক করা ছাড়া।309 // দেখুন https://ethereum.stackexchange.com/a/14016/36603310 // এটি কীভাবে কাজ করে সে সম্পর্কে আরও বিস্তারিত জানতে।311 // TODO Serenity রিলিজের আগে এটি আবার চেক করুন, কারণ তখন সমস্ত ঠিকানা হবে312 // কন্ট্রাক্ট।313 // solium-disable-next-line security/no-inline-assembly314 assembly {315 size := extcodesize(account)316 }317 return size > 0;318 }319}সব দেখানআরও পড়ুন
স্মার্ট কন্ট্রাক্ট সম্পর্কে আরও সম্পূর্ণ ধারণা পেতে Solidity এবং Vyper-এর ডকুমেন্টেশন দেখুন:
সম্পর্কিত বিষয়গুলো
সম্পর্কিত টিউটোরিয়াল
- কন্ট্রাক্ট সাইজ লিমিট মোকাবেলা করতে কন্ট্রাক্ট ছোট করা – আপনার স্মার্ট কন্ট্রাক্টের আকার কমানোর জন্য কিছু ব্যবহারিক টিপস।
- ইভেন্টের মাধ্যমে স্মার্ট কন্ট্রাক্ট থেকে ডেটা লগ করা – স্মার্ট কন্ট্রাক্ট ইভেন্টগুলোর একটি পরিচিতি এবং কীভাবে আপনি সেগুলো ডেটা লগ করতে ব্যবহার করতে পারেন।
- Solidity থেকে অন্যান্য কন্ট্রাক্টের সাথে ইন্টারঅ্যাক্ট করা – কীভাবে একটি বিদ্যমান কন্ট্রাক্ট থেকে একটি স্মার্ট কন্ট্রাক্ট ডিপ্লয় করবেন এবং এর সাথে ইন্টারঅ্যাক্ট করবেন।