Game on Sui
Game on Sui

Game on Sui

Giới thiệu

Sau khi chúng ta đã học các phần cơ bản của ngôn ngữ lập trình Move, đặc biệt là cách để tạo ra các Objects trên Sui. Bây giờ chúng ta sẽ áp dụng các kiến thức đó để tạo ra một game trên Sui

Mô tả Game Play

  1. Tạo nhân vật(Player):

    • Người chơi có thể tạo nhân vật với các thuộc tính cơ bản
    • Mỗi nhân vật sẽ là một Object riêng biệt trên Sui blockchain
    • Người chơi sở hữu hoàn toàn nhân vật của mình
  2. Chiến đấu:

    • Người chơi có thể cho nhân vật tham gia các trận chiến với quái vật
    • Kết quả trận đấu được lưu trữ trên blockchain
  3. Phát triển nhân vật:

    • Nhân vật có thể tăng cấp thông qua các trận đấu (đánh quái, ... )
    • Các chỉ số của nhân vật được cập nhật và lưu trữ trên chain
    • Người chơi có thể trang bị các vật phẩm cho nhân vật ví dụ Sword, ...

Code

Step 1: Tạo project

sui move new game_on_sui

Link code: https://github.com/CocDap/game_on_move/tree/main (opens in a new tab)

Step 2: Định nghĩa các Objects

  • Hero: Mô tả nhân vật của người chơi
  • Sword: Vật phẩm của người chơi
  • Boar: Quái vật heo
  • GameInfo: Thông tin của game
  • GameAdmin: Admin quản lý game
  • Potion: Vật phẩm hồi máu
/// hero.move
/// -------------------------------------------------------------------------------
/// -------------------------------ĐỊNH NGHĨA OBJECT-------------------------------
/// -------------------------------------------------------------------------------
 
// Tạo 1 object Sword
 
public struct Sword has key, store {
    id: UID,
    // Sức mạnh phép thuật 
    magic: u64,
    // Sức mạnh của Sword trong game 
    strength: u64,
    game_id: ID
 
}
// Tạo 1 object Hero 
 
public struct Hero has key, store {
    id: UID,
    // Hit point - Máu của Hero 
    hp: u64,
    // Điểm kinh nghiệm của hero 
    experience: u64,
    // Hero có Sword 
    sword: Option<Sword>,
    // Game id mà user đang chơi 
    game_id: ID,
}
 
// Tạo 1 object "Máu" -> Sử dụng để hồi HP 
public struct Potion has key, store {
    id: UID,
    // Lượng hồi máu
    potency: u64,
    // Game id mà user đang chơi 
    game_id: ID
 
}
 
// Tạo 1 object Quái Boar
 
public struct Boar has key {
    id: UID,
    // Máu của quái tối đa bao nhiêu 
    // HP =0 -> quái chết 
    hp: u64,
    // Sức mạnh của quái 
    strength: u64,
    // Game id 
    game_id: ID
 
}
 
// Tạo GameAdmin
public struct GameInfo has key {
    id: UID,
    admin: address,
    payments: Balance<SUI>,
}
 
public struct GameAdmin has key {
    id: UID,
    boars_created: u64,
    potions_created: u64,
    game_id: ID
 
}
 

Link code: https://github.com/CocDap/game_on_move/tree/step2 (opens in a new tab)

Step 3: Định nghĩa Events

  • BoarSlainEvent: Sự kiện khi người chơi hạ gục quái vật
/// hero.move
 
/// -------------------------------------------------------------------------------
/// -------------------------------ĐỊNH NGHĨA EVENT--------------------------------
/// -------------------------------------------------------------------------------
 
public struct BoarSlainEvent has copy, drop {
    // player nào hạ gục boar
    slayer_address: address,
    // ID của hero mà user đang control 
    hero: ID,
    // ID của boar đã bị hạ gục
    boar: ID,
    game_id: ID
 
}

Link code: https://github.com/CocDap/game_on_move/tree/step3 (opens in a new tab)

Step 4: Định nghĩa hằng số và lỗi

/// hero.move
 
/// -------------------------------------------------------------------------------
/// ----------------------ĐỊNH NGHĨA CÁC HẰNG SỐ ----------------------------------
/// -------------------------------------------------------------------------------
const MAX_HP: u64 = 1000;
 
