Publish a Package
Before you can call functions in a Move package (beyond an emulated Sui execution scenario), that package must be available on the Sui network. When you publish a package, you are actually creating an immutable Sui object on the network that anyone can access.
To publish your package to the Sui network, you can use the sui client publish
command of the Sui Client CLI. After the package becomes available on the network, you can then use the sui client call
command to access the functions available in the package.
Module initializers
Each module in a package can include a special initializer function that runs at publication time. The goal of an initializer function is to pre-initialize module-specific data (for example, to create singleton objects). The initializer function must have the following properties for it to execute at publication:
- Function name must be
init
- The parameter list must end with either a
&mut TxContext
or a&TxContext
type - No return values
- Private visibility
- Optionally, the parameter list starts by accepting the module's one-time witness by value
For example, the following init
functions are all valid:
fun init(ctx: &TxContext)
fun init(ctx: &mut TxContext)
fun init(otw: EXAMPLE, ctx: &TxContext)
fun init(otw: EXAMPLE, ctx: &mut TxContext)
While the sui move
command does not support publishing explicitly, you can still test module initializers using the testing framework by dedicating the first transaction to executing the initializer function.
Continuing the fantasy game example, the init
function should create a Forge
object.
/// 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));
}
The tests you have so far call the init
function, but the initializer function itself isn't tested to ensure it properly creates a Forge
object. To test this functionality, modify the sword_create
function to take the forge as a parameter and to update the number of
created swords at the end of the function:
/// Constructor for creating swords
public fun new_sword(
forge: &mut Forge,
magic: u64,
strength: u64,
ctx: &mut TxContext,
): Sword {
forge.swords_created = forge.swords_created + 1;
// ...
}
Now, create a function to test the module initialization:
#[test_only] use sui::test_scenario as ts;
#[test_only] const ADMIN: address = @0xAD;
#[test]
public fun test_module_init() {
let ts = ts::begin(@0x0);
// first transaction to emulate module initialization.
{
ts::next_tx(&mut ts, ADMIN);
init(ts::ctx(&mut ts));
};
// second transaction to check if the forge has been created
// and has initial value of zero swords created
{
ts::next_tx(&mut ts, ADMIN);
// extract the Forge object
let forge: Forge = ts::take_from_sender(&mut ts);
// verify number of created swords
assert!(swords_created(&forge) == 0, 1);
// return the Forge object to the object pool
ts::return_to_sender(&mut ts, forge);
};
ts::end(ts);
}
As the new test function shows, the first transaction (explicitly) calls the initializer. The next transaction checks if the Forge
object has been created and properly initialized.
If you try to run tests on the whole package at this point, you encounter compilation errors in the existing tests because of the
new_sword
function signature change. The changes required for the tests to run again is an exercise left for you. If you need help, you can refer to the source code for the package (with all the tests properly adjusted) in first_package.