6.MOVE从入门到实战-简单的ERC20

  • 木头
  • 更新于 2022-08-12 17:53
  • 阅读 4282

我们设计一个实现基本代币和余额接口的模块,代币可以在不同地址下的余额之间铸造和转移。

公共接口设计

/// Publish an empty balance resource under `account`'s address. This function must be called before
/// minting or transferring to the account.
public fun init_account(account: &signer) { ... }

/// Mint `amount` tokens to `mint_addr`. Mint must be approved by the module owner.
public fun mint(module_owner: &signer, mint_addr: address, amount: u64) acquires Balance { ... }

/// Returns the balance of `owner`.
public fun balance_of(owner: address): u64 acquires Balance { ... }

/// Transfers `amount` of tokens from `from` to `to`.
public fun transfer(from: &signer, to: address, amount: u64) acquires Balance { ... }

Move和Solidity的存储区别:

  • Solidity:在大多数以太坊ERC-20合约中,每个地址的余额存储在类型为的状态变量中,该状态变量存储在特定智能合约的存储器中。
    
    mapping(address => uint256)
  • Move:每个地址下的"move资源存储"是从类型到值的映射。观察力敏锐的读者可能会观察到,这意味着每个地址每种类型的一个值只能有一个值。这方便地为我们提供了按地址索引的本机映射。在我们的模块中,我们定义了以下资源,表示每个地址持有的硬币数量:
    
     /// Struct representing the balance of each address.
    struct Balance has key {
        coin: Coin // same Coin from Step 1
    }

Move没有自己的存储,相反 Move 使用的"全局存储"(我们称之为区块链状态)按地址编制索引。每个地址下都有"Move 模块(代码)"和"Move资源(值)"。

接口模块实现

新建项目

Move.toml

[package]
name = "myCoin"
version = "0.0.0"

[addresses]
std = "0x1"
MyCoinAddr = "0xf"

[dependencies]
AptosStdlib = { local = "../aptos-stdlib" }
MoveStdlib = { local = "../move-stdlib" }

代币基础模块

/// sources/MyCoin.move
module MyCoinAddr::MyCoin{
    use std::signer;

    /// Address of the owner of this module
    const MODULE_OWNER: address = @MyCoinAddr;

    /// Error codes
    const ENOT_MODULE_OWNER: u64 = 0;
    const EINSUFFICIENT_BALANCE: u64 = 1;
    const EALREADY_HAS_BALANCE: u64 = 2;
    const ACCOUNT_INITIALIZED: u64 = 3;

    struct Coin has store {
        value: u64
    }

    ///  Struct representing the balance of each address.
    struct Balance has key {
        coin: Coin
    }
}

MODULE_OWNER限制只有模块发布者才能铸造代币

方法 init_account()

此方法将资源发布到给定地址。由于通过铸币或转账接收硬币需要此资源,因此用户必须先调用此方法,然后才能接收代币

public fun init_account(account: &signer) {
    let account_addr = signer::address_of(account);
    // TODO: add an assert to check that `account` doesn't already have a `Balance` resource.
    assert!(!exists<Balance>(account_addr), ACCOUNT_INITIALIZED);
    if(!exists<Balance>(account_addr)){
        move_to(account, Balance {coin: Coin {value: 0}});
    }
}

方法 mint()

铸币方法将硬币铸造到给定的账户,在这里我们要求必须得到模块所有者的批准。我们使用assert语句实现。

public fun mint(module_owner: &signer, mint_addr: address, amount: u64) acquires Balance {
    // Only the owner of the module can initialize this module
    assert!(signer::address_of(module_owner) == MODULE_OWNER, ENOT_MODULE_OWNER);

    // Deposit `amount` of tokens to `mint_addr`'s balance
    deposit(mint_addr, Coin { value: amount });
}

deposit私有方法存入代币

fun deposit(_addr: address, check: Coin) acquires Balance {
    let balance = balance_of(_addr);
    // borrow_global_mut
    let balance_ref = &mut borrow_global_mut<Balance>(_addr).coin.value;
    let Coin { value } = check;
    *balance_ref = balance + value;
}

方法 balance_of()

查询地址余额

public fun balance_of(owner: address): u64 acquires Balance {
    borrow_global<Balance>(owner).coin.value
}

方法 transfer()

此函数从的余额中提取代币,并将代币存入的余额中。

public fun transfer(from: &signer, to: address, amount: u64) acquires Balance {
    // balance - amount
    let check = withdraw(signer::address_of(from), amount);
    // add amount
    deposit(to, check);
}

withdraw 私有方法从余额中提取代币

fun withdraw(addr: address, amount: u64):Coin acquires Balance {
    let balance = balance_of(addr);
    // balance must be greater than the withdraw amount
    assert!(balance >= amount, EINSUFFICIENT_BALANCE);
    let balance_ref = &mut borrow_global_mut<Balance>(addr).coin.value;
    *balance_ref = balance - amount;
    Coin { value: amount }
}

编写脚本

1.将资源发布到0xf 0xa地址,并且只能发布一次 2.铸造100个代币到0xa地址 3.0xa地址转账50个代币给到0xf地址

// sources/Main.move
script {

    use MyCoinAddr::MyCoin;
    use std::signer;
    use std::debug;

    fun main(account: signer, mint_addr: signer) {
        //Initialize account
        MyCoin::init_account(&account);
        MyCoin::init_account(&mint_addr);
        // mint
        MyCoin::mint(&account, signer::address_of(&mint_addr), 100);
        // balance
        let mintBalance = MyCoin::balance_of(signer::address_of(&mint_addr));
        debug::print(&mintBalance);
        // transfer
        MyCoin::transfer(&mint_addr, signer::address_of(&account), 50);

        // balance
        let accountBalance = MyCoin::balance_of(signer::address_of(&account));
        debug::print(&accountBalance);
        let mintNewBalance = MyCoin::balance_of(signer::address_of(&mint_addr));
        debug::print(&mintnewBalance);
    }
}

执行脚本

$ move build
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING myCoin
$ move sandbox publish -v                                
Found 1 modules
Publishing a new module 0000000000000000000000000000000F::MyCoin (wrote 566 bytes)
Wrote 566 bytes of module ID's and code
$ move sandbox run sources/Main.move --signers 0xf 0xa 
[debug] 100
[debug] 50
[debug] 50
点赞 2
收藏 3
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

1 条评论

请先 登录 后评论
木头
木头
0xC020...10cf
江湖只有他的大名,没有他的介绍。