# Create Smart Contracts with Sui Move ## Move.toml File 1. [package]: Your package metadata, such as name and version. * name: The name of your package. Created by Sui Move compiler. * version: The current version of your package. The Sui Move compiler creates the first value. * published-at: The published address of the package. The Sui Move compiler does not automatically create this entry. ![](https://hackmd.io/_uploads/rJ99nQQ82.png) The published-at specifies the address that the dependency is published at ![](https://hackmd.io/_uploads/S1QPI0RI2.png) 2. [dependencies]: List of packages that your package depends on. Initially, the Sui Framework is the only dependency, but you add third-party dependencies to this section as needed. ![](https://hackmd.io/_uploads/BJ0hhmXIn.png) 3. [addresses]: A list of named addresses. You can use the names listed as convenient aliases for the given addresses in the source code. ![](https://hackmd.io/_uploads/r1502mXL2.png) ## Move.lock File According to the indication on [Move.lock File page](https://docs.sui.io/build/move/lock-file) the file is not for us to edit ![](https://hackmd.io/_uploads/H17AumXUh.png) ![](https://hackmd.io/_uploads/BJ4GT77Ln.png) ## Write a Sui Move Package #### Create a package ``` sui move new my_first_package ``` ![](https://hackmd.io/_uploads/ryDNCmQ8n.png) #### The original Move.toml ![](https://hackmd.io/_uploads/S1HvRm783.png) #### Defining the Package ``` touch my_first_package/sources/my_module.move ``` ``` module my_first_package::my_module { // Part 1: Imports use sui::object::{Self, UID}; use sui::transfer; use sui::tx_context::{Self, TxContext}; // Part 2: Struct definitions struct Sword has key, store { id: UID, magic: u64, strength: u64, } struct Forge has key, store { id: UID, swords_created: u64, } // Part 3: Module initializer to be executed when this module is published fun init(ctx: &mut TxContext) { let admin = Forge { id: object::new(ctx), swords_created: 0, }; // Transfer the forge object to the module/package publisher transfer::transfer(admin, tx_context::sender(ctx)); } // Part 4: Accessors required to read the struct attributes public fun magic(self: &Sword): u64 { self.magic } public fun strength(self: &Sword): u64 { self.strength } public fun swords_created(self: &Forge): u64 { self.swords_created } // Part 5: Public/entry functions (introduced later in the tutorial) // Part 6: Private functions (if any) } ``` ## Build and Test Move Packages Sui move build command ``` sui move build ``` #### Check the package name and the name in move file if the error occured ![](https://hackmd.io/_uploads/S1SzlVQUh.png) #### Build success ![](https://hackmd.io/_uploads/ryBflVXLn.png) #### Testing a package Sui move test command ``` sui move test ``` ##### Without test code snippet ![](https://hackmd.io/_uploads/SkBflN7Ln.png) ##### Syntax error ![](https://hackmd.io/_uploads/H1SGlNQ83.png) ##### First test code snippet Paste the first test code snippet and run sui move test ``` #[test] public fun test_sword_create() { use sui::tx_context; // Create a dummy TxContext for testing let ctx = tx_context::dummy(); // Create a sword let sword = Sword { id: object::new(&mut ctx), magic: 42, strength: 7, }; // Check if accessor functions return correct values assert!(magic(&sword) == 42 && strength(&sword) == 7, 1); } ``` ![](https://hackmd.io/_uploads/SJBzx4mU2.png) ##### Second test code snippet Update my_module.move and paste the following code snippet in part 5 ``` public entry fun sword_create(magic: u64, strength: u64, recipient: address, ctx: &mut TxContext) { use sui::transfer; // create a sword let sword = Sword { id: object::new(ctx), magic: magic, strength: strength, }; // transfer the sword transfer::transfer(sword, recipient); } public entry fun sword_transfer(sword: Sword, recipient: address, _ctx: &mut TxContext) { use sui::transfer; // transfer the sword transfer::transfer(sword, recipient); } ``` Paste the second test code snippet and run sui move test ``` #[test] fun test_sword_transactions() { use sui::test_scenario; // create test addresses representing users let admin = @0xBABE; let initial_owner = @0xCAFE; let final_owner = @0xFACE; // first transaction to emulate module initialization let scenario_val = test_scenario::begin(admin); let scenario = &mut scenario_val; { init(test_scenario::ctx(scenario)); }; // second transaction executed by admin to create the sword test_scenario::next_tx(scenario, admin); { // create the sword and transfer it to the initial owner sword_create(42, 7, initial_owner, test_scenario::ctx(scenario)); }; // third transaction executed by the initial sword owner test_scenario::next_tx(scenario, initial_owner); { // extract the sword owned by the initial owner let sword = test_scenario::take_from_sender<Sword>(scenario); // transfer the sword to the final owner sword_transfer(sword, final_owner, test_scenario::ctx(scenario)) }; // fourth transaction executed by the final sword owner test_scenario::next_tx(scenario, final_owner); { // extract the sword owned by the final owner let sword = test_scenario::take_from_sender<Sword>(scenario); // verify that the sword has expected properties assert!(magic(&sword) == 42 && strength(&sword) == 7, 1); // return the sword to the object pool (it cannot be simply "dropped") test_scenario::return_to_sender(scenario, sword) }; test_scenario::end(scenario_val); } ``` ![](https://hackmd.io/_uploads/H1rMgN783.png) ## Debug and Publish Move Packages ##### Debugging a package Debug code snippet ``` use std::debug; debug::print(&v); ``` ![](https://hackmd.io/_uploads/B1SMx4mUn.png) ``` debug::print_stack_trace(); ``` ![](https://hackmd.io/_uploads/SkHfl47In.png) ##### Publishing a package ![](https://hackmd.io/_uploads/rkfxYV7Ih.png) The third test code snippet for testing the module initialization ``` #[test] public fun test_module_init() { use sui::test_scenario; // Create test address representing game admin let admin = @0xBABE; // First transaction to emulate module initialization let scenario_val = test_scenario::begin(admin); let scenario = &mut scenario_val; { init(test_scenario::ctx(scenario)); }; // Second transaction to check if the forge has been created // and has initial value of zero swords created test_scenario::next_tx(scenario, admin); { // Extract the Forge object let forge = test_scenario::take_from_sender<Forge>(scenario); // Verify number of created swords assert!(swords_created(&forge) == 0, 1); // Return the Forge object to the object pool test_scenario::return_to_sender(scenario, forge); }; test_scenario::end(scenario_val); } ``` ![](https://hackmd.io/_uploads/HkSzgEQL3.png) ![](https://hackmd.io/_uploads/HJrGl47L2.png) The final fixed code ``` module my_first_package::my_module { // Part 1: Imports use sui::object::{Self, UID}; use sui::transfer; use sui::tx_context::{Self, TxContext}; // Part 2: Struct definitions struct Sword has key, store { id: UID, magic: u64, strength: u64, } struct Forge has key, store { id: UID, swords_created: u64, } // Part 3: Module initializer to be executed when this module is published fun init(ctx: &mut TxContext) { let admin = Forge { id: object::new(ctx), swords_created: 0, }; // Transfer the forge object to the module/package publisher transfer::transfer(admin, tx_context::sender(ctx)); } // Part 4: Accessors required to read the struct attributes public fun magic(self: &Sword): u64 { self.magic } public fun strength(self: &Sword): u64 { self.strength } public fun swords_created(self: &Forge): u64 { self.swords_created } // Part 5: Public/entry functions (introduced later in the tutorial) public entry fun sword_create(forge: &mut Forge, magic: u64, strength: u64, recipient: address, ctx: &mut TxContext) { use sui::transfer; use std::debug; let v = 2345678; debug::print(&v); debug::print_stack_trace(); // create a sword let sword = Sword { id: object::new(ctx), magic: magic, strength: strength, }; // transfer the sword transfer::transfer(sword, recipient); forge.swords_created = forge.swords_created + 1; } public entry fun sword_transfer(sword: Sword, recipient: address, _ctx: &mut TxContext) { use sui::transfer; // transfer the sword transfer::transfer(sword, recipient); } // Part 6: Private functions (if any) // testing #[test] public fun test_sword_create() { use sui::transfer; use sui::tx_context; // Create a dummy TxContext for testing let ctx = tx_context::dummy(); // Create a sword let sword = Sword { id: object::new(&mut ctx), magic: 42, strength: 7, }; // Check if accessor functions return correct values assert!(magic(&sword) == 42 && strength(&sword) == 7, 1); // Create a dummy address and transfer the sword let dummy_address = @0xCAFE; transfer::transfer(sword, dummy_address); } #[test] fun test_sword_transactions() { use sui::test_scenario; // create test addresses representing users let admin = @0xBABE; let initial_owner = @0xCAFE; let final_owner = @0xFACE; // first transaction to emulate module initialization let scenario_val = test_scenario::begin(admin); let scenario = &mut scenario_val; { init(test_scenario::ctx(scenario)); }; // second transaction executed by admin to create the sword test_scenario::next_tx(scenario, admin); { let forge = test_scenario::take_from_sender<Forge>(scenario); // create the sword and transfer it to the initial owner sword_create(&mut forge, 42, 7, initial_owner, test_scenario::ctx(scenario)); test_scenario::return_to_sender(scenario, forge); }; // third transaction executed by the initial sword owner test_scenario::next_tx(scenario, initial_owner); { // extract the sword owned by the initial owner let sword = test_scenario::take_from_sender<Sword>(scenario); // transfer the sword to the final owner sword_transfer(sword, final_owner, test_scenario::ctx(scenario)) }; // fourth transaction executed by the final sword owner test_scenario::next_tx(scenario, final_owner); { // extract the sword owned by the final owner let sword = test_scenario::take_from_sender<Sword>(scenario); // verify that the sword has expected properties assert!(magic(&sword) == 42 && strength(&sword) == 7, 1); // return the sword to the object pool (it cannot be simply "dropped") test_scenario::return_to_sender(scenario, sword) }; test_scenario::end(scenario_val); } #[test] public fun test_module_init() { use sui::test_scenario; // Create test address representing game admin let admin = @0xBABE; // First transaction to emulate module initialization let scenario_val = test_scenario::begin(admin); let scenario = &mut scenario_val; { init(test_scenario::ctx(scenario)); }; // Second transaction to check if the forge has been created // and has initial value of zero swords created test_scenario::next_tx(scenario, admin); { // Extract the Forge object let forge = test_scenario::take_from_sender<Forge>(scenario); // Verify number of created swords assert!(swords_created(&forge) == 0, 1); // Return the Forge object to the object pool test_scenario::return_to_sender(scenario, forge); }; test_scenario::end(scenario_val); } } ``` ![](https://hackmd.io/_uploads/rJHGgEXLn.png) Publishing ``` sui client publish /home/garrick/Development/VSCode/sui_project/my_first_package --gas 0xd80cc482701eff5e85174194c5eab4a528e95f3c98ac4b1a95f0b09b2ee6d9bf --gas-budget 1000000 ``` ![](https://hackmd.io/_uploads/BJSzxV7L3.png) ``` sui client publish /home/garrick/Development/VSCode/sui_project/my_first_package --gas 0xd80cc482701eff5e85174194c5eab4a528e95f3c98ac4b1a95f0b09b2ee6d9bf --gas-budget 100000000 --skip-dependency-verification ``` ![](https://hackmd.io/_uploads/HkHfgV7U3.png) ![](https://hackmd.io/_uploads/HJHzeVXU2.png) ![](https://hackmd.io/_uploads/rk4GlNQL3.png)