1. 首页
  2. 区块链手艺

利用 Ink!开辟 Substrate ERC20 智能合约

1 情况搭建

1.1 装配Substrate节点

$ git clone [email protected]:paritytech/substrate.git
$ cd substrate
(master)$ git checkout -b v2.0.0-rc4 v2.0.0-rc4
切换到一个新分支 ‘v2.0.0-rc4’
(v2.0.0-rc4)$ cargo build –release

1.2 装配cargo contract插件

•装配号令

$ cargo install cargo-contract –vers 0.6.1 –force

•辅佐手册

$ cargo contract –help
cargo-contract 0.6.1
Utilities to develop Wasm smart contracts

USAGE:
    cargo contract <SUBCOMMAND>

OPTIONS:
    -h, –help       Prints help information
    -V, –version    Prints version information

SUBCOMMANDS:
    new                  Setup and create a new smart contract project
    build                Compiles the smart contract
    generate-metadata    Generate contract metadata artifacts
    test                 Test the smart contract off-chain
    help                 Prints this message or the help of the given subcommand(s)

2 ERC20合约介绍

2.1 甚么是ERC20规范

ERC20 通证规范(ERC20 Token Standard)是颠末以太坊创建通证时的一种规范。遵循 ERC20 的规范能够或许编写一个智能合约,创建“可交换通证”。它并非逼迫请求,但遵循这个规范,所创建的通证能够或许与浩繁生意所、钱包等遏制交互,它现在已被行业遍及蒙受。

ERC20界说了一些规范的接口函数:balanceOf 、 totalSupply 、transfer 、transferFrom 、approve和allowance 。和一些可选的字段,比方通证称呼、标记和小数保管位数等。

利用 Ink!开辟 Substrate ERC20 智能合约

详见:http://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md

2.2 ERC20接口

contract ERC20 {
   function totalSupply() constant returns (uint theTotalSupply);
   function balanceOf(address _owner) constant returns (uint balance);
   function transfer(address _to, uint _value) returns (bool success);
   function transferFrom(address _from, address _to, uint _value) returns (bool success);
   function approve(address _spender, uint _value) returns (bool success);
   function allowance(address _owner, address _spender) constant returns (uint remaining);
   event Transfer(address indexed _from, address indexed _to, uint _value);
   event Approval(address indexed _owner, address indexed _spender, uint _value);
}

•服从介绍:

利用 Ink!开辟 Substrate ERC20 智能合约

3 ERC20合约开辟

3.1 创建合约工程

履行号令后,会天生2个文件,此中lib.rs会包含一些根抵框架,咱们能够或许在此根抵上开辟咱们的合约。

$ cargo contract new erc20
    Created contract erc20

$ tree erc20/
erc20/
├── Cargo.toml
└── lib.rs

3.2 合约存储创建

    #[ink(storage)]
    struct Erc20 {
        /// 代币刊行总量
        total_supply: storage::Value<Balance>,
        /// 用户及余额映照
        balances: storage::HashMap<AccountId, Balance>,
    }

3.3 合约布局方法创建

        #[ink(constructor)]
        fn new(&mut self, initial_supply: Balance) {
            // 获得合约创建者
            let caller = self.env().caller();
            // 设置刊行总量
            self.total_supply.set(initial_supply);
            // 合约创建者具有统统刊行代币
            self.balances.insert(caller, initial_supply);
        }

3.4 合约接口方法创建

(1)查问代币刊行总量接口

        #[ink(message)]
        fn total_supply(&self) -> Balance {
            *self.total_supply
        }