const MAX_MAGIC: u64 = 10;
 
const MIN_SWORD_COST: u64 = 100;
 
 
/// -------------------------------------------------------------------------------
/// -------------------------------ĐỊNH NGHĨA LỖI----------------------------------
/// -------------------------------------------------------------------------------
 
const EBOAR_WON: u64 = 0;
 
const EHERO_TIRED: u64 = 1;
const ENOT_ADMIN: u64 = 2;
 
const EINSUFFICIENT_FUNDS: u64 = 3; 
 
const ENO_SWORD: u64 = 4;
 
const EWRONG_GAME_PLAY: u64 = 5;

Link code: https://github.com/CocDap/game_on_move/tree/step4 (opens in a new tab)

Step 5: Tạo game info và game admin

  • GameInfo: Lưu trữ thông tin game -> Share Object
  • GameAdmin: Lưu trữ thông tin game admin -> Transfer Object
/// hero.move
public entry fun new_game(ctx: &mut TxContext) {
    create(ctx);
}
 
/// -------------------------------------------------------------------------------
/// -----------------HELPER FUNCTION CHO VIỆC TẠO GAME ADMIN VÀ GAME INFO----------
/// -------------------------------------------------------------------------------
fun create(ctx: &mut TxContext){
    let sender = tx_context::sender(ctx);
    let id = object::new(ctx);
    let game_id = object::uid_to_inner(&id);
 
    transfer::share_object(GameInfo { 
        id, 
        admin: sender,
        payments: balance::zero()
    });
 
    transfer::transfer(
        GameAdmin {
            id: object::new(ctx),
            boars_created: 0,
            potions_created: 0,
            game_id,
        }, 
        sender
    )
 
}

Link code: https://github.com/CocDap/game_on_move/tree/step5 (opens in a new tab)

Step 6: Tạo game Hero và Sword

  • Hero Object: Lưu trữ thông tin hero -> Transfer Object
  • Sword Object: Lưu trữ thông tin sword -> Transfer Object
/// hero.move
 
public entry fun acquire_hero(game: &mut GameInfo, payment: Coin<SUI>, ctx: &mut TxContext) {
    let sword = create_sword(game, payment, ctx);
    let hero = create_hero(game, sword, ctx);
    transfer::transfer(hero, tx_context::sender(ctx))
 
}
 
/// -------------------------------------------------------------------------------
/// ---------------------HELPER FUNCTION TẠO HERO VÀ SWORD-------------------------
/// -------------------------------------------------------------------------------
 
// Tạo Sword
public fun create_sword(game: &mut GameInfo, payment: Coin<SUI>, ctx: &mut TxContext): Sword {
    // Lấy số lượng coin hiện tại mà user sở hữu 
    let value = coin::value(&payment);
 
    assert!(value >= MIN_SWORD_COST, EINSUFFICIENT_FUNDS);
 
    // pay coin cho admin 
    coin::put(&mut game.payments, payment);
 
    // Công thức tính magic 
    let magic =  (value - MIN_SWORD_COST)/ MIN_SWORD_COST;
    Sword {
        id: object::new(ctx),
        magic: std::u64::min(magic, MAX_MAGIC),
        strength: 1,
        game_id: object::id(game)
    }
 
}
 
// Tạo hero 
public fun create_hero(game: &GameInfo, sword: Sword, ctx: &mut TxContext): Hero {
    // Kiểm tra có cùng game id hay không 
    assert!(object::id(game) == sword.game_id, EWRONG_GAME_PLAY);
    Hero {
        id: object::new(ctx),
        hp: 100,
        experience: 0,
        sword: option::some(sword),
        game_id: object::id(game),
    }
}
 

Link code: https://github.com/CocDap/game_on_move/tree/step6 (opens in a new tab)

Step 7: Tạo quái vật Boar

  • Boar: Lưu trữ thông tin quái vật -> Transfer Object
  • Chỉ có admin mới tạo được quái vật
// Admin tạo boar cho player 
public entry fun send_boar(
    game: &GameInfo,
    admin: &mut GameAdmin,
    hp: u64,
    strength: u64,
    player: address,
    ctx: &mut TxContext
) {
    assert!(object::id(game) == admin.game_id, EWRONG_GAME_PLAY);
 
    admin.boars_created = admin.boars_created + 1;
    // send boars to the designated player
    transfer::transfer(
        Boar { id: object::new(ctx), hp, strength, game_id: object::id(game) },
        player
    )
}

