第6节:通用generic
Code
使用CoinType改写代码,这是一个通用类型,此时会将BasicCoin变成一个模板,库,可以提供给其他代码调用。
BasicCoin.move改写如下:
/// This module defines a minimal and generic Coin and Balance.
module NamedAddr::BasicCoin {
use std::signer;
/// Error codes
const ENOT_MODULE_OWNER: u64 = 0;
const EINSUFFICIENT_BALANCE: u64 = 1;
const EALREADY_HAS_BALANCE: u64 = 2;
struct Coin<phantom CoinType> has store {
value: u64
}
struct Balance<phantom CoinType> has key {
coin: Coin<CoinType>
}
/// Publish an empty balance resource under `account`'s address. This function must be called before
/// minting or transferring to the account.
public fun publish_balance<CoinType>(account: &signer) {
let empty_coin = Coin<CoinType> { value: 0 };
assert!(!exists<Balance<CoinType>>(signer::address_of(account)), EALREADY_HAS_BALANCE);
move_to(account, Balance<CoinType> { coin: empty_coin });
}
/// Mint `amount` tokens to `mint_addr`. This method requires a witness with `CoinType` so that the
/// module that owns `CoinType` can decide the minting policy.
public fun mint<CoinType: drop>(mint_addr: address, amount: u64, _witness: CoinType) acquires Balance {
// Deposit `total_value` amount of tokens to mint_addr's balance
deposit(mint_addr, Coin<CoinType> { value: amount });
}
public fun balance_of<CoinType>(owner: address): u64 acquires Balance {
borrow_global<Balance<CoinType>>(owner).coin.value
}
/// Transfers `amount` of tokens from `from` to `to`. This method requires a witness with `CoinType` so that the
/// module that owns `CoinType` can decide the transferring policy.
public fun transfer<CoinType: drop>(from: &signer, to: address, amount: u64, _witness: CoinType) acquires Balance {
let check = withdraw<CoinType>(signer::address_of(from), amount);
deposit<CoinType>(to, check);
}
fun withdraw<CoinType>(addr: address, amount: u64) : Coin<CoinType> acquires Balance {
let balance = balance_of<CoinType>(addr);
assert!(balance >= amount, EINSUFFICIENT_BALANCE);
let balance_ref = &mut borrow_global_mut<Balance<CoinType>>(addr).coin.value;
*balance_ref = balance - amount;
Coin<CoinType> { value: amount }
}
fun deposit<CoinType>(addr: address, check: Coin<CoinType>) acquires Balance{
let balance = balance_of<CoinType>(addr);
let balance_ref = &mut borrow_global_mut<Balance<CoinType>>(addr).coin.value;
let Coin { value } = check;
*balance_ref = balance + value;
}
}
MyOddCoin.move
在这里,我们使用了BasicCoin作为一个库,实现了自己的代币,同时增加了transfer方法,仅转账是基数时,才会成功。
/// Module implementing an odd coin, where only odd number of coins can be
/// transferred each time.
module NamedAddr::MyOddCoin {
use std::signer;
use NamedAddr::BasicCoin;
struct MyOddCoin has drop {}
const ENOT_ODD: u64 = 0;
public fun setup_and_mint(account: &signer, amount: u64) {
BasicCoin::publish_balance<MyOddCoin>(account);
BasicCoin::mint<MyOddCoin>(signer::address_of(account), amount, MyOddCoin {});
}
public fun transfer(from: &signer, to: address, amount: u64) {
// amount must be odd.
assert!(amount % 2 == 1, ENOT_ODD);
BasicCoin::transfer<MyOddCoin>(from, to, amount, MyOddCoin {});
}
/*
Unit tests
*/
#[test(from = @0x42, to = @0x10)]
fun test_odd_success(from: signer, to: signer) {
// 这里面的两个地址进行mint时,会创建token,它是如何保证这两个词调用使用的是同一个token呢?
// 在mint的时候,会使用同一个对象。
setup_and_mint(&from, 42);
setup_and_mint(&to, 10);
// transfer an odd number of coins so this should succeed.
transfer(&from, @0x10, 7);
assert!(BasicCoin::balance_of<MyOddCoin>(@0x42) == 35, 0);
assert!(BasicCoin::balance_of<MyOddCoin>(@0x10) == 17, 0);
}
#[test(from = @0x42, to = @0x10)]
#[expected_failure]
fun test_not_odd_failure(from: signer, to: signer) {
setup_and_mint(&from, 42);
setup_and_mint(&to, 10);
// transfer an even number of coins so this should fail.
transfer(&from, @0x10, 8);
}
}
Note
- phantom类型,点击查看