module book::witness; use sui::coin::{Self,Coin}; use sui::sui::SUI; use std::string::{Self,String}; const PASSPORT_FEE:u64 = 50000;
publicstructShareInfo has key{ id :UID, owner:address, } fun init(ctx:&mut TxContext){ let share_info = ShareInfo{ id:object::new(ctx), owner:ctx.sender(), }; transfer::freeze_object(share_info); }
publicstructHouse has key{ id:UID, name:String, }
publicstructPassport has drop{ }
/// 花钱购买一个Passport public fun requirePassport(mut c:Coin<SUI>,share_info: &ShareInfo, ctx : &mut TxContext) : Passport{ //分割出来收费 let fee = coin::split(&mut c, PASSPORT_FEE, ctx); transfer::public_transfer(fee,share_info.owner); //归还找零的钱 transfer::public_transfer(c,ctx.sender()); //返回Passport Passport{} }
///获得Passport 才能创建House , Passport 每次使用之后都会消耗掉 public fun create_house(_:Passport, name:String, ctx:&mut TxContext){ let house = House{ id:object::new(ctx), name:name, }; //转让房子给交易发起者 transfer::transfer(house,ctx.sender()); }
/// 为了在模块外测试代码调用init函数 #[test_only] public fun test_init(ctx:&mut TxContext){ init(ctx); } ///为了模块外测试代码获取本模块常量, 因为常量不是public的 #[test_only] public fun get_passport_fee():u64{ PASSPORT_FEE }
#[test_only] module book::witness_tests ; use sui::test_scenario::{Self, Scenario}; use sui::coin::{Self, Coin}; use sui::sui::SUI; use std::string; use book::witness::{Self, ShareInfo, House, Passport}; use book::witness::get_passport_fee;
#[test] fun test_witness_flow(){ let owner = @0xA; let user = @0xB; // 第一步:初始化场景 let mut scenario = test_scenario::begin(owner); // 初始化合约,创建 ShareInfo test_scenario::next_tx(&mut scenario, owner); { witness::test_init(test_scenario::ctx(&mut scenario)); };
// 用户购买 Passport test_scenario::next_tx(&mut scenario, user); { // 创建测试用的 SUI coin let coin = coin::mint_for_testing<SUI>(100000, test_scenario::ctx(&mut scenario)); let share_info = test_scenario::take_immutable<ShareInfo>(&scenario); let passport = witness::requirePassport( coin, &share_info, test_scenario::ctx(&mut scenario) ); // 使用 Passport 创建 House witness::create_house( passport, string::utf8(b"My House"), test_scenario::ctx(&mut scenario) ); test_scenario::return_immutable(share_info); };
// 验证 House 是否创建成功 test_scenario::next_tx(&mut scenario, user); { let coin = test_scenario::take_from_address<Coin<SUI>>(&scenario, owner); assert!(coin.value() == get_passport_fee()); test_scenario::return_to_address(owner, coin);
fatal: unable to access 'https://github.com/hexojs/hexo-starter.git/': GnuTLS recv error (-110): The TLS connection was non-properly terminated. WARN git clone failed. Copying data instead INFO Install dependencies warning ../../../package.json: No license field error Error: certificate has expired at TLSSocket.onConnectSecure (node:_tls_wrap:1539:34) at TLSSocket.emit (node:events:513:28) at TLSSocket._finishInit (node:_tls_wrap:953:8) at TLSWrap.ssl.onhandshakedone (node:_tls_wrap:734:12)
Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.
ljl@ljl-i5-14400:~$ ls *.tgz sui-testnet-v1.36.2-ubuntu-x86_64.tgz sui-testnet-v1.37.1-ubuntu-x86_64.tgz ljl@ljl-i5-14400:~$ mkdir sui ljl@ljl-i5-14400:~$ cd sui ljl@ljl-i5-14400:~/sui$ tar xzf ../sui-testnet-v1.37.1-ubuntu-x86_64.tgz ljl@ljl-i5-14400:~/sui$ ls move-analyzer sui-bridge sui-data-ingestion sui-faucet sui-node sui-tool sui sui-bridge-cli sui-debug sui-graphql-rpc sui-test-validator ljl@ljl-i5-14400:~/sui$ pwd /home/ljl/sui
struct Balance<T>{} struct Supply<T>{} struct Coin<T>{} struct TreasureCap<T>{} /// unique instance of CoinMetadata<T> that stores the metadata for this coin type. public struct CoinMetadata<phantom T> has key, store { ... }
modulepkg::hk{ public struct HK has drop {}, init(hk:&mut HK, ctx:mut TxContext){ let (cap,meta) = coin::create_currency(hk,&mut ctx); } }
module sui::balance; public fun balance::create_supply<T: drop>(_: T): Supply<T> { Supply { value: 0 } }
public fun increase_supply<T>(self: &mut Supply<T>, value: u64): Balance<T> { assert!(value < (18446744073709551615u64 - self.value), EOverflow); self.value = self.value + value; Balance { value } }
sui::balance 这个Supply 和Balance 都需要一个OTW类
1 2 3 4 5 6 7 8 9
public struct Supply<phantom T> has store { value: u64, }
/// Storable balance - an inner struct of a Coin type. /// Can be used to store coins which don't need the key ability. public struct Balance<phantom T> has store { value: u64, }
sui::coin 也有一个type arg 需要一个OTW 类
1 2 3 4 5
/// A coin of type `T` worth `value`. Transferable and storable public struct Coin<phantom T> has key, store { id: UID, balance: Balance<T>, }
modulecoin_owner::hk { use sui::coin::create_currency; use sui::tx_context::{TxContext, sender};
public struct HK has drop {}
fun init(hk: HK, ctx: &mut TxContext) { let (treasury_cap, coin_metadata) = create_currency( hk, 8, b"HK", b"HK made in hongkong", b"HK made in hongkong", option::none(), ctx); transfer::public_freeze_object(coin_metadata); let my_address = sender(ctx); transfer::public_transfer(treasury_cap, my_address) } }
货币创建方法 coin::create_currency, 里面需要第一个参数 witness是 One time witness
public fun create_currency<T: drop>( witness: T, decimals: u8, symbol: vector<u8>, name: vector<u8>, description: vector<u8>, icon_url: Option<Url>, ctx: &mut TxContext, ): (TreasuryCap<T>, CoinMetadata<T>) { // Make sure there's only one instance of the type T assert!(sui::types::is_one_time_witness(&witness), EBadWitness);
不同用户都可以调用这个mint_to 方法. 创建出来的coin 不能视为代币,使用 sui client balance 不能看到. 因为没有对应的CoinMetadata
{.line-numbers}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
modulestrange_coin::jp; use sui::balance::{Supply,Balance}; public struct Other has drop{} public struct SupplyHold has key,store{ id : UID, supply :Supply<Other>, } public entry fun mint_to(amount :u64,to:address,ctx : &mut TxContext){ let other = Other{}; let mut supply = sui::balance::create_supply(other); let balance = supply.increase_supply(amount); let coin = sui::coin::from_balance(balance, ctx); let hold = SupplyHold{ id : object::new(ctx), supply, }; transfer::public_transfer(hold,tx_context::sender(ctx)); transfer::public_transfer(coin,to); }
10. move table 对象的链上查看问题
问题查看
move 中 Table 是通过 dynamic field 实现的,在浏览器中似乎没有办法查到 table 中所有的数据,table 对应的那个 object id 在sui浏览器中也无法访问。只是浏览器不支持,还是说不能实现在链下查到 table 里所有的数据。
答复,可以看到动态字段
10.1 直接保存的table对象
相关代码
{.line-numbers}
1 2 3 4 5 6 7 8 9 10
moduletable_example::table_example; use sui::table::{Self,Table}; use std::string::String;
fun init(ctx : &mut TxContext){ let mut tb = table::new<String,u32>(ctx); tb.add(b"abc".to_string(),1); tb.add(b"def".to_string(),2); transfer::public_transfer(tb,tx_context::sender(ctx)); }
/// Adds a key-value pair to the table `table: &mut Table<K, V>` /// Aborts with `sui::dynamic_field::EFieldAlreadyExists` if the table already has an entry with /// that key `k: K`. public fun add<K: copy + drop + store, V: store>(table: &mut Table<K, V>, k: K, v: V) { field::add(&mut table.id, k, v); table.size = table.size + 1; }
dynamic field, 通过table address 和 属性名,一起计算字段.
{.line-numbers}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
public fun add<Name: copy + drop + store, Value: store>( // we use &mut UID in several spots for access control object: &mut UID, name: Name, value: Value, ) { let object_addr = object.to_address(); let hash = hash_type_and_key(object_addr, name); assert!(!has_child_object(object_addr, hash), EFieldAlreadyExists); let field = Field { id: object::new_uid_from_hash(hash), name, value, }; add_child_object(object_addr, field) }
#[test] fun test_nodrop(){ let no_drop = NoDrop{ value :34}; print(&no_drop); letNoDrop{ value: _ } = no_drop; }
这个例子 NoDrop类型没有drop能力,对象离开作用域,需要析构,或者将对象的所有权转移.
第8行是析构对象的代码
13.1.2 转移:
{.line-numbers}
1 2 3 4 5 6 7 8 9 10 11
fun useNoDrop(o : NoDrop ) : NoDrop{ std::debug::print(&o); o }
#[test] fun testUseNoDrop(){ let o = NoDrop{value :4}; let d = useNoDrop(o); NoDrop{value:_} = d; }
第1行函数UseNoDrop获得了对象o
第3行,函数将N哦Drop对象o 返回出去,将所有权转移出去.
第10 行,显示代码析构这个NoDrop对象
13.1.3 key:独立存储在链上:
对象独立存储在链上,必须具有key能力 has key
{.line-numbers}
1 2 3 4 5 6 7 8 9 10 11
public struct Keyable has key{ id : UID, }
#[test] fun test_key(){ let mut ctx = tx_context ::dummy(); let k = Keyable{ id: object::new(&mut ctx)}; std::debug::print(&k); transfer::transfer(k,ctx.sender()); }
use std::string::String; public struct Grandson has store{ name : String, } public struct Child has store{ name : String, child : Grandson, } public struct Parent has key{ id: UID, child: Child, } #[test] fun test_store_child(){ let mut ctx = tx_context::dummy(); let foo = Parent { id : object::new(&mut ctx), child: Child { name : b"one child".to_string(), child: Grandson{ name : b"a grandson".to_string(),
publicstructCoin has key,store{ id:UID, amount:u64 }
//借出钱 public fun borrow(amount:u64,ctx:&mut TxContext) :(Coin,Loan){ let feedback = amount * 103 /100; let c = Coin{ id: object::new(ctx),amount}; (c, Loan{feedback}) }
sui client ptb --move-call $PKG::upgrade_demo::withdraw "@$TREASURY 50000 @$ADMIN_CAP" --assign coin1 --transfer-objects [coin1] @$ADDR_A --gas-budget 30000000 RPC call failed: ErrorObject { code: ServerError(-32002), message: "Transaction validator signing failed due to issues with transaction inputs, please review the errors and try again:\n- Transaction was not signed by the correct sender: Object 0x10ca605ce0437c10274bcc260901ef5b34bab91c747f2dc498b722ae151a5baf is owned by account address 0x7cbe5e6596e23266dd5763dd89b4ab1195516908ecde8febfe96685c7cbe6432, but given owner/signer address is 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c\n- Could not find the referenced object 0x38417a57a4f061487895b2c97b3e188e847c25de37762c5e0146bd6a32cd5a89 at version None", data: None }
8. 切换B 用户,取款成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# 切换到ADDR_B $ sui client switch --address $ADDR_B Active address switched to 0x7cbe5e6596e23266dd5763dd89b4ab1195516908ecde8febfe96685c7cbe6432 # 调用withdraw取款 $ sui client ptb --move-call $PKG::upgrade_demo::withdraw "@$TREASURY 50000 @$ADMIN_CAP" --assign coin1 --transfer-objects [coin1] @$ADDR_B
/// A coin of type `T` worth `value`. Transferable and storable publicstructCoin<phantom T> has key, store { id: UID, balance: Balance<T>, }
/// Each Coin type T created through `create_currency` function will have a /// unique instance of CoinMetadata<T> that stores the metadata for this coin type. publicstructCoinMetadata<phantom T> has key, store { id: UID, /// Number of decimal places the coin uses. /// A coin with `value ` N and `decimals` D should be shown as N / 10^D /// E.g., a coin with `value` 7002 and decimals 3 should be displayed as 7.002 /// This is metadata for display usage only. decimals: u8, /// Name for the token name: string::String, /// Symbol for the token symbol: ascii::String, /// Description of the token description: string::String, /// URL for the token logo icon_url: Option<Url>, }
2. 货币的诞生:
2.1 货币诞生相关函数的启发
参看 0x2:sui::coin 的创建货币的代码,需要一个otw对象.
货币诞生需要这个泛型参数witness丢想是一个One Time Witness struct 对象
ljl@ljl-i5-14400:~/work/sui/move-cn/letsmove/mover/nextuser/code$ sui client balance [warn] Client/Server api version mismatch, client api version : 1.37.1, server api version : 1.37.2 ╭──────────────────────────────────────────────╮ │ Balance of coins owned by this address │ ├──────────────────────────────────────────────┤ │ ╭──────────────────────────────────────────╮ │ │ │ coin balance (raw) balance │ │ │ ├──────────────────────────────────────────┤ │ │ │ Sui 3637300968 3.63 SUI │ │ │ │ YEN of Japan 8800000 8.80M YEN │ │ │ ╰──────────────────────────────────────────╯ │
#[test] fun test_nodrop(){ let no_drop = NoDrop{ value :34}; print(&no_drop); letNoDrop{ value: _ } = no_drop; }
这个例子 NoDrop类型没有drop能力,对象离开作用域,需要析构,或者将对象的所有权转移.
第8行是析构对象的代码
2.3.4 不能析构时,能转移所有权:
{.line-numbers}
1 2 3 4 5 6 7 8 9 10 11
fun useNoDrop(o : NoDrop ) : NoDrop{ std::debug::print(&o); o }
#[test] fun testUseNoDrop(){ let o = NoDrop{value :4}; let d = useNoDrop(o); NoDrop{value:_} = d; }
第1行函数UseNoDrop获得了对象o
第3行,函数将N哦Drop对象o 返回出去,将所有权转移出去.
第10 行,显示代码析构这个NoDrop对象
2.3.5 独立存储在链上
对象独立存储在链上,必须具有key能力 has key
{.line-numbers}
1 2 3 4 5 6 7 8 9 10 11
public struct Keyable has key{ id : UID, }
#[test] fun test_key(){ let mut ctx = tx_context ::dummy(); let k = Keyable{ id: object::new(&mut ctx)}; std::debug::print(&k); transfer::transfer(k,ctx.sender()); }
use std::string::String; public struct Grandson has store{ name : String, } public struct Child has store{ name : String, child : Grandson, } public struct Parent has key{ id: UID, child: Child, } #[test] fun test_store_child(){ let mut ctx = tx_context::dummy(); let foo = Parent { id : object::new(&mut ctx), child: Child { name : b"one child".to_string(), child: Grandson{ name : b"a grandson".to_string(),
publicstructCoin has key,store{ id:UID, amount:u64 }
//借出钱 public fun borrow(amount:u64,ctx:&mut TxContext) :(Coin,Loan){ let feedback = amount * 103 /100; let c = Coin{ id: object::new(ctx),amount}; (c, Loan{feedback}) }