Link code: https://github.com/CocDap/game_on_move/tree/step7 (opens in a new tab)

Step 8: Player đánh quái vật boar

/// -------------------------------------------------------------------------------
/// --------------ENTRY FUNCTION LIÊN QUAN TỚI HÀNH ĐỘNG CỦA HERO------------------
/// -------------------------------------------------------------------------------
 
// Hàm đánh quái 
public entry fun slay(game: &GameInfo, hero: &mut Hero, boar: Boar, ctx: &TxContext){
 
    // Kiểm tra game ID của hero 
    assert!(object::id(game) == hero.game_id, EWRONG_GAME_PLAY);
    // Kiểm tra game ID của Boar
    assert!(object::id(game) == boar.game_id, EWRONG_GAME_PLAY);
 
    // Destructure boar object 
    let Boar {id: boar_id, strength: boar_strength, hp, game_id: _} = boar;
    let hero_strength = hero_strength(hero);
 
    assert!(hero_strength !=0, EHERO_TIRED);
 
    let mut boar_hp = hp;
 
    let mut hero_hp = hero.hp;
 
    // Tấn công boar cho đến khi HP của Boar còn 0 
    while (boar_hp > hero_strength) {
        // đầu tiên hero attack trước 
        boar_hp = boar_hp - hero_strength; 
        // sau đó boar attack 
        assert!(hero_hp >= boar_strength, EBOAR_WON);
 
        hero_hp = hero_hp - boar_strength;
 
    };
 
    // lưu lại HP của Hero sau khi tấn công boar
 
    hero.hp = hero_hp;
    // Nhận kinh nghiệm bằng lượng HP của boar sau khi đánh bại 
    hero.experience = hero.experience + hp;
    
    // Tăng sức mạnh của Sword lên 1 đơn vị 
 
    if (option::is_some(&hero.sword)) {
        level_up_sword(option::borrow_mut(&mut hero.sword), 1)
    };
    event::emit(BoarSlainEvent {
        slayer_address: tx_context::sender(ctx),
        hero: object::uid_to_inner(&hero.id),
        boar: object::uid_to_inner(&boar_id),
        game_id: object::id(game)
    });
 
    object::delete(boar_id);
 
}
 
/// -------------------------------------------------------------------------------
/// ---------------------HELPER FUNCTION CHO HÀM SLAY(ĐÁNH QUÁI)-------------------
/// -------------------------------------------------------------------------------
 
// Sức mạnh của Sword khi tấn công 
public fun sword_strength(sword: &Sword): u64 {
    sword.magic + sword.strength
}
 
// Sức mạnh của Hero khi tấn công 
public fun hero_strength(hero: &Hero): u64 {
    // Nếu hero có HP = 0 thì không thể attack 
 
    if (hero.hp == 0) {
        return 0
    };
    // Nếu hero có sử dụng Sword -> lấy sức mạnh của Sword 
    let  sword_strength  = if (option::is_some(&hero.sword)) {
        sword_strength(option::borrow(&hero.sword))
    }
    else {
        0
    };
 
    // Sức mạnh của hero 
    (hero.experience * hero.hp) + sword_strength
}
 
// Tăng cấp cho Sword 
public fun level_up_sword(sword: &mut Sword, amount: u64) {
    sword.strength =  sword.strength + amount;
}
 
 

Link code: https://github.com/CocDap/game_on_move/tree/step8 (opens in a new tab)

Step 9: Hàm tạo vật phẩm hồi máu

  • Potion: Lưu trữ thông tin vật phẩm hồi máu -> Transfer Object
  • Chỉ có admin mới tạo được vật phẩm hồi máu
// Admin tạo potion (bình máu) cho player 
public entry fun send_potion(
    game: &GameInfo,
    potency: u64,
    player: address,
    admin: &mut GameAdmin,
    ctx: &mut TxContext
) {
    assert!(object::id(game) == admin.game_id, EWRONG_GAME_PLAY);
    admin.potions_created = admin.potions_created + 1;
    // send potion to the designated player
    transfer::transfer(
        Potion { id: object::new(ctx), potency, game_id: object::id(game) },
        player
    )
}

Link code: https://github.com/CocDap/game_on_move/tree/step9 (opens in a new tab)

