Result and error
The outcome and error objects of a tick.
Every tick produces an outcome. The runtime hands it to your plugin as one of two objects: ExecutionResult for onExecuted, and the narrower ErrorContext for onError. Both are read-only echoes of what already happened. Mutating them changes nothing — the tx is already submitted (or already failed).
ExecutionResult
onExecuted receives this for every outcome.
type ExecutionOutcome = "submitted" | "noop" | "failed";
interface ExecutionResult {
outcome: ExecutionOutcome;
actions: Action[];
dryRun?: boolean;
digest?: string;
effectsStatus?: "success" | "failure";
events?: ExecutionEvent[];
gasUsed?: bigint;
error?: string;
}outcome: the verdict."submitted"= the tx landed and effects succeeded."noop"= every action was anoop(nothing to submit)."failed"= a client error (RPC, signing) or an on-chain Move abort.actions: an echo of the [Action[]](/reference/actions) yourdecide()returned, kept for correlation. If auto-fund prepended apmDeposit, it's in here.dryRun:truewhen the tick was adevInspectsimulation. In practice you won't see it set inside the hook — the runtime skips both hooks whendryRun === true, so sims never touch the same state backend production uses.digest: the Sui tx digest. Set only when a real tx reached the chain. Absent on dry-runs and client errors.effectsStatus:"success"or"failure", set when the tx (real or simulated) returned effects.events: the [ExecutionEvent[]](#executionevent) the tx emitted (or simulated). Empty on failure.gasUsed: total gas in MIST (computation + storage − rebate). An estimate whendryRun.error: the error message. Set whenoutcome === "failed".
ExecutionEvent
interface ExecutionEvent {
type: string; // full type ID, e.g. "0xabc::vault::VaultMintBinary"
parsedJson: unknown;
}Match on the type string, then cast parsedJson. Use type.endsWith("::vault::VaultMintBinary") rather than a hardcoded package prefix, since the package ID is part of the full type.
ErrorContext
onError receives this, and only on failed. It runs after onExecuted, never instead of it. The point is a dedicated error path without branching on result.outcome.
interface ErrorContext {
actions: Action[];
phase: "on-chain" | "client";
digest?: string;
error: string;
dryRun?: boolean;
}phase: where it broke."on-chain"= the tx was submitted but Move aborted, so you have adigest."client"= RPC or signing failed before the tx ever reached the chain.digest: set whenphase === "on-chain".error: the description. Move abort code names (EPositionTooLargeand friends) leak through here, so you canerror.includes(...)to react to a specific revert.actions: the same echo as inExecutionResult— correlate which trade failed.dryRun: like above, you won't see it set in the hook; the runtime skipsonErroron simulations.
phaseis the first branch."client"is transient: retry next tick."on-chain"means a real revert you can act on, such as banning a degenerate strike.
From here, the lifecycle hooks topic shows the canonical patterns for both objects.