Actions
Declare one-shot on-chain effects (mint, create-singleton, seed-config) that run once after their dependencies are ready.
The action() plugin declares a one-shot on-chain effect — a mint, a singleton-object creation, a
seed-config call — that runs after its declared upstream refs (typically a signer account plus one
or more published packages) are ready. The result is an ActionReceipt carrying the real
transaction digest and projected object changes.
Actions are role: 'task': the body runs once at acquire and reaches "done" after start resolves.
They are not long-lived services.
import { account, action, defineDevstack, localPackage, sui } from '@mysten-incubation/devstack';
const localnet = sui();
const alice = account('alice');
const connectFour = localPackage('connect-four', {
sourcePath: './move/connect-four',
publisher: alice,
});
const openLobby = action('connect-four.openLobby', {
dependsOn: { signer: alice, pkg: connectFour },
body: (ctx, { signer, pkg }) =>
ctx.signAndExecute(signer, (tx) => {
tx.moveCall({ target: `${pkg.packageId}::game::create_lobby` });
}),
});
export default defineDevstack({
members: [localnet, alice, connectFour, openLobby],
});The body callback
action(name, opts) takes:
dependsOn— single ref, tuple, or object of plugin/refs. The resolved values arrive in the second argument ofbodyin the same shape.body: (ctx, deps) => Effect<ActionReceipt, ActionError>— the on-chain effect.discriminator— optional caller-supplied material that influences the cache key (literal string or(ctx, deps) => Effect<string>).
ctx.signAndExecute(account, build) folds the full pipeline:
- Build the
Transactionvia the caller'sbuild(tx)callback. - Sign with the supplied
account. - Execute with
{effects, objectTypes}includes. - Wait for finality.
- Project the envelope into an
ActionReceiptwithdigest+objectChanges.
The returned receipt's objectChanges array surfaces the SDK's changedObjects with
kind: 'created' | 'mutated' and the fully-qualified objectType string when available.
Caching
Action results are cached by the substrate's artifact publisher under the action namespace. The
cache key folds:
- the chain id (so
localnetandtestnetcache independently); - a content hash of the action name and the resolved dependency resource ids;
- a dynamic discriminator (literal or callback-supplied) when present.
This means re-running the same stack against the same chain reuses the recorded receipt unless the
discriminator changes. Supplying a callback discriminator is how you force re-execution on mutable
upstream state (e.g. a counter on a singleton object).
Failure surface
action(...) raises a single ActionError with a phase tag:
'discriminator'— the dynamic discriminator failed.'build'— substrate dependency wiring failed before the body ran.'sign'— the body itself raised, or signing/submit transport failed (also covers a missing digest).'execute-failed'— the transaction was delivered and executed but the validator returned aFailedTransactionenvelope.'verify'— substrate-side cache verify exhausted retries.
See Errors for the full catalog.