Chuyển code erc20 solidity sang ink!

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()
    }

Tài liệu tham khảo: