Cách chuyển code erc20 solidity sang ink!
Định nghĩa on-chain storage và events và errors
Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract ERC20 {
string public name;
string public symbol;
uint8 public immutable decimals;
uint256 public immutable totalSupply;
mapping(address => uint256) _balances;
// spender => (owner => no of tokens allowed)
mapping(address => mapping(address => uint256)) _allowances;
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
Ink!
/// A simple ERC-20 contract.
#[ink(storage)]
#[derive(Default)]
pub struct MyErc20 {
/// Total token supply.
total_supply: Balance,
/// Token decimals
decimals: u8,
/// Token name
name: String,
/// Token symbol
symbol: String,
/// Mapping from owner to number of owned token.
balances: Mapping<AccountId, Balance>,
/// Mapping of the token amount which an account is allowed to withdraw
/// from another account.
allowances: Mapping<(AccountId, AccountId), Balance>,
}
/// Event emitted when a token transfer occurs.
#[ink(event)]
pub struct Transfer {
#[ink(topic)]
from: Option<AccountId>,
#[ink(topic)]
to: Option<AccountId>,
value: Balance,
}
/// Event emitted when an approval occurs that `spender` is allowed to withdraw
/// up to the amount of `value` tokens from `owner`.
#[ink(event)]
pub struct Approval {
#[ink(topic)]
owner: AccountId,
#[ink(topic)]
spender: AccountId,
value: Balance,
}
/// The ERC-20 error types.
#[derive(Debug, PartialEq, Eq)]
#[ink::scale_derive(Encode, Decode, TypeInfo)]
pub enum Error {
/// Returned if not enough balance to fulfill a request is available.
InsufficientBalance,
/// Returned if not enough allowance to fulfill a request is available.
InsufficientAllowance,
}
Tạo constructor
Solidity
constructor(string memory _name, string memory _symbol, uint256 _totalSupply) {
name = _name;
symbol = _symbol;
decimals = 18;
totalSupply = _totalSupply;
_balances[msg.sender] = _totalSupply;
}
Ink!
/// Creates a new ERC-20 contract with the specified initial supply.
#[ink(constructor)]
pub fn new(name: String, symbol: String, total_supply: Balance) -> Self {
let mut balances = Mapping::default();
let caller = Self::env().caller();
balances.insert(caller, &total_supply);
Self::env().emit_event(Transfer {
from: None,
to: Some(caller),
value: total_supply,
});
Self {
total_supply,
decimals: 18u8,
name,
symbol,
balances,
allowances: Default::default(),
}
}
Tạo hàm balanceOf
Solidity
function balanceOf(address _owner) public view returns(uint256) {
require(_owner != address(0), "!ZA");
return _balances[_owner];
}
Ink!
#[ink(message)]
pub fn balance_of(&self, owner: AccountId) -> Balance {
self.balance_of_impl(&owner)
}
Tạo hàm transfer
Solidity
function transfer(address _to, uint256 _value) public returns(bool) {
require((_balances[msg.sender] >= _value) && (_balances[msg.sender] != 0), "!Bal");
_balances[msg.sender] -= _value;
_balances[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}
Ink!
#[ink(message)]
pub fn transfer(&mut self, to: AccountId, value: Balance) -> Result<()> {
let from = self.env().caller();
self.transfer_from_to(&from, &to, value)
}
Tạo hàm transferFrom
Solidity
function transferFrom(address _from, address _to, uint256 _value) public returns(bool) {
require(_allowances[msg.sender][_from] >= _value, "!Alw");
require((_balances[_from] >= _value) && (_balances[_from] != 0), "!Bal");
_balances[_from] -= _value;
_balances[_to] += _value;
_allowances[msg.sender][_from] -= _value;
emit Transfer(_from, _to, _value);
return true;
}
Ink!
#[ink(message)]
pub fn transfer_from(
&mut self,
from: AccountId,
to: AccountId,
value: Balance,
) -> Result<()> {
let caller = self.env().caller();
let allowance = self.allowance_impl(&from, &caller);
if allowance < value {
return Err(Error::InsufficientAllowance)
}
self.transfer_from_to(&from, &to, value)?;
// We checked that allowance >= value
#[allow(clippy::arithmetic_side_effects)]
self.allowances
.insert((&from, &caller), &(allowance - value));
Ok(())
}
Tạo hàm approve
Solidity
function approve(address _spender, uint256 _value) public returns(bool) {
require(_balances[msg.sender] >= _value, "!Bal");
_allowances[_spender][msg.sender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
Ink!
#[ink(message)]
pub fn approve(&mut self, spender: AccountId, value: Balance) -> Result<()> {
let owner = self.env().caller();
self.allowances.insert((&owner, &spender), &value);
self.env().emit_event(Approval {
owner,
spender,
value,
});
Ok(())
}
Tạo hàm allowance
Solidity
function allowance(address _owner, address _spender) public view returns(uint256) {
return _allowances[_spender][_owner];
}
Ink!
#[ink(message)]
pub fn allowance(&self, owner: AccountId, spender: AccountId) -> Balance {
self.allowance_impl(&owner, &spender)
}
Tạo helper function
Lấy thông tin balance của 1 account
Ink!
fn balance_of_impl(&self, owner: &AccountId) -> Balance {
self.balances.get(owner).unwrap_or_default()
}
Chuyển token từ from
sang to
Ink!
fn transfer_from_to(
&mut self,
from: &AccountId,
to: &AccountId,
value: Balance,
) -> Result<()> {
let from_balance = self.balance_of_impl(from);
if from_balance < value {
return Err(Error::InsufficientBalance)
}
// We checked that from_balance >= value
#[allow(clippy::arithmetic_side_effects)]
self.balances.insert(from, &(from_balance - value));
let to_balance = self.balance_of_impl(to);
self.balances
.insert(to, &(to_balance.checked_add(value).unwrap()));
self.env().emit_event(Transfer {
from: Some(*from),
to: Some(*to),
value,
});
Ok(())
}
Số lượng token cho phép
Ink!
fn allowance_impl(&self, owner: &AccountId, spender: &AccountId) -> Balance {
self.allowances.get((owner, spender)).unwrap_or_default()
}