Step 10: Hàm liên quan tới vật phẩm trang bị

/// Phục hồi máu cho hero 
public fun heal(hero: &mut Hero, potion: Potion) {
    assert!(hero.game_id == potion.game_id, EWRONG_GAME_PLAY);
    let Potion { id, potency, game_id: _ } = potion;
    object::delete(id);
    let new_hp = hero.hp + potency;
    // maximum HP của hero là 1000
    hero.hp = std::u64::min(new_hp, MAX_HP)
}
 
/// Trang bị New Sword cho Hero và return old sword 
public fun equip_sword(hero: &mut Hero, new_sword: Sword): Option<Sword> {
    option::swap_or_fill(&mut hero.sword, new_sword)
}
 
/// Tháo Sword ra khỏi trang bị của Hero 
public fun remove_sword(hero: &mut Hero): Sword {
    assert!(option::is_some(&hero.sword), ENO_SWORD);
    option::extract(&mut hero.sword)
}

Link code: https://github.com/CocDap/game_on_move/tree/step10 (opens in a new tab)

Step 11: Tạo hàm lấy payment

  • Chỉ có admin mới có thể lấy payment
/// -------------------------------------------------------------------------------
/// ---------------------ENTRY FUNCTION LIÊN QUAN TỚI PAYMENT----------------------
/// -------------------------------------------------------------------------------
 
public entry fun take_payment(admin: &GameAdmin, game: &mut GameInfo, ctx: &mut TxContext
) {
    assert!(admin.game_id == object::id(game), ENOT_ADMIN);
    let payment = coin::from_balance(balance::withdraw_all(&mut game.payments), ctx);
    transfer::public_transfer(payment, tx_context::sender(ctx))
}
 

Link code: https://github.com/CocDap/game_on_move/tree/step11 (opens in a new tab)

Step 12: Viết test case

/// tests/hero_tests.move
let admin = @0xAD014;
let player = @0x0;
 
let mut scenario_val = test_scenario::begin(admin);
let scenario = &mut scenario_val;
// Run the create new game 
test_scenario::next_tx(scenario, admin);
{
    new_game(test_scenario::ctx(scenario));
};
// Tạo Hero cùng với Sword
test_scenario::next_tx(scenario, player);
{
    let mut game = test_scenario::take_shared<GameInfo>(scenario);
    let coin = coin::mint_for_testing(500, test_scenario::ctx(scenario));
    acquire_hero(&mut game, coin, test_scenario::ctx(scenario));
    test_scenario::return_shared(game);
};
// Admin tạo boar cho Player 
test_scenario::next_tx(scenario, admin);
{
    let game = test_scenario::take_shared<GameInfo>(scenario);
    let game_ref = &game;
    let mut admin_cap = test_scenario::take_from_sender<GameAdmin>(scenario);
    send_boar(game_ref, &mut admin_cap, 10, 10, player, test_scenario::ctx(scenario));
    test_scenario::return_to_sender(scenario, admin_cap);
    test_scenario::return_shared(game);
};
// Player slays the boar!
test_scenario::next_tx(scenario, player);
{
    let game = test_scenario::take_shared<GameInfo>(scenario);
    let game_ref = &game;
    let mut hero = test_scenario::take_from_sender<Hero>(scenario);
    std::debug::print(&b"Before slaying boar, our hero is:".to_string());
    std::debug::print(&hero);
    let boar = test_scenario::take_from_sender<Boar>(scenario);
    slay(game_ref, &mut hero, boar, test_scenario::ctx(scenario));
    test_scenario::return_to_sender(scenario, hero);
    test_scenario::return_shared(game);
};
 
test_scenario::next_tx(scenario, player);
{
    let hero = test_scenario::take_from_sender<Hero>(scenario);
    std::debug::print(&b"After slaying boar, our updated hero is:".to_string());
    std::debug::print(&hero);
    test_scenario::return_to_sender(scenario, hero);
};
 
// Take Payment Checking 
 
test_scenario::next_tx(scenario, admin);
{
    let game = test_scenario::take_shared<GameInfo>(scenario);
    std::debug::print(&b"Before taking payment:".to_string());
    std::debug::print(&game);
    test_scenario::return_shared(game);
};
 
