Transaction Context
You can access the input arguments and the transaction context when anybody sends transactions to your contracts. You can use the context to fetch who called it, when, and what’s happening. That context is passed into your functions as&mut TxContext
.
[sui.move]
[sui.move]
[sui.move]
owner
variable.
Module Initializers
You’d need a module initializer to execute actions once e.g create a pool or assign special abilities. You’d declare aninit
function in the module, which runs automatically once published.
[sui.move]
init
, private, return nothing, and optionally take in a one-time witness.
[sui.move]
init
function with a one-time witness:
[sui.move]
Capabilities
Capabilities are objects that give rights and resource access. No need for riskyif sender == admin
conditionals. If you’ve got the cap, you’re allowed.
Here’s an example of a struct with capabilities.
[sui.move]
The convention is adding Cap
as a suffix with the CamelCase.You typically mint the capability once, right when the module is published with a module initializer:
[sui.move]
init
function doesn’t stop someone from adding a new function that creates another cap later. Consider using a One-Time Witness or an un-upgradable package to enforce true one-time access.
Witnesses
Capabilities are great for managing access, but what if you need one-time access to perform something sensitive, like initializing a global config, or minting a single admin cap that must never be duplicated? That’s where witnesses come in. A witness is a proof object passed into a function to prove that something happened before or didn’t happen.One-Time Witness
TheOne-Time Witness (OTW)
pattern enforces that certain code can only run once. It is perfect for ensuring that only one capability is created or that something can only be initialized during the first and only setup phase.
[sui.move]
publish
phase. If you try calling the function again later, there will be no witness and no dice.
:::info
Use OTW when you need strict one-time-only logic baked into your module.
:::
Time Management on Sui
Blockchains use epochs to track time deterministically and Sui is no different. You can access epoch-related info from the transaction context:[sui.move]
Clock
module for For millisecond-level accuracy.
[sui.move]
sui
library:
[sui.move]
Clock
as an immutable reference; then you can access the timestamp like this:
[sui.move]
Events
You can emit events and listen to them to log specific data as they happen on-chain. How? You’ll define your own event structs, then use the built-inevent::emit
function.
First, import the event
module like this:
[sui.move]
[sui.move]
Error Handling
You’re going to run into errors, and you’ll need to handle them. By default, when your Move function hits an abort!, it fails the transaction and returns a module name + error code. That’s helpful—until it isn’t.[sui.move]
0
, you have no idea which one did it. That’s where better error-handling patterns come in.
Instead of letting a function fail blindly, wrap it with checks. Constants come in handy here.
First, define a constant for each error case:
[sui.move]
[sui.move]