Module Initializer
A common use case in many applications is to run certain code just once when the package is
published. Imagine a simple store module that needs to create the main Store object upon its
publication. In Sui, this is achieved by defining an init
function within the module. This
function will automatically be called when the module is published.
All of the modules'
init
functions are called during the publishing process. Currently, this behavior is limited to the publish command and does not extend to package upgrades.
module book::shop {
/// The Capability which grants the Shop owner the right to manage
/// the shop.
public struct ShopOwnerCap has key, store { id: UID }
/// The singular Shop itself, created in the `init` function.
public struct Shop has key {
id: UID,
/* ... */
}
// Called only once, upon module publication. It must be
// private to prevent external invocation.
fun init(ctx: &mut TxContext) {
// Transfers the ShopOwnerCap to the sender (publisher).
transfer::transfer(ShopOwnerCap {
id: object::new(ctx)
}, ctx.sender());
// Shares the Shop object.
transfer::share_object(Shop {
id: object::new(ctx)
});
}
}
In the same package, another module can have its own init
function, encapsulating distinct logic.
// In the same package as the `shop` module
module book::bank {
public struct Bank has key {
id: UID,
/* ... */
}
fun init(ctx: &mut TxContext) {
transfer::share_object(Bank {
id: object::new(ctx)
});
}
}
init
features
The function is called on publish, if it is present in the module and follows the rules:
- The function has to be named
init
, be private and have no return values. - Takes one or two arguments: One Time Witness (optional) and
TxContext. With
TxContext
always being the last argument.
fun init(ctx: &mut TxContext) { /* ... */}
fun init(otw: OTW, ctx: &mut TxContext) { /* ... */ }
TxContext can also be passed as immutable reference: &TxContext
. However, practically speaking, it
should always be &mut TxContext
since the init
function can't access the onchain state and to
create new objects it requires the mutable reference to the context.
fun init(ctx: &TxContext) { /* ... */}
fun init(otw: OTW, ctx: &TxContext) { /* ... */ }
Trust and security
While init
function can be used to create sensitive objects once, it is important to know that the
same object (eg. StoreOwnerCap
from the first example) can still be created in another function.
Especially given that new functions can be added to the module during an upgrade. So the init
function is a good place to set up the initial state of the module, but it is not a security measure
on its own.
There are ways to guarantee that the object was created only once, such as the One Time Witness. And there are ways to limit or disable the upgrade of the module, which we will cover in the Package Upgrades chapter.
Next steps
As follows from the definition, the init
function is guaranteed to be called only once when the
module is published. So it is a good place to put the code that initializes module's objects and
sets up the environment and configuration.
For example, if there's a Capability which is required for certain actions, it
should be created in the init
function. In the next chapter we will talk about the Capability
pattern in more detail.