test_scenario::next_tx(scenario, admin);
{
    let mut game = test_scenario::take_shared<GameInfo>(scenario);
    let admin_game = test_scenario::take_from_sender<GameAdmin>(scenario);
    take_payment(&admin_game, &mut game, test_scenario::ctx(scenario));
    test_scenario::return_to_sender(scenario, admin_game);
    test_scenario::return_shared(game);
 
};
 
 
test_scenario::next_tx(scenario, admin);
{
    let game = test_scenario::take_shared<GameInfo>(scenario);
    
    std::debug::print(&b"After taking payment:".to_string());
    std::debug::print(&game);
    test_scenario::return_shared(game);
};
 
 
 
test_scenario::end(scenario_val);
 

Link code: https://github.com/CocDap/game_on_move/tree/step12 (opens in a new tab)

Cách publish và chạy game thông qua SUI CLI

Lưu ý:

  • Bạn cần phải có 2 account -> Account 1 cho admin, account 2 cho player
  • Faucet coin SUI cho các account
  • Khi bạn publish hay run game thì lưu ý phải thay đổi packageId và objectId trong cli
  • Tham khảo thêm ở trên Suiscan để view được các thông tin on-chain

Trong trường hợp này, mình sử dụng nifty-alexandrite cho Player và lucid-sphene cho Admin

╭───────────────────┬────────────────────────────────────────────────────────────────────┬────────────────╮
│ alias             │ address                                                            │ active address │
├───────────────────┼────────────────────────────────────────────────────────────────────┼────────────────┤
│ nifty-alexandrite │ 0xb2ff9d4b13077deecb7e03e2041772abcbe4b7720f413f84264c60384cf29b5c │                │
│ lucid-sphene      │ 0xd51d17a672dfd63b91c1c555542f8151126f5e0872b85553644730c2471fdf28 │ *              │
╰───────────────────┴────────────────────────────────────────────────────────────────────┴────────────────╯
  • Export biến
export ADMIN=0xd51d17a672dfd63b91c1c555542f8151126f5e0872b85553644730c2471fdf28
export PLAYER=0xb2ff9d4b13077deecb7e03e2041772abcbe4b7720f413f84264c60384cf29b5c

Step 1: Publish game

  • Sử dụng lucid-sphene để publish game
sui client publish --gas-budget 100000000
  • Kết quả
...
│ Published Objects:                                                                               │
│  ┌──                                                                                             │
│  │ PackageID: 0x0816216c6f165ceb6125cd1f72b9185527f6534bf58f56e5c29575c95321c45b                 │
│  │ Version: 1                                                                                    │
│  │ Digest: 6JGSUL8gUU648ZCi1Gv7XSStTBxwZ7C59jGgUtQSaxFJ                                          │
│  │ Modules: hero                                                                                 │
│  └──                                                                                             │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
...
  • Sử dụng EXPORT để lưu packageId
export PACKAGE_ID=0x0816216c6f165ceb6125cd1f72b9185527f6534bf58f56e5c29575c95321c45b

Step 2: Tạo new game

  • Sử dụng lucid-sphene để tạo new game
sui client call --package $PACKAGE_ID --module hero --function new_game --gas-budget 100000000
  • Kết quả
...
│ Created Objects:                                                                                   │
│  ┌──                                                                                               │
│  │ ObjectID: 0x72dfc389f24d0f63cce06b9502ba9cb8e0a9ce313962f8065efa1cdd2e2858a0                    │
│  │ Sender: 0xd51d17a672dfd63b91c1c555542f8151126f5e0872b85553644730c2471fdf28                      │
│  │ Owner: Shared( 289571933 )                                                                      │
│  │ ObjectType: 0x816216c6f165ceb6125cd1f72b9185527f6534bf58f56e5c29575c95321c45b::hero::GameInfo   │
│  │ Version: 289571933                                                                              │
│  │ Digest: 3Ts3DZzBnhX6JAiEr16YjF5VXw9jyisL4eVMMq6eYmcZ                                            │
│  └──                                                                                               │
│  ┌──                                                                                               │
│  │ ObjectID: 0x80099d4f44f6a013a0e6ec8668a619ff7880ab956278b883ac01cfe9e07864c3                    │
│  │ Sender: 0xd51d17a672dfd63b91c1c555542f8151126f5e0872b85553644730c2471fdf28                      │
│  │ Owner: Account Address ( 0xd51d17a672dfd63b91c1c555542f8151126f5e0872b85553644730c2471fdf28 )   │
│  │ ObjectType: 0x816216c6f165ceb6125cd1f72b9185527f6534bf58f56e5c29575c95321c45b::hero::GameAdmin  │
│  │ Version: 289571933                                                                              │
│  │ Digest: 9T5FdpWZJozJkbNQRfyqSjVKcSjCgLRB6h35fTqRFvqA                                            │
│  └── 
...
  • Object Id của GameInfo: 0x72dfc389f24d0f63cce06b9502ba9cb8e0a9ce313962f8065efa1cdd2e2858a0