(2)查问用户代币余额接口

        #[ink(message)]
        fn balance_of(&self, owner: AccountId) -> Balance {
            self.balance_of_or_zero(&owner)
        }

        // 东西方法:若用户未被初始化,代币余额置为0
        fn balance_of_or_zero(&self, owner: &AccountId) -> Balance {
            *self.balances.get(owner).unwrap_or(&0)

(3)转账接口

        #[ink(message)]
        fn transfer(&mut self, to: AccountId, value: Balance) -> bool {
            // 获得合约接口挪用者地点
            let from = self.env().caller();
            // 给采取地点转出指定金额代币
            self.transfer_from_to(from, to, value)
        }

        fn transfer_from_to(&mut self, from: AccountId, to: AccountId, value: Balance) -> bool {
            // 获得合约挪用者账户余额
            let from_balance = self.balance_of_or_zero(&from);
            if from_balance < value {
                return false
            }
            // 获得合约蒙受者账户余额(代币采取者账户能够或许未被初始化,颠末此方法将其余额初始化为0)
            let to_balance = self.balance_of_or_zero(&to);
            // 发送者余额增添指定数目
            self.balances.insert(from, from_balance – value);
            // 采取者余额增加指定命目
            self.balances.insert(to, to_balance + value);
            true
        }

咱们寄望到,在遏制余额的增减时,并未像以太坊的solidity智能合约,利用额外的SafeMath接口,这是因为ink!供给了内置防溢出掩护,颠末在Cargo.toml 设置装备摆设文件中,增加以下设置装备摆设来供给该安然机制:

[profile.release]
panic = “abort”           <– Panics shall be treated as aborts: reduces binary size
lto = true                <– enable link-time-optimization: more efficient codegen
opt-level = “z”           <– Optimize for small binary output
overflow-checks = true    <– Arithmetic overflow protection

(4)授权转账——授权接口

颠末授权转账,挪用方能够或许授权指定账户,从其地点中安然的花费指定命目的代币。

需完美合约存储:

    #[ink(storage)]
    struct Erc20 {
        ……
        // (代币统统者, 代币授权利用者) -> 代币授权利用者可安排余额
        allowances: storage::HashMap<(AccountId, AccountId), Balance>,
    }
        #[ink(message)]
        fn approve(&mut self, spender: AccountId, value: Balance) -> bool {
            let owner = self.env().caller();
            // 代币统统者(owner)授权代币利用者(spender)可安排余额(value)
            self.allowances.insert((owner, spender), value);
            true
        }

(5)授权转账——余额查问

获得代币授权利用者残剩被许可转移的代币数目。

        #[ink(message)]
        fn allowance(&self, owner: AccountId, spender: AccountId) -> Balance {
            self.allowance_of_or_zero(&owner, &spender)
        }

(6)授权转账——转账接口

许可智能合约主动履行转账流程并代表统统者发送给定命目的代币

        #[ink(message)]
        fn transfer_from(&mut self, from: AccountId, to: AccountId, value: Balance) -> bool {
            let caller = self.env().caller();
            let allowance = self.allowance_of_or_zero(&from, &caller);
            if allowance < value {
                return false
            }
            self.allowances.insert((from, caller), allowance – value);
            self.transfer_from_to(from, to, value)
        }

        fn allowance_of_or_zero(&self, owner: &AccountId, spender: &AccountId) -> Balance {
            *self.allowances.get(&(*owner, *spender)).unwrap_or(&0)
        }

3.5 合约任务创建

•任务界说

    #[ink(event)]
    struct Transfer {
        #[ink(topic)]
        from: Option<AccountId>,
        #[ink(topic)]
        to: Option<AccountId>,
        #[ink(topic)]
        value: Balance,
    }

    #[ink(event)]
    struct Approval {
        #[ink(topic)]
        owner: AccountId,
        #[ink(topic)]
        spender: AccountId,
        #[ink(topic)]
        value: Balance,
    }

•合约布局任务

            self.env().emit_event(Transfer {
                from: None,
                to: Some(caller),
                value: initial_supply,
            });

•转账任务

            self.env().emit_event(Transfer {
                from: Some(from),
                to: Some(to),
                value,
            });

•授权任务

            self.env().emit_event(Approval {
                owner,
                spender,
                value,
            });

3.6 单位测试用例编写

        #[test]
        fn new_works() {
            let contract = Erc20::new(777);
            assert_eq!(contract.total_supply(), 777);
        }

        #[test]
        fn balance_works() {
            let contract = Erc20::new(100);
            assert_eq!(contract.total_supply(), 100);
            assert_eq!(contract.balance_of(AccountId::from([0x1; 32])), 100);
            assert_eq!(contract.balance_of(AccountId::from([0x0; 32])), 0);
        }

        #[test]
        fn transfer_works() {
            let mut contract = Erc20::new(100);
            assert_eq!(contract.balance_of(AccountId::from([0x1; 32])), 100);
            assert!(contract.transfer(AccountId::from([0x0; 32]), 10));
            assert_eq!(contract.balance_of(AccountId::from([0x0; 32])), 10);
            assert!(!contract.transfer(AccountId::from([0x0; 32]), 100));
        }

        #[test]
        fn transfer_from_works() {
            let mut contract = Erc20::new(100);
            assert_eq!(contract.balance_of(AccountId::from([0x1; 32])), 100);
            contract.approve(AccountId::from([0x1; 32]), 20);
            contract.transfer_from(AccountId::from([0x1; 32]), AccountId::from([0x0; 32]), 10);
            assert_eq!(contract.balance_of(AccountId::from([0x0; 32])), 10);
        }

跑测试用例:

$ cargo +nightly test

利用 Ink!开辟 Substrate ERC20 智能合约

3.7 无缺代码

#![cfg_attr(not(feature = “std”), no_std)]

use ink_lang as ink;

#[ink::contract(version = “0.1.0”)]
mod erc20 {
    use ink_core::storage;

    #[ink(storage)]
    struct Erc20 {
        /// The total supply.
        total_supply: storage::Value<Balance>,
        /// The balance of each user.
        balances: storage::HashMap<AccountId, Balance>,
        /// Approval spender on behalf of the message’s sender.
        allowances: storage::HashMap<(AccountId, AccountId), Balance>,
    }

    #[ink(event)]
    struct Transfer {
        #[ink(topic)]
        from: Option<AccountId>,
        #[ink(topic)]
        to: Option<AccountId>,
        #[ink(topic)]
        value: Balance,
    }

    #[ink(event)]
    struct Approval {
        #[ink(topic)]
        owner: AccountId,
        #[ink(topic)]
        spender: AccountId,
        #[ink(topic)]
        value: Balance,
    }

    impl Erc20 {
        #[ink(constructor)]
        fn new(&mut self, initial_supply: Balance) {
            let caller = self.env().caller();
            self.total_supply.set(initial_supply);
            self.balances.insert(caller, initial_supply);
            self.env().emit_event(Transfer {
                from: None,
                to: Some(caller),
                value: initial_supply,
            });
        }

        #[ink(message)]
        fn total_supply(&self) -> Balance {
            *self.total_supply
        }

        #[ink(message)]
        fn balance_of(&self, owner: AccountId) -> Balance {
            self.balance_of_or_zero(&owner)
        }

        #[ink(message)]
        fn approve(&mut self, spender: AccountId, value: Balance) -> bool {
            let owner = self.env().caller();
            self.allowances.insert((owner, spender), value);
            self.env().emit_event(Approval {
                owner,
                spender,
                value,
            });
            true
        }

        #[ink(message)]
        fn allowance(&self, owner: AccountId, spender: AccountId) -> Balance {
            self.allowance_of_or_zero(&owner, &spender)
        }

        #[ink(message)]
        fn transfer_from(&mut self, from: AccountId, to: AccountId, value: Balance) -> bool {
            let caller = self.env().caller();
            let allowance = self.allowance_of_or_zero(&from, &caller);
            if allowance < value {
                return false
            }
            self.allowances.insert((from, caller), allowance – value);
            self.transfer_from_to(from, to, value)
        }

        #[ink(message)]
        fn transfer(&mut self, to: AccountId, value: Balance) -> bool {
            let from = self.env().caller();
            self.transfer_from_to(from, to, value)
        }

        fn transfer_from_to(&mut self, from: AccountId, to: AccountId, value: Balance) -> bool {
            let from_balance = self.balance_of_or_zero(&from);
            if from_balance < value {
                return false
            }
            let to_balance = self.balance_of_or_zero(&to);
            self.balances.insert(from, from_balance – value);
            self.balances.insert(to, to_balance + value);
            self.env().emit_event(Transfer {
                from: Some(from),
                to: Some(to),
                value,
            });
            true
        }

        fn balance_of_or_zero(&self, owner: &AccountId) -> Balance {
            *self.balances.get(owner).unwrap_or(&0)
        }

        fn allowance_of_or_zero(&self, owner: &AccountId, spender: &AccountId) -> Balance {
            *self.allowances.get(&(*owner, *spender)).unwrap_or(&0)
        }
    }

    #[cfg(test)]
    mod tests {
        use super::*;

        #[test]
        fn new_works() {
            let contract = Erc20::new(777);
            assert_eq!(contract.total_supply(), 777);
        }

        #[test]
        fn balance_works() {
            let contract = Erc20::new(100);
            assert_eq!(contract.total_supply(), 100);
            assert_eq!(contract.balance_of(AccountId::from([0x1; 32])), 100);
            assert_eq!(contract.balance_of(AccountId::from([0x0; 32])), 0);
        }

        #[test]
<span style="back

首创文章,作者:区块腾,如若转载,请说明来由:http://asongge.com/3732099.html 《利用 Ink!开辟 Substrate ERC20 智能合约》

颁发批评

电子邮件地点不会被公然。 必填项已用*标注

接洽咱们

在线征询:

邮件:快乐飞艇一到十名怎么玩:[email protected]

任务时候:周一至周五,9:30-18:30,节沐日歇息

快乐飞艇做任务靠谱吗 快乐飞艇综合走势图 快乐飞艇开奖结果 快乐飞艇官网 快乐飞艇app首页 熊猫乐园快乐飞艇 快乐飞艇开奖 华创投资快乐飞艇靠谱吗 快乐飞艇计划 快乐飞艇技巧 快乐飞艇开奖记录天天 快乐飞艇彩票