سمارٹ کنٹریکٹس کی ساخت
ایک سمارٹ کنٹریکٹ ایک پروگرام ہے جو ایتھیریم پر ایک پتہ پر چلتا ہے۔ یہ ڈیٹا اور فنکشنز پر مشتمل ہوتے ہیں جو ٹرانزیکشن موصول ہونے پر عمل میں آ سکتے ہیں۔ یہاں ایک جائزہ ہے کہ سمارٹ کنٹریکٹ کن چیزوں سے مل کر بنتا ہے۔
پیشگی شرائط
یقینی بنائیں کہ آپ نے پہلے سمارٹ کنٹریکٹس کے بارے میں پڑھ لیا ہے۔ یہ دستاویز فرض کرتی ہے کہ آپ پہلے ہی JavaScript یا Python جیسی پروگرامنگ زبانوں سے واقف ہیں۔
ڈیٹا
کسی بھی کنٹریکٹ کے ڈیٹا کو ایک مقام تفویض کیا جانا چاہیے: یا تو storage یا memory پر۔ سمارٹ کنٹریکٹ میں سٹوریج کو تبدیل کرنا مہنگا ہوتا ہے اس لیے آپ کو غور کرنے کی ضرورت ہے کہ آپ کا ڈیٹا کہاں رہنا چاہیے۔
سٹوریج
مستقل ڈیٹا کو سٹوریج کہا جاتا ہے اور اسے حالت کے متغیرات (حالت کا متغیرs) سے ظاہر کیا جاتا ہے۔ یہ اقدار بلاک چین پر مستقل طور پر محفوظ ہو جاتی ہیں۔ آپ کو اس کی قسم (type) کا اعلان کرنے کی ضرورت ہے تاکہ کنٹریکٹ مرتب (compile) ہوتے وقت یہ حساب رکھ سکے کہ اسے بلاک چین پر کتنی سٹوریج کی ضرورت ہے۔
// Solidity کی مثال
contract SimpleStorage {
uint storedData; // حالت کا متغیر
// ...
}
# Vyper کی مثال
storedData: int128
اگر آپ نے پہلے ہی آبجیکٹ اورینٹڈ زبانوں میں پروگرامنگ کی ہے، تو آپ ممکنہ طور پر زیادہ تر اقسام (types) سے واقف ہوں گے۔ تاہم، اگر آپ ایتھیریم ڈیولپمنٹ میں نئے ہیں تو address آپ کے لیے نیا ہونا چاہیے۔
ایک address قسم ایک ایتھیریم پتہ رکھ سکتی ہے جو 20 bytes یا 160 bits کے برابر ہوتا ہے۔ یہ ہیکسا ڈیسیمل (hexadecimal) شکل میں واپس آتا ہے جس کے شروع میں 0x ہوتا ہے۔
دیگر اقسام میں شامل ہیں:
- بولین (boolean)
- انٹیجر (integer)
- فکسڈ پوائنٹ نمبرز (fixed point numbers)
- فکسڈ سائز بائٹ ایریز (fixed-size byte arrays)
- ڈائنیمکلی سائزڈ بائٹ ایریز (dynamically sized byte arrays)
- ریشنل اور انٹیجر لٹرلز (rational and integer literals)
- سٹرنگ لٹرلز (string literals)
- ہیکسا ڈیسیمل لٹرلز (hexadecimal literals)
- اینمز (enums)
مزید وضاحت کے لیے، دستاویزات پر ایک نظر ڈالیں:
میموری
وہ اقدار جو صرف کنٹریکٹ فنکشن کے عمل درآمد کی مدت کے لیے محفوظ کی جاتی ہیں، میموری متغیرات (memory variables) کہلاتی ہیں۔ چونکہ یہ بلاک چین پر مستقل طور پر محفوظ نہیں ہوتیں، اس لیے ان کا استعمال بہت سستا ہوتا ہے۔
اس بارے میں مزید جانیں کہ EVM ڈیٹا کیسے محفوظ کرتا ہے (سٹوریج، میموری، اور سٹیک) Solidity کی دستاویزات (opens in a new tab) میں۔
ماحولیاتی متغیرات (Environment variables)
آپ کے کنٹریکٹ پر بیان کردہ متغیرات کے علاوہ، کچھ خاص عالمی متغیرات (global variables) بھی ہوتے ہیں۔ یہ بنیادی طور پر بلاک چین یا موجودہ ٹرانزیکشن کے بارے میں معلومات فراہم کرنے کے لیے استعمال ہوتے ہیں۔
مثالیں:
| خصوصیت (Prop) | حالت کا متغیر (State variable) | تفصیل |
|---|---|---|
block.timestamp | uint256 | موجودہ بلاک کے دور (epoch) کا ٹائم سٹیمپ |
msg.sender | پتہ | پیغام بھیجنے والا (موجودہ کال) |
فنکشنز
انتہائی سادہ الفاظ میں، فنکشنز آنے والی ٹرانزیکشنز کے جواب میں معلومات حاصل کر سکتے ہیں یا معلومات سیٹ کر سکتے ہیں۔
فنکشن کالز کی دو اقسام ہیں:
internal– یہ EVM کال نہیں بناتے- اندرونی فنکشنز اور حالت کے متغیرات تک صرف اندرونی طور پر رسائی حاصل کی جا سکتی ہے (یعنی موجودہ کنٹریکٹ یا اس سے اخذ کردہ کنٹریکٹس کے اندر سے)
external– یہ EVM کال بناتے ہیں- بیرونی فنکشنز کنٹریکٹ انٹرفیس کا حصہ ہوتے ہیں، جس کا مطلب ہے کہ انہیں دوسرے کنٹریکٹس سے اور ٹرانزیکشنز کے ذریعے کال کیا جا سکتا ہے۔ ایک بیرونی فنکشن
fکو اندرونی طور پر کال نہیں کیا جا سکتا (یعنیf()کام نہیں کرتا، لیکنthis.f()کام کرتا ہے)۔
- بیرونی فنکشنز کنٹریکٹ انٹرفیس کا حصہ ہوتے ہیں، جس کا مطلب ہے کہ انہیں دوسرے کنٹریکٹس سے اور ٹرانزیکشنز کے ذریعے کال کیا جا سکتا ہے۔ ایک بیرونی فنکشن
یہ public یا private بھی ہو سکتے ہیں
publicفنکشنز کو کنٹریکٹ کے اندر سے اندرونی طور پر یا پیغامات کے ذریعے بیرونی طور پر کال کیا جا سکتا ہےprivateفنکشنز صرف اسی کنٹریکٹ کے لیے نظر آتے ہیں جس میں ان کی تعریف کی گئی ہو اور اخذ کردہ کنٹریکٹس میں نہیں
فنکشنز اور حالت کے متغیرات دونوں کو پبلک یا پرائیویٹ بنایا جا سکتا ہے
یہاں ایک کنٹریکٹ پر حالت کے متغیر کو اپ ڈیٹ کرنے کے لیے ایک فنکشن ہے:
// Solidity کی مثال
function update_name(string value) public {
dapp_name = value;
}
stringقسم کا پیرامیٹرvalueفنکشن میں پاس کیا جاتا ہے:update_name- اسے
publicقرار دیا گیا ہے، جس کا مطلب ہے کہ کوئی بھی اس تک رسائی حاصل کر سکتا ہے - اسے
viewقرار نہیں دیا گیا ہے، اس لیے یہ کنٹریکٹ کی حالت کو تبدیل کر سکتا ہے
ویو فنکشنز (View functions)
یہ فنکشنز کنٹریکٹ کے ڈیٹا کی حالت کو تبدیل نہ کرنے کا وعدہ کرتے ہیں۔ عام مثالیں "گیٹر" (getter) فنکشنز ہیں – مثال کے طور پر آپ اسے صارف کا بیلنس حاصل کرنے کے لیے استعمال کر سکتے ہیں۔
// Solidity کی مثال
function balanceOf(address _owner) public view returns (uint256 _balance) {
return ownerPizzaCount[_owner];
}
dappName: public(string)
@view
@public
def readName() -> string:
return dappName
حالت کو تبدیل کرنا کیا سمجھا جاتا ہے:
- حالت کے متغیرات میں لکھنا۔
- ایونٹس کا اخراج (opens in a new tab)۔
- دوسرے کنٹریکٹس بنانا (opens in a new tab)۔
selfdestructکا استعمال۔- کالز کے ذریعے ایتھر بھیجنا۔
- کسی بھی ایسے فنکشن کو کال کرنا جس پر
viewیاpureکا نشان نہ ہو۔ - لو لیول (low-level) کالز کا استعمال۔
- ان لائن اسمبلی (inline assembly) کا استعمال جس میں مخصوص اوپ کوڈز (opcodes) شامل ہوں۔
کنسٹرکٹر فنکشنز
constructor فنکشنز صرف ایک بار عمل میں آتے ہیں جب کنٹریکٹ پہلی بار تعینات کیا جاتا ہے۔ بہت سی کلاس پر مبنی پروگرامنگ زبانوں میں constructor کی طرح، یہ فنکشنز اکثر حالت کے متغیرات کو ان کی مخصوص اقدار کے ساتھ شروع (initialize) کرتے ہیں۔
// Solidity کی مثال
// کنٹریکٹ کے ڈیٹا کو شروع کرتا ہے، `owner` کو سیٹ کرتے ہوئے
// کنٹریکٹ بنانے والے کے پتہ پر۔
constructor() public {
// تمام سمارٹ کنٹریکٹ اپنے فنکشنز کو متحرک کرنے کے لیے بیرونی ٹرانزیکشنز پر انحصار کرتے ہیں۔
// `msg` ایک گلوبل متغیر ہے جس میں دی گئی ٹرانزیکشن کا متعلقہ ڈیٹا شامل ہوتا ہے،
// جیسے بھیجنے والے کا پتہ اور ٹرانزیکشن میں شامل ETH کی مالیت۔
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties
owner = msg.sender;
}
# Vyper کی مثال
@external
def __init__(_beneficiary: address, _bidding_time: uint256):
self.beneficiary = _beneficiary
self.auctionStart = block.timestamp
self.auctionEnd = self.auctionStart + _bidding_time
بلٹ ان فنکشنز (Built-in functions)
آپ کے کنٹریکٹ پر بیان کردہ متغیرات اور فنکشنز کے علاوہ، کچھ خاص بلٹ ان فنکشنز بھی ہوتے ہیں۔ سب سے واضح مثال یہ ہے:
address.send()– Soliditysend(address)– Vyper
یہ کنٹریکٹس کو دوسرے اکاؤنٹس میں ETH بھیجنے کی اجازت دیتے ہیں۔
فنکشنز لکھنا
آپ کے فنکشن کو ضرورت ہوتی ہے:
- پیرامیٹر متغیر اور قسم (اگر یہ پیرامیٹرز قبول کرتا ہے)
- اندرونی/بیرونی (internal/external) کا اعلان
- پیور/ویو/پے ایبل (pure/view/payable) کا اعلان
- واپسی کی قسم (اگر یہ کوئی قدر واپس کرتا ہے)
pragma solidity >=0.4.0 <=0.6.0;
contract ExampleDapp {
string dapp_name; // state variable
// جب کنٹریکٹ تعینات کیا جاتا ہے تو اسے کال کیا جاتا ہے اور یہ ویلیو کو شروع کرتا ہے
constructor() public {
dapp_name = "My Example dapp";
}
// Get فنکشن
function read_name() public view returns(string) {
return dapp_name;
}
// Set فنکشن
function update_name(string value) public {
dapp_name = value;
}
}
ایک مکمل کنٹریکٹ کچھ اس طرح نظر آ سکتا ہے۔ یہاں constructor فنکشن dapp_name متغیر کے لیے ایک ابتدائی قدر فراہم کرتا ہے۔
ایونٹس اور لاگز
ایونٹس آپ کے سمارٹ کنٹریکٹ کو آپ کے فرنٹ اینڈ یا دیگر سبسکرائب کرنے والی ایپلیکیشنز کے ساتھ بات چیت کرنے کے قابل بناتے ہیں۔ ایک بار جب ٹرانزیکشن کی توثیق ہو جاتی ہے اور اسے بلاک میں شامل کر دیا جاتا ہے، تو سمارٹ کنٹریکٹس ایونٹس خارج کر سکتے ہیں اور معلومات لاگ کر سکتے ہیں، جسے فرنٹ اینڈ پھر پروسیس اور استعمال کر سکتا ہے۔
تشریح شدہ مثالیں
یہ Solidity میں لکھی گئی کچھ مثالیں ہیں۔ اگر آپ کوڈ کے ساتھ تجربہ کرنا چاہتے ہیں، تو آپ Remix (opens in a new tab) میں ان کے ساتھ تعامل کر سکتے ہیں۔
ہیلو ورلڈ (Hello world)
// سیمینٹک ورژننگ کا استعمال کرتے ہوئے، Solidity کا ورژن بتاتا ہے۔
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
pragma solidity ^0.5.10;
// `HelloWorld` نامی کنٹریکٹ کی وضاحت کرتا ہے۔
// ایک کنٹریکٹ فنکشنز اور ڈیٹا (اس کی حالت) کا مجموعہ ہوتا ہے۔
// ایک بار تعینات ہونے کے بعد، کنٹریکٹ ایتھیریم بلاک چین پر ایک مخصوص پتہ پر رہتا ہے۔
// مزید جانیں: 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) public {
// ایک سٹرنگ آرگومنٹ `initMessage` قبول کرتا ہے اور ویلیو کو سیٹ کرتا ہے
// کنٹریکٹ کے `message` سٹوریج متغیر میں)۔
message = initMessage;
}
// ایک پبلک فنکشن جو سٹرنگ آرگومنٹ قبول کرتا ہے
// اور `message` سٹوریج متغیر کو اپ ڈیٹ کرتا ہے۔
function update(string memory newMessage) public {
message = newMessage;
}
}
ٹوکن
pragma solidity ^0.5.10;
contract Token {
// ایک `address` (پتہ) ای میل ایڈریس کے مترادف ہے - یہ ایتھیریم پر اکاؤنٹ کی شناخت کے لیے استعمال ہوتا ہے۔
// پتے کسی سمارٹ کنٹریکٹ یا بیرونی (صارف) اکاؤنٹس کی نمائندگی کر سکتے ہیں۔
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/types.html#address
address public owner;
// ایک `mapping` بنیادی طور پر ایک ہیش ٹیبل ڈیٹا سٹرکچر ہے۔
// یہ `mapping` ایک ان سائنڈ انٹیجر (ٹوکن بیلنس) کو ایک پتہ (ٹوکن ہولڈر) کے ساتھ تفویض کرتی ہے۔
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/types.html#mapping-types
mapping (address => uint) public balances;
// ایونٹس بلاک چین پر سرگرمی کی لاگنگ کی اجازت دیتے ہیں۔
// ایتھیریم کلائنٹس کنٹریکٹ کی حالت کی تبدیلیوں پر ردعمل ظاہر کرنے کے لیے ایونٹس کو سن سکتے ہیں۔
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#events
event Transfer(address from, address to, uint amount);
// کنٹریکٹ کے ڈیٹا کو شروع کرتا ہے، `owner` کو سیٹ کرتے ہوئے
// کنٹریکٹ بنانے والے کے پتہ پر۔
constructor() public {
// تمام سمارٹ کنٹریکٹ اپنے فنکشنز کو متحرک کرنے کے لیے بیرونی ٹرانزیکشنز پر انحصار کرتے ہیں۔
// `msg` ایک گلوبل متغیر ہے جس میں دی گئی ٹرانزیکشن کا متعلقہ ڈیٹا شامل ہوتا ہے،
// جیسے بھیجنے والے کا پتہ اور ٹرانزیکشن میں شامل ETH کی مالیت۔
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties
owner = msg.sender;
}
// نئے ٹوکنز کی ایک مقدار بناتا ہے اور انہیں ایک پتہ پر بھیجتا ہے۔
function mint(address receiver, uint amount) public {
// `require` ایک کنٹرول سٹرکچر ہے جو کچھ شرائط کو نافذ کرنے کے لیے استعمال ہوتا ہے۔
// اگر `require` سٹیٹمنٹ `false` ہو جائے، تو ایک ایکسیپشن متحرک ہو جاتی ہے،
// جو موجودہ کال کے دوران حالت میں کی گئی تمام تبدیلیوں کو واپس (revert) کر دیتی ہے۔
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions
// صرف کنٹریکٹ کا مالک ہی اس فنکشن کو کال کر سکتا ہے
require(msg.sender == owner, "You are not the owner.");
// ٹوکنز کی زیادہ سے زیادہ مقدار کو نافذ کرتا ہے
require(amount < 1e60, "Maximum issuance exceeded");
// `receiver` کے بیلنس میں `amount` کا اضافہ کرتا ہے
balances[receiver] += amount;
}
// کسی بھی کالر سے موجودہ ٹوکنز کی ایک مقدار کسی پتہ پر بھیجتا ہے۔
function transfer(address receiver, uint amount) public {
// بھیجنے والے کے پاس بھیجنے کے لیے کافی ٹوکنز ہونے چاہئیں
require(amount <= balances[msg.sender], "Insufficient balance.");
// دونوں پتوں کے ٹوکن بیلنس کو ایڈجسٹ کرتا ہے
balances[msg.sender] -= amount;
balances[receiver] += amount;
// پہلے سے بیان کردہ ایونٹ کو خارج (emit) کرتا ہے
emit Transfer(msg.sender, receiver, amount);
}
}
منفرد ڈیجیٹل اثاثہ
pragma solidity ^0.5.10;
// دوسری فائلوں سے سمبلز کو موجودہ کنٹریکٹ میں امپورٹ کرتا ہے۔
// اس صورت میں، OpenZeppelin سے ہیلپر کنٹریکٹس کا ایک سلسلہ۔
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#importing-other-source-files
import "../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "../node_modules/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "../node_modules/@openzeppelin/contracts/introspection/ERC165.sol";
import "../node_modules/@openzeppelin/contracts/math/SafeMath.sol";
// کی ورڈ `is` بیرونی کنٹریکٹس سے فنکشنز اور کی ورڈز کو وراثت (inherit) میں لینے کے لیے استعمال ہوتا ہے۔
// اس صورت میں، `CryptoPizza` کو `IERC721` اور `ERC165` کنٹریکٹس سے وراثت ملتی ہے۔
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#inheritance
contract CryptoPizza is IERC721, ERC165 {
// ریاضی کے آپریشنز کو محفوظ طریقے سے انجام دینے کے لیے OpenZeppelin کی SafeMath لائبریری کا استعمال کرتا ہے۔
// مزید جانیں: https://docs.openzeppelin.com/contracts/2.x/api/math#SafeMath
using SafeMath for uint256;
// Solidity میں مستقل (constant) حالت کے متغیرات دوسری زبانوں کی طرح ہوتے ہیں
// لیکن آپ کو ایک ایسے ایکسپریشن سے تفویض کرنا ہوگا جو کمپائل ٹائم پر مستقل ہو۔
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constant-state-variables
uint256 constant dnaDigits = 10;
uint256 constant dnaModulus = 10 ** dnaDigits;
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
// Struct ٹائپس آپ کو اپنی ٹائپ کی وضاحت کرنے دیتی ہیں
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/types.html#structs
struct Pizza {
string name;
uint256 dna;
}
// Pizza سٹرکٹس کی ایک خالی ایرے بناتا ہے
Pizza[] public pizzas;
// پیزا ID سے اس کے مالک کے پتہ تک میپنگ
mapping(uint256 => address) public pizzaToOwner;
// مالک کے پتہ سے ملکیتی ٹوکنز کی تعداد تک میپنگ
mapping(address => uint256) public ownerPizzaCount;
// ٹوکن ID سے منظور شدہ پتہ تک میپنگ
mapping(uint256 => address) pizzaApprovals;
// آپ میپنگز کو نیسٹ (nest) کر سکتے ہیں، یہ مثال مالک کو آپریٹر کی منظوریوں سے میپ کرتی ہے
mapping(address => mapping(address => bool)) private operatorApprovals;
// سٹرنگ (نام) اور DNA سے ایک بے ترتیب (random) پیزا بنانے کے لیے اندرونی فنکشن
function _createPizza(string memory _name, uint256 _dna)
// کی ورڈ `internal` کا مطلب ہے کہ یہ فنکشن صرف نظر آتا ہے
// اس کنٹریکٹ اور ان کنٹریکٹس کے اندر جو اس کنٹریکٹ سے اخذ (derive) کیے گئے ہیں
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#visibility-and-getters
internal
// `isUnique` ایک فنکشن موڈیفائر ہے جو چیک کرتا ہے کہ آیا پیزا پہلے سے موجود ہے
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html#function-modifiers
isUnique(_name, _dna)
{
// پیزا کو پیزا کی ایرے میں شامل کرتا ہے اور id حاصل کرتا ہے
uint256 id = SafeMath.sub(pizzas.push(Pizza(_name, _dna)), 1);
// چیک کرتا ہے کہ پیزا کا مالک وہی ہے جو موجودہ صارف ہے
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions
// نوٹ کریں کہ address(0) زیرو پتہ ہے،
// جو ظاہر کرتا ہے کہ pizza[id] ابھی تک کسی خاص صارف کو الاٹ نہیں کیا گیا ہے۔
assert(pizzaToOwner[id] == address(0));
// پیزا کو مالک سے میپ کرتا ہے
pizzaToOwner[id] = msg.sender;
ownerPizzaCount[msg.sender] = SafeMath.add(
ownerPizzaCount[msg.sender],
1
);
}
// سٹرنگ (نام) سے ایک بے ترتیب پیزا بناتا ہے
function createRandomPizza(string memory _name) public {
uint256 randDna = generateRandomDna(_name, msg.sender);
_createPizza(_name, randDna);
}
// سٹرنگ (نام) اور مالک (بنانے والے) کے پتہ سے بے ترتیب DNA تیار کرتا ہے
function generateRandomDna(string memory _str, address _owner)
public
// وہ فنکشنز جنہیں `pure` کے طور پر نشان زد کیا گیا ہے، حالت کو نہ پڑھنے اور نہ ہی اس میں ترمیم کرنے کا وعدہ کرتے ہیں
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#pure-functions
pure
returns (uint256)
{
// سٹرنگ (نام) + پتہ (مالک) سے بے ترتیب uint تیار کرتا ہے
uint256 rand = uint256(keccak256(abi.encodePacked(_str))) +
uint256(_owner);
rand = rand % dnaModulus;
return rand;
}
// مالک کے ذریعہ پائے جانے والے پیزا کی ایرے واپس کرتا ہے
function getPizzasByOwner(address _owner)
public
// وہ فنکشنز جنہیں `view` کے طور پر نشان زد کیا گیا ہے، حالت میں ترمیم نہ کرنے کا وعدہ کرتے ہیں
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#view-functions
view
returns (uint256[] memory)
{
// `memory` سٹوریج لوکیشن کا استعمال کرتا ہے تاکہ ویلیوز کو صرف
// اس فنکشن کال کے لائف سائیکل کے لیے محفوظ کیا جا سکے۔
// مزید جانیں: https://solidity.readthedocs.io/en/v0.5.10/introduction-to-smart-contracts.html#storage-memory-and-the-stack
uint256[] memory result = new uint256[](ownerPizzaCount[_owner]);
uint256 counter = 0;
for (uint256 i = 0; i < pizzas.length; i++) {
if (pizzaToOwner[i] == _owner) {
result[counter] = i;
counter++;
}
}
return result;
}
// پیزا اور ملکیت کو دوسرے پتہ پر منتقل کرتا ہے
function transferFrom(address _from, address _to, uint256 _pizzaId) public {
require(_from != address(0) && _to != address(0), "Invalid address.");
require(_exists(_pizzaId), "Pizza does not exist.");
require(_from != _to, "Cannot transfer to the same address.");
require(_isApprovedOrOwner(msg.sender, _pizzaId), "Address is not approved.");
ownerPizzaCount[_to] = SafeMath.add(ownerPizzaCount[_to], 1);
ownerPizzaCount[_from] = SafeMath.sub(ownerPizzaCount[_from], 1);
pizzaToOwner[_pizzaId] = _to;
// امپورٹ کیے گئے IERC721 کنٹریکٹ میں بیان کردہ ایونٹ کو خارج کرتا ہے
emit Transfer(_from, _to, _pizzaId);
_clearApproval(_to, _pizzaId);
}
/**
* دیے گئے ٹوکن ID کی ملکیت کو محفوظ طریقے سے دوسرے پتہ پر منتقل کرتا ہے
* اگر ہدف کا پتہ ایک کنٹریکٹ ہے، تو اسے `onERC721Received` کو لاگو کرنا چاہیے،
* جسے محفوظ منتقلی پر کال کیا جاتا ہے، اور جادوئی ویلیو (magic value) واپس کرنی چاہیے
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
* بصورت دیگر، منتقلی واپس (revert) کر دی جاتی ہے۔
*/
function safeTransferFrom(address from, address to, uint256 pizzaId)
public
{
// solium-disable-next-line arg-overflow
this.safeTransferFrom(from, to, pizzaId, "");
}
/**
* دیے گئے ٹوکن ID کی ملکیت کو محفوظ طریقے سے دوسرے پتہ پر منتقل کرتا ہے
* اگر ہدف کا پتہ ایک کنٹریکٹ ہے، تو اسے `onERC721Received` کو لاگو کرنا چاہیے،
* جسے محفوظ منتقلی پر کال کیا جاتا ہے، اور جادوئی ویلیو (magic value) واپس کرنی چاہیے
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
* بصورت دیگر، منتقلی واپس (revert) کر دی جاتی ہے۔
*/
function safeTransferFrom(
address from,
address to,
uint256 pizzaId,
bytes memory _data
) public {
this.transferFrom(from, to, pizzaId);
require(_checkOnERC721Received(from, to, pizzaId, _data), "Must implement onERC721Received.");
}
/**
* ہدف کے پتہ پر `onERC721Received` کو شروع (invoke) کرنے کے لیے اندرونی فنکشن
* اگر ہدف کا پتہ کنٹریکٹ نہیں ہے تو کال پر عمل نہیں کیا جاتا
*/
function _checkOnERC721Received(
address from,
address to,
uint256 pizzaId,
bytes memory _data
) internal returns (bool) {
if (!isContract(to)) {
return true;
}
bytes4 retval = IERC721Receiver(to).onERC721Received(
msg.sender,
from,
pizzaId,
_data
);
return (retval == _ERC721_RECEIVED);
}
// پیزا کو جلاتا (burn) ہے - ٹوکن کو مکمل طور پر تباہ کر دیتا ہے
// `external` فنکشن موڈیفائر کا مطلب ہے کہ یہ فنکشن
// کنٹریکٹ انٹرفیس کا حصہ ہے اور دوسرے کنٹریکٹس اسے کال کر سکتے ہیں
function burn(uint256 _pizzaId) external {
require(msg.sender != address(0), "Invalid address.");
require(_exists(_pizzaId), "Pizza does not exist.");
require(_isApprovedOrOwner(msg.sender, _pizzaId), "Address is not approved.");
ownerPizzaCount[msg.sender] = SafeMath.sub(
ownerPizzaCount[msg.sender],
1
);
pizzaToOwner[_pizzaId] = address(0);
}
// پتہ کے لحاظ سے پیزا کی گنتی واپس کرتا ہے
function balanceOf(address _owner) public view returns (uint256 _balance) {
return ownerPizzaCount[_owner];
}
// id کے ذریعہ پائے جانے والے پیزا کا مالک واپس کرتا ہے
function ownerOf(uint256 _pizzaId) public view returns (address _owner) {
address owner = pizzaToOwner[_pizzaId];
require(owner != address(0), "Invalid Pizza ID.");
return owner;
}
// پیزا کی ملکیت منتقل کرنے کے لیے دوسرے پتہ کو منظور کرتا ہے
function approve(address _to, uint256 _pizzaId) public {
require(msg.sender == pizzaToOwner[_pizzaId], "Must be the Pizza owner.");
pizzaApprovals[_pizzaId] = _to;
emit Approval(msg.sender, _to, _pizzaId);
}
// مخصوص پیزا کے لیے منظور شدہ پتہ واپس کرتا ہے
function getApproved(uint256 _pizzaId)
public
view
returns (address operator)
{
require(_exists(_pizzaId), "Pizza does not exist.");
return pizzaApprovals[_pizzaId];
}
/**
* دیے گئے ٹوکن ID کی موجودہ منظوری کو صاف کرنے کے لیے پرائیویٹ فنکشن
* اگر دیا گیا پتہ واقعی ٹوکن کا مالک نہیں ہے تو واپس (revert) کر دیتا ہے
*/
function _clearApproval(address owner, uint256 _pizzaId) private {
require(pizzaToOwner[_pizzaId] == owner, "Must be pizza owner.");
require(_exists(_pizzaId), "Pizza does not exist.");
if (pizzaApprovals[_pizzaId] != address(0)) {
pizzaApprovals[_pizzaId] = address(0);
}
}
/*
* دیے گئے آپریٹر کی منظوری کو سیٹ یا ان سیٹ کرتا ہے
* ایک آپریٹر کو بھیجنے والے کی جانب سے اس کے تمام ٹوکنز منتقل کرنے کی اجازت ہوتی ہے
*/
function setApprovalForAll(address to, bool approved) public {
require(to != msg.sender, "Cannot approve own address");
operatorApprovals[msg.sender][to] = approved;
emit ApprovalForAll(msg.sender, to, approved);
}
// بتاتا ہے کہ آیا کسی آپریٹر کو دیے گئے مالک کی طرف سے منظور کیا گیا ہے
function isApprovedForAll(address owner, address operator)
public
view
returns (bool)
{
return operatorApprovals[owner][operator];
}
// پیزا کی ملکیت لیتا ہے - صرف منظور شدہ صارفین کے لیے
function takeOwnership(uint256 _pizzaId) public {
require(_isApprovedOrOwner(msg.sender, _pizzaId), "Address is not approved.");
address owner = this.ownerOf(_pizzaId);
this.transferFrom(owner, msg.sender, _pizzaId);
}
// چیک کرتا ہے کہ آیا پیزا موجود ہے
function _exists(uint256 pizzaId) internal view returns (bool) {
address owner = pizzaToOwner[pizzaId];
return owner != address(0);
}
// چیک کرتا ہے کہ آیا پتہ مالک ہے یا پیزا منتقل کرنے کے لیے منظور شدہ ہے
function _isApprovedOrOwner(address spender, uint256 pizzaId)
internal
view
returns (bool)
{
address owner = pizzaToOwner[pizzaId];
// solium چیک کو غیر فعال کریں کیونکہ
// https://github.com/duaraghav8/Solium/issues/175
// solium-disable-next-line operator-whitespace
return (spender == owner ||
this.getApproved(pizzaId) == spender ||
this.isApprovedForAll(owner, spender));
}
// چیک کریں کہ آیا پیزا منفرد ہے اور ابھی تک موجود نہیں ہے
modifier isUnique(string memory _name, uint256 _dna) {
bool result = true;
for (uint256 i = 0; i < pizzas.length; i++) {
if (
keccak256(abi.encodePacked(pizzas[i].name)) ==
keccak256(abi.encodePacked(_name)) &&
pizzas[i].dna == _dna
) {
result = false;
}
}
require(result, "Pizza with such name already exists.");
_;
}
// واپس کرتا ہے کہ آیا ہدف کا پتہ ایک کنٹریکٹ ہے
function isContract(address account) internal view returns (bool) {
uint256 size;
// فی الحال یہ چیک کرنے کا کوئی بہتر طریقہ نہیں ہے کہ آیا کسی پتہ میں کوئی کنٹریکٹ ہے
// سوائے اس کے کہ اس پتہ پر کوڈ کا سائز چیک کیا جائے۔
// دیکھیں https://ethereum.stackexchange.com/a/14016/36603
// اس بارے میں مزید تفصیلات کے لیے کہ یہ کیسے کام کرتا ہے۔
// TODO Serenity ریلیز سے پہلے اسے دوبارہ چیک کریں، کیونکہ تب تمام پتے
// کنٹریکٹس ہوں گے۔
// solium-disable-next-line security/no-inline-assembly
assembly {
size := extcodesize(account)
}
return size > 0;
}
}
مزید مطالعہ
سمارٹ کنٹریکٹس کے مزید مکمل جائزے کے لیے Solidity اور Vyper کی دستاویزات دیکھیں:
متعلقہ موضوعات
متعلقہ ٹیوٹوریلز
- کنٹریکٹ کے سائز کی حد سے نمٹنے کے لیے کنٹریکٹس کو چھوٹا کرنا – آپ کے سمارٹ کنٹریکٹ کا سائز کم کرنے کے لیے کچھ عملی تجاویز۔
- ایونٹس کے ساتھ سمارٹ کنٹریکٹس سے ڈیٹا لاگ کرنا – سمارٹ کنٹریکٹ ایونٹس کا تعارف اور آپ انہیں ڈیٹا لاگ کرنے کے لیے کیسے استعمال کر سکتے ہیں۔
- Solidity سے دوسرے کنٹریکٹس کے ساتھ تعامل کریں – موجودہ کنٹریکٹ سے سمارٹ کنٹریکٹ کو کیسے تعینات کریں اور اس کے ساتھ تعامل کریں۔