export GAME_INFO=0x72dfc389f24d0f63cce06b9502ba9cb8e0a9ce313962f8065efa1cdd2e2858a0
  • Object Id của GameAdmin: 0x80099d4f44f6a013a0e6ec8668a619ff7880ab956278b883ac01cfe9e07864c3
export GAME_ADMIN=0x80099d4f44f6a013a0e6ec8668a619ff7880ab956278b883ac01cfe9e07864c3

Step 3: Tạo hero

  • Sử dụng nifty-alexandrite để tạo hero
sui client switch --address nifty-alexandrite
  • Faucet để tạo 1 object coins mới hoặc sử dụng transfer coins (Bạn cần phải có 1 object coins mới)
sui client transfer-sui --to nifty-alexandrite --sui-coin-object-id 0xd9384d06b794e76383e5f798eb02c97d9a01525e598187b2c44bb9c6d7434e92 --amount 500000000 --gas-budget 100000000
  • Sau khi tạo 1 object coin mới
╭────────────────────────────────────────────────────────────────────┬────────────────────┬──────────────────╮
│ gasCoinId                                                          │ mistBalance (MIST) │ suiBalance (SUI) │
├────────────────────────────────────────────────────────────────────┼────────────────────┼──────────────────┤
│ 0x240bc32f91d5ca0db1fe1e3dfa5ec9c0978956ebc9da371dd02f6d23813e8989 │ 500000000          │ 0.50             │
│ 0xf23a5e0ab3f054ac8762ef9b09d8b118f5168cc6c2a49a253f49a4cfeed50923 │ 179105192          │ 0.17             │
  • Lưu object coin mới vào biến
export COIN=0x240bc32f91d5ca0db1fe1e3dfa5ec9c0978956ebc9da371dd02f6d23813e8989
  • Tạo hero
sui client call --package $PACKAGE_ID --module hero --function acquire_hero --args $GAME_INFO $COIN --gas-budget 100000000
  • Kết quả
│ Created Objects:                                                                                  │
│  ┌──                                                                                              │
│  │ ObjectID: 0x55291019b9bb75105ec5a4e0dc3643b1428c984b53a1f86ec4210e9082af28e2                   │
│  │ Sender: 0xb2ff9d4b13077deecb7e03e2041772abcbe4b7720f413f84264c60384cf29b5c                     │
│  │ Owner: Account Address ( 0xb2ff9d4b13077deecb7e03e2041772abcbe4b7720f413f84264c60384cf29b5c )  │
│  │ ObjectType: 0x816216c6f165ceb6125cd1f72b9185527f6534bf58f56e5c29575c95321c45b::hero::Hero      │
│  │ Version: 289571934                                                                             │
│  │ Digest: CbLiGhmacsxHZzM8AeiKPJYycTqcXRH22tkKaBwEx1KY  
  • Object Id của Hero: 0x55291019b9bb75105ec5a4e0dc3643b1428c984b53a1f86ec4210e9082af28e2
export HERO=0x55291019b9bb75105ec5a4e0dc3643b1428c984b53a1f86ec4210e9082af28e2

Step 4: Tạo boar

  • Chỉ có Admin mới tạo Boar -> switch account sang lucid-sphene
sui client switch --address lucid-sphene
  • Tạo bear
sui client call --package $PACKAGE_ID --module hero --function send_boar --args $GAME_INFO $GAME_ADMIN 10 1 $PLAYER --gas-budget 100000000
  • Kết quả
...
 Created Objects:                                                                                   │
