模块初始化器
在许多应用程序中,一个常见的用例是仅在发布包时运行某些代码。想象一个简单的商店模块,需要在发布时创建主商店对象。在Sui中,这可以通过在模块中定义一个init
函数来实现。当模块发布时,该函数将自动被调用。
所有模块的
init
函数都会在发布过程中被调用。目前,这种行为仅限于发布命令,不包括包升级。
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)
});
}
}
在同一个包中,另一个模块可以有自己的init
函数,封装不同的逻辑。
// 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
功能
如果模块中存在init
函数并遵循以下规则,则在发布时会调用该函数:
fun init(ctx: &mut TxContext) { /* ... */ }
fun init(otw: OTW, ctx: &mut TxContext) { /* ... */ }
TxContext也可以作为不可变引用传递:&TxContext
。然而,实际上,它应该始终是&mut TxContext
,因为init
函数不能访问链上状态,并且要创建新对象需要上下文的可变引用。
fun init(ctx: &TxContext) { /* ... */ }
fun init(otw: OTW, ctx: &TxContext) { /* ... */ }
信任与安全
虽然init
函数可以用于创建一次性对象,但重要的是要知道同样的对象(例如第一个示例中的StoreOwnerCap
)仍然可以在另一个函数中创建。特别是考虑到在升级过程中可以向模块添加新函数。因此,init
函数是设置模块初始状态的好地方,但它本身不是一种安全措施。
有一些方法可以保证对象只创建一次,例如一次性见证。还有一些方法可以限制或禁用模块的升级,我们将在包升级章节中进行讨论。
下一步
根据定义,init
函数保证在模块发布时只调用一次。因此,它是放置初始化模块对象、设置环境和配置的代码的好地方。
例如,如果有一个Capability,需要它来执行某些操作,那么它应该在init
函数中创建。在下一章节中,我们将更详细地讨论Capability
模式。