دنیای ارزهای دیجیتال فضای بسیار جدیدی را ایجاد کرده که افراد و شرکت ها بتوانند توکن های خود را ایجاد کرده و از آن برای مقاصد داخلی یا حتی انتقال وجه استفاده کنند.
بسترهای زیادی برای ساخت توکن وجود دارد. در این مقاله بستر اتریوم که در حال حاضر معروف ترین و متداول ترین بستر برای ساخت توکن هست رو انتخاب میکنیم.
دقت داشته باشید هدف ما فعلا ساخت یک توکن هست و برای این کار نیاز به یک بستر مانند اتریوم، سولانا یا غیره داریم. اکثر شتکوین ها در واقع توکن هایی هستند که محبوبیت زیادی بین کاربران پیدا کرده اند.
برای ایجاد یک توکن از زبان برنامه نویسی Solidity استفاده میکنیم. این زبان شباهت زیادی به زبان های پیشرفته مانند جاوا و C++ دارد.
در این کد ابتدا متغیرهای لازم را تعریف میکنیم. سپس داده ها رو مشخص کرده و تابع ها که در واقع همان functionهای قابل اجرا در توکن هستند را برنامه نویسی می کنیم.
این روش به ما کمک میکند هر function که مورد نظر ما نبود را به راحتی پاک کنیم و مشکلی هم در کل کد ایجاد نخواهد شد (به شریط که آن تابع در بخش های دیگر فراخوانی نشود)
برای اجرا و کامپایل کد از Remix IDE استفاده میکنیم که قبل از تست در شبکه اصلی بتونیم ایرادات احتمالی را ببینیم و تغییرات لازم را اعمال کنیم.
خب بدون اتلاف وقت به سراغ کد میرویم:
pragma solidity ^0.8.0;
interface ERC20 {
function totalSupply() external pure returns (uint256 _totalSupply);
function balanceOf(address _owner) external view returns (uint256 balance);
function transfer(address _to, uint256 _value) external returns (bool success);
function transferFrom(
address _from,
address _to,
uint256 _value
) external returns (bool success);
function approve(address _spender, uint256 _value) external returns (bool success);
function allowance(address _owner, address _sender) external view returns (uint256 remaining);
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
contract MyERC20Token is ERC20 {
string public constant symbol = "MET"
string public constant name = "My ERC20 Token"
uint8 public constant decimals = 18;
// 1,000,000 + 18 zeros
uint256 private constant __totalSupply = 1000000000000000000000000;
// this mapping is where we store the balances of an address
mapping(address => uint256) private __balanceOf;
// This is for the approval function to determine how much an address can spend
mapping(address => mapping(address => uint256)) private __allowances;
constructor() {
__balanceOf[msg.sender] = __totalSupply; //the creator of the contract has the total supply and no one can create tokens
}
// constant value that does not change/ returns the amount of initial tokens to display
function totalSupply() public pure override returns (uint256) {
return __totalSupply;
}
// returns the balance of a specific address
function balanceOf(address _address) public view override returns (uint256) {
return __balanceOf[_address];
}
// Transfer an amount of tokens to another address.
// Pre-checks:
// - The transfer needs to be > 0
// - does the msg.sender have enough tokens to forfill the transfer
// Output:
// - decrease the balance of the sender
// - increase the balance of the to address
// - Emit transfer event
function transfer(address _to, uint256 _value) public override returns (bool) {
if (_value > 0 && _value <= balanceOf(msg.sender)) {
__balanceOf[msg.sender] -= _value;
__balanceOf[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}
return false;
}
// this allows someone else (a 3rd party) to transfer from my wallet to someone elses wallet
// Pre-checks:
// - The transfer needs to be > 0
// - and the 3rd party has an allowance of > 0
// - and the allowance is >= the value of the transfer
// - and it is not a contract
// Output:
// - decrease the balance of the from account
// - increase the balance of the to account
// - Emit transfer event
function transferFrom(
address _from,
address _to,
uint256 _value
) public override returns (bool) {
if (
_value > 0 &&
__allowances[_from][msg.sender] > 0 &&
__allowances[_from][msg.sender] >= _value &&
!isContract(_to)
) {
__balanceOf[_from] -= _value;
__balanceOf[_to] += _value;
emit Transfer(_from, _to, _value);
return true;
}
return false;
}
// This check is to determine if we are sending to a contract?
// Is there code at this address? If the code size is greater then 0 then it is a contract.
function isContract(address _address) public view returns (bool) {
uint256 codeSize;
assembly {
codeSize := extcodesize(_address)
}
return codeSize > 0;
}
// allows a spender address to spend a specific amount of value
function approve(address _spender, uint256 _value) external override returns (bool) {
__allowances[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
// shows how much a spender has the approval to spend to a specific address
function allowance(address _owner, address _spender) external view returns (uint256 remaining) {
return __allowances[_owner][_spender];
} }
خب نیازی نیست بترسین. قدم به قدم این کد رو توضیح خواهیم داد.
اما اول بدونین که این کد رو میتونین در remix IDE اپلود کنید.
مطابق با تصویر بالا، اول start coding و بعد در بخش contract یک فایل جدید ایجاد کرده و اسم دلخواه خودتون رو بنویسید. سپس enter بزنید تا وارد صفحه کدنویسی بشید و کد بالا رو اونجا قرار بدید
این یک قرارداد هوشمند در زبان Solidity است که یک توکن ساده مبتنی بر استاندارد ERC-20 (شبکه اتریوم)پیادهسازی میکند. حالا بریم سراغ توضیح خط به خط که هرجا لازم داشتید، کد را تغییر داده و توکن مناسب خود را بسازید.
pragma solidity ^0.8.0;
این دستور به کامپایلر میگوید که نسخه 0.8.0 یا بالاتر از Solidity برای کامپایل این قرارداد استفاده شود.
interface ERC20 { ... }
اینجا یک اینترفیس برای توکن ERC-20 تعریف شده که مجموعهای از فانکشنهای استاندارد مثل totalSupply
، transfer
و approve
دارد.
totalSupply()
: مقدار کل توکنهای موجود را برمیگرداند.balanceOf(address _owner)
: مقدار توکنهای یک آدرس خاص را برمیگرداند.transfer(address _to, uint256 _value)
: مقدار _value
توکن را به آدرس _to
ارسال میکند.transferFrom(address _from, address _to, uint256 _value)
: انتقال توکن توسط شخص ثالث.approve(address _spender, uint256 _value)
: اجازه میدهد که _spender
مقدار _value
از حساب شما خرج کند.allowance(address _owner, address _spender)
: مقدار توکنی که یک آدرس اجازه دارد خرج کند را برمیگرداند.Transfer
: هنگام انتقال توکن فعال میشود.Approval
: هنگام تأیید مقدار قابل خرج توسط شخص ثالث فعال میشود.contract MyERC20Token is ERC20 {
اینجا قرارداد MyERC20Token بر اساس ERC20 ساخته شده و شامل اطلاعات مربوط به یک توکن است.
string public constant symbol = "MET" string public constant name = "My ERC20 Token" uint8 public constant decimals = 18;
symbol
: نماد توکن (MET
).name
: نام توکن (My ERC20 Token
).decimals
: تعداد رقمهای اعشار (18 رقم، مثل ETH).uint256 private constant __totalSupply = 1000000000000000000000000;
mapping(address => uint256) private __balanceOf;
mapping(address => mapping(address => uint256)) private __allowances;
constructor() { __balanceOf[msg.sender] = __totalSupply; }
function totalSupply() public pure override returns (uint256) { return __totalSupply; }
function balanceOf(address _address) public view override returns (uint256) { return __balanceOf[_address]; }
function transfer(address _to, uint256 _value) public override returns (bool) { if (_value > 0 && _value <= balanceOf(msg.sender)) { __balanceOf[msg.sender] -= _value; __balanceOf[_to] += _value; emit Transfer(msg.sender, _to, _value); return true; } return false; }
توضیح بیشتر:
_value
باید بیشتر از صفر باشد.عملیات انتقال:
_value
از حساب فرستنده کم میشود._value
به حساب گیرنده اضافه میشود.Transfer
اجرا میشود.function transferFrom(address _from, address _to, uint256 _value) public override returns (bool) { if ( _value > 0 && __allowances[_from][msg.sender] > 0 && __allowances[_from][msg.sender] >= _value && !isContract(_to) ) { __balanceOf[_from] -= _value; __balanceOf[_to] += _value; emit Transfer(_from, _to, _value); return true; } return false; }
بررسی بیشتر:
_value
باید بیشتر از صفر باشد.msg.sender
) باید اجازهی کافی داشته باشد.isContract
) (جلوتر توضیح می دهم)عملیات انتقال:
_value
از حساب _from
کم میشود._value
به حساب _to
اضافه میشود.Transfer
اجرا میشود.function isContract(address _address) public view returns (bool) { uint256 codeSize; assembly { codeSize := extcodesize(_address) } return codeSize > 0; }
function approve(address _spender, uint256 _value) external override returns (bool) { __allowances[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); return true; }
_spender
اجازه میدهد که مقدار _value
از حساب کاربر خرج کند.Approval
اجرا میشود.function allowance(address _owner, address _spender) external view returns (uint256 remaining) { return __allowances[_owner][_spender]; }
_spender
اجازه دارد از _owner
خرج کند را برمیگرداند.به زبان ساده کد بالا یک توکن ERC-20 می باشد که به نام MET ساختیم و عملیات های ابتدایی برای یک توکن را برای شما انجام می دهد:
transfer
، transferFrom
) را فراهم میکند.approve
، allowance
) را فراهم میکند.خب قدم بعدی تست این کد در بستر Remix IDE می باشد.
مطابق با عکس بالا در منوی سمت چپ وارد solidity compiler شده و دکمه مشخص شده را کلیک می کنید.
در صورتی که کامپایل با موفقیت انجام شود کنار تب compiler یک تیک سبز ایجاد می شود. حالا کد شما اماده اجرا شدن در بستر تست می باشد.
در تب پایین کامپایل Deploy & run transactions گزینه مشخص شده رو کلیک میکنید. در صورتی که ادرس را در بخش owner وارد کنید اطلاعات توکن به شرح زیر نشان داده می شود:
طبیعی است که در محیط Remix چون یک اکانت داریم نمیتوانیم انتقال و تست را به درستی انجام دهیم. برای این کار باید به یک کیف پول مانند metamask متصل شویم.
بعد از ایجاد یک کیف پول متاماسک، از طریق import token کد را در متاماسک اجرا کرده و ذخیره می کنیم. دقت کنید که اسم و تعداد اعشاری که در متاماسک اعلام میکنید با آنچه در کد نوشته شده تطابق داشته باشد
برای اینکه یک توکن واقعا ارزش معامله داشته باشد باید در شبکه اصلی اتریوم (mainnet) اجرا شده و یا در صرافی های غیر متمرکز مانند Uniswap لیست شود.
برای اینکار شما نیاز به یک استخر پول (liquidity pool) دارید
بعد از ایجاد استخر نقدینگی در uniswap امکان معامله توکن توسط سایرین وجود خواهد داشت.