│  ┌──                                                                                               │
│  │ ObjectID: 0x3a3675e0c58694cbc031f0dff5d22892fe97722f0e61bad9ab67df5b82eaf821                    │
│  │ Sender: 0xd51d17a672dfd63b91c1c555542f8151126f5e0872b85553644730c2471fdf28                      │
│  │ Owner: Account Address ( 0xb2ff9d4b13077deecb7e03e2041772abcbe4b7720f413f84264c60384cf29b5c )   │
│  │ ObjectType: 0x816216c6f165ceb6125cd1f72b9185527f6534bf58f56e5c29575c95321c45b::hero::Boar       │
│  │ Version: 289571935                                                                              │
│  │ Digest: 4CQPs9Rcyc9ZtPR7xeMTk7uFsKAe2o5RJjB8oLAMeRwZ  
...
  • Object Id của Boar: 0x3a3675e0c58694cbc031f0dff5d22892fe97722f0e61bad9ab67df5b82eaf821

  • Lưu object boar vào biến

export BOAR=0x3a3675e0c58694cbc031f0dff5d22892fe97722f0e61bad9ab67df5b82eaf821

Step 5: Đánh quái

  • Sử dụng nifty-alexandrite để đánh quái
sui client switch --address nifty-alexandrite
  • Call hàm đánh quái
sui client call --package $PACKAGE_ID --module hero --function slay --args $GAME_INFO $HERO $BOAR --gas-budget 100000000
  • Kết quả
...
╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Block Events                                                                              │
├───────────────────────────────────────────────────────────────────────────────────────────────────────┤
│  ┌──                                                                                                  │
│  │ EventID: 8GtPubC3J3KMcwotB9An1Hg3bGWS3qPFbxpsZ7iTvfV3:0                                            │
│  │ PackageID: 0x0816216c6f165ceb6125cd1f72b9185527f6534bf58f56e5c29575c95321c45b                      │
│  │ Transaction Module: hero                                                                           │
│  │ Sender: 0xb2ff9d4b13077deecb7e03e2041772abcbe4b7720f413f84264c60384cf29b5c                         │
│  │ EventType: 0x816216c6f165ceb6125cd1f72b9185527f6534bf58f56e5c29575c95321c45b::hero::BoarSlainEvent │
│  │ ParsedJSON:                                                                                        │
│  │   ┌────────────────┬────────────────────────────────────────────────────────────────────┐          │
│  │   │ boar           │ 0x3a3675e0c58694cbc031f0dff5d22892fe97722f0e61bad9ab67df5b82eaf821 │          │
│  │   ├────────────────┼────────────────────────────────────────────────────────────────────┤          │
│  │   │ game_id        │ 0x72dfc389f24d0f63cce06b9502ba9cb8e0a9ce313962f8065efa1cdd2e2858a0 │          │
│  │   ├────────────────┼────────────────────────────────────────────────────────────────────┤          │
│  │   │ hero           │ 0x55291019b9bb75105ec5a4e0dc3643b1428c984b53a1f86ec4210e9082af28e2 │          │
│  │   ├────────────────┼────────────────────────────────────────────────────────────────────┤          │
│  │   │ slayer_address │ 0xb2ff9d4b13077deecb7e03e2041772abcbe4b7720f413f84264c60384cf29b5c │          │
│  │   └────────────────┴────────────────────────────────────────────────────────────────────┘          │
│  └──                                                                                                  │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯
...

Step 6: Lấy payment

  • Sử dụng lucid-sphene để lấy payment
sui client switch --address lucid-sphene
  • Lấy payment
sui client call --package $PACKAGE_ID --module hero --function take_payment --args $GAME_ADMIN $GAME_INFO --gas-budget 100000000
  • Kết quả
...
│ Created Objects:                                                                                   │
│  ┌──                                                                                               │
│  │ ObjectID: 0x6f14e050068c87b13ded32c4cd171f351e344362d8fd8fed826ddae8f8e69617                    │
│  │ Sender: 0xd51d17a672dfd63b91c1c555542f8151126f5e0872b85553644730c2471fdf28                      │
│  │ Owner: Account Address ( 0xd51d17a672dfd63b91c1c555542f8151126f5e0872b85553644730c2471fdf28 )   │
│  │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI>                                                      │
│  │ Version: 289571936                                                                              │
│  │ Digest: 9rpiTJgqM3DDifmY6Bt4tgnNkn64ks7jkCfLTfKqSrAU                                            │
│  └──      
...

Tài liệu tham khảo