Sui Move Architecture
Design the smallest on-chain system that preserves consensus, custody, composition, and reconstruction.
Target repository instructions, accepted design records, pinned toolchain behavior, and published compatibility commitments take precedence over this standard's examples.
Apply the standard
- Establish the target's publication status, accepted decisions, compatibility
promises, and pinned dependencies. This context is complete when each source
of authority for the design has been located or recorded as an evidence gap.
- Map every affected invariant to its canonical state, owning module,
authorized transitions, public consumers, and reconstruction source. The map
is complete when every changed fact and callable seam has one owner.
- Apply every relevant rule below and every triggered reference to that map.
Resolve each conflict in favor of the target's accepted contract rather than
this skill's examples.
- Finish only when the architecture gate accounts for every changed package,
module, state field, authority, public type or function, event, and external
dependency.
Load matching references
- Object custody: Read
Storage and indexer-visible object custody
completely when choosing inline or dynamic child storage, or when a
key
object is stored, nested, wrapped, indexed, or expected to remain discoverable
by its original ID.
- Events: Read Events completely when adding,
compacting, changing, indexing, or reviewing events, payload fields, emitters,
or replay promises.
- Value types: Read
Reusable value invariants
completely when extracting, composing, simplifying, or reviewing a sealed
value type, validated unit wrapper, or field relationship reused by multiple
domain types.
Read every matching reference; keep the loaded set to the triggered branches.
Decide what belongs on chain
Put logic or state on chain only when at least one of these conditions applies:
- authorize, reject, or order a consensus-critical transition;
- custody, allocate, conserve, mint, burn, or settle assets;
- provide a synchronous result for trustless contract composition;
- commit the smallest primitive fact required for independent reconstruction.
Keep deterministic projections, formatting, aggregation, previews, transaction construction, and convenience reads off chain. A transition can recompute a derived value internally when it must enforce fees, slippage, bounds, conservation, or another invariant.
Choose package boundaries
- Start with one core package.
- Add a package only for a real security, deployment, ownership, or dependency boundary.
- Put DEX, oracle, bridge, or other volatile external dependencies in adapter packages.
- Make adapters depend inward on a narrow core interface. Never make core import an adapter.
- Keep independently deployed systems separate when core does not require their result synchronously.
- Do not split packages only to shorten files or mirror another repository.
- Do not ship placeholder integrations, dummy returns, silent no-ops,
todo, or production functions whose intended path aborts. A dependency interface link stub that is never executed and is verified against deployed bytecode is not a production implementation.
Use this default direction:
types, errors, constants, units, pure math
↓
state and lifecycle transitions
↓
fees, allocations, migration handoffs
↓
external adapters and independent systems
↓
SDK, indexing, transaction, and deployment tooling
Organize modules by invariant ownership
- Make one module the owner of each coherent invariant set. A module is an
invariant owner, not a bucket for one function, one type, or one conceptual
subtopic.
- Keep code together when it has the same authority, lifecycle, consumers, and
reason to change. The owning module may contain all of the related types,
transitions, checks, and private helpers needed to preserve its invariants.
- Split a module only when the seam enforces a real security, custody,
dependency-isolation, or visibility boundary. When the required boundary is
independent deployment, split the package rather than pretending a module
can deploy independently.
- A small module is justified when it enforces one of those boundaries;
otherwise it is navigation overhead. Do not split merely to shorten a file,
mirror a function or type taxonomy, or give every nameable subtopic a home.
- When two proposed modules share authority, lifecycle, consumers, and reason
to change, combine them unless their seam enforces one of those boundaries.
- Put state with the lifecycle that mutates it.
- Put a capability type in the module that owns its one-time issuance and lifecycle.
- Put authorization checks beside the private authority representation they inspect.
- Extract a small authority module only for a real shared domain or to break a real module cycle.
- Separate live mutable settings from cold policy snapshotted into newly created state.
- Store and mutate each administrative resource in the module that defines its
authority, lifecycle, and invariants.
- Do not combine capability custody, ACL membership, cryptographic keys,
identity, fees, upgrades, or product configuration in a generic
config,
manager, or admin module.
- Avoid generic
config, utils, manager, helper, or registry dumping grounds. Name the domain concern.
Use these module roles only when needed:
- package marker module: one-time witness and publisher claim; private
init;
constants: package-wide protocol constants as package macros;
errors: package error registry with no state or assertions;
events: payload schemas and package-only emitters;
events_wrapper: the package's one envelope and raw emission seam;
- authority or
acl: root rotation, routine membership, and ephemeral sign-in witness;
- runtime configuration: the smallest live settings;
- cold configuration: bounded policy copied into new domain objects;
- package administration: validated custody of
Publisher and UpgradeCap;
- domain modules: lifecycle transitions and owned invariants;
- pure math modules: deterministic arithmetic without storage or transfer effects;
- adapter packages: sealed venue witness, external dependency, and venue settlement.
Give an initializer an OTW parameter only when it validates, transforms,
consumes, or stores that witness for its own concern. Other initializers take
TxContext only.
Treat initialization as publication-only. Package upgrades do not rerun module
initializers, so post-upgrade setup belongs in an explicit authorized migration
or activation transition.
Make invalid states hard to express
- Model lifecycle states with an enum and permit only named transitions.
- Use typed capabilities for durable authority and drop-only witnesses for ephemeral authorization.
- Use a non-copyable, non-droppable resource for one-time handoffs and replay prevention.
- Wrap a stored linear resource in
Option<T> only when a transition must remove it from a borrowed parent; do not pair that option with a redundant claimed flag.
- Use phantom type parameters for domain separation when no value of the type is stored.
- Keep reserves, fees, pending burns, allocations, refunds, and claims in separate balances when their ownership or terminal treatment differs.
- Use the defining module's exclusive construction and field access to enforce the type's invariants. Expose only constructors and mutation operations that create valid state and preserve those invariants.
- Give shared top-level state
key only unless transfer or nesting is an explicit requirement.
- Add
store, copy, or drop only when the intended lifecycle requires it.
- Treat every published struct or enum layout and ability set as fixed across
compatible upgrades.
- To change stored shape, introduce a new type and an authorized migration, or
use a dynamic-extension seam designed before publication.
- For every stored field, name its canonical authority and the transition that needs a local copy. Reject a mirror when construction makes disagreement unreachable or no operation can change the copy independently.
- Do not duplicate state already owned by a native registry or canonical object merely to revalidate it later. Read the authority at the transition that needs it, unless a sealed construction proof already establishes the invariant.
- Prefer consuming a unique authorization resource over storing a parallel
claimed flag or nonce.
- Decide upgrade seams before publication. A compatible upgrade cannot add
fields to an existing struct, so include version or migration fields only
when an accepted upgrade plan requires them.
- Otherwise use a new type or a predesigned dynamic-extension seam instead of
speculative flags, modes, roles, or governance hooks.
Design authority by scope
- A protocol-global admin cap normally carries only its identity. Revocation comes from live shared registry state.
- A per-instance cap carries target IDs only when those IDs define its exact authority domain.
- The module that issues a capability owns its destruction, rotation, or consumption rules.
- Separate root authority from routine administration.
- Do not make object possession or an address check imply broader authority than the type promises.
- Do not pass overlapping capabilities to one operation.
Protect dependency integrity
- Pin Git dependencies to immutable full commit SHAs and commit lockfiles.
- Keep core buildable without mutable external heads.
- Treat external interfaces as hostile versioned boundaries.
- Never rely on an external dependency to enforce core invariants. Validate adapter inputs and outputs, limit delegated authority and custody, and re-check asset, state, and economic invariants before committing settlement.
- Verify the deployed package, original type origin, ABI, and economic assumptions consumed by each adapter.
Architecture gate
Architecture is complete only when every changed fact has a canonical owner,
every public seam has a concrete caller and invariant, every stored value has a
required lifecycle, and every promised off-chain fact has a durable
reconstruction source.
Reject on-chain logic whose removal would not weaken a consensus transition,
asset invariant, trustless composition seam, or independent reconstruction.
Reject speculative roles, modes, governance hooks, and public APIs without a
concrete caller and invariant.
1---2name: sui-move-architecture3description: Architecture for Sui Move on-chain scope, invariant ownership, package and module boundaries, state and value types, authority, custody, abilities, public APIs, events and replay, and dependency seams. Use when designing, changing, simplifying, or reviewing any of those boundaries.4---56# Sui Move Architecture78Design the smallest on-chain system that preserves consensus, custody, composition, and reconstruction.910Target repository instructions, accepted design records, pinned toolchain behavior, and published compatibility commitments take precedence over this standard's examples.1112## Apply the standard13141. Establish the target's publication status, accepted decisions, compatibility15 promises, and pinned dependencies. This context is complete when each source16 of authority for the design has been located or recorded as an evidence gap.172. Map every affected invariant to its canonical state, owning module,18 authorized transitions, public consumers, and reconstruction source. The map19 is complete when every changed fact and callable seam has one owner.203. Apply every relevant rule below and every triggered reference to that map.21 Resolve each conflict in favor of the target's accepted contract rather than22 this skill's examples.234. Finish only when the architecture gate accounts for every changed package,24 module, state field, authority, public type or function, event, and external25 dependency.2627## Load matching references2829- **Object custody:** Read30 [Storage and indexer-visible object custody](references/indexer-visible-object-custody.md)31 completely when choosing inline or dynamic child storage, or when a `key`32 object is stored, nested, wrapped, indexed, or expected to remain discoverable33 by its original ID.34- **Events:** Read [Events](references/events.md) completely when adding,35 compacting, changing, indexing, or reviewing events, payload fields, emitters,36 or replay promises.37- **Value types:** Read38 [Reusable value invariants](references/reusable-value-invariants.md)39 completely when extracting, composing, simplifying, or reviewing a sealed40 value type, validated unit wrapper, or field relationship reused by multiple41 domain types.4243Read every matching reference; keep the loaded set to the triggered branches.4445## Decide what belongs on chain4647Put logic or state on chain only when at least one of these conditions applies:4849- authorize, reject, or order a consensus-critical transition;50- custody, allocate, conserve, mint, burn, or settle assets;51- provide a synchronous result for trustless contract composition;52- commit the smallest primitive fact required for independent reconstruction.5354Keep deterministic projections, formatting, aggregation, previews, transaction construction, and convenience reads off chain. A transition can recompute a derived value internally when it must enforce fees, slippage, bounds, conservation, or another invariant.5556## Choose package boundaries5758- Start with one core package.59- Add a package only for a real security, deployment, ownership, or dependency boundary.60- Put DEX, oracle, bridge, or other volatile external dependencies in adapter packages.61- Make adapters depend inward on a narrow core interface. Never make core import an adapter.62- Keep independently deployed systems separate when core does not require their result synchronously.63- Do not split packages only to shorten files or mirror another repository.64- Do not ship placeholder integrations, dummy returns, silent no-ops, `todo`, or production functions whose intended path aborts. A dependency interface link stub that is never executed and is verified against deployed bytecode is not a production implementation.6566Use this default direction:6768```text69types, errors, constants, units, pure math70 ↓71state and lifecycle transitions72 ↓73fees, allocations, migration handoffs74 ↓75external adapters and independent systems76 ↓77SDK, indexing, transaction, and deployment tooling78```7980## Organize modules by invariant ownership8182- Make one module the owner of each coherent invariant set. A module is an83 invariant owner, not a bucket for one function, one type, or one conceptual84 subtopic.85- Keep code together when it has the same authority, lifecycle, consumers, and86 reason to change. The owning module may contain all of the related types,87 transitions, checks, and private helpers needed to preserve its invariants.88- Split a module only when the seam enforces a real security, custody,89 dependency-isolation, or visibility boundary. When the required boundary is90 independent deployment, split the package rather than pretending a module91 can deploy independently.92- A small module is justified when it enforces one of those boundaries;93 otherwise it is navigation overhead. Do not split merely to shorten a file,94 mirror a function or type taxonomy, or give every nameable subtopic a home.95- When two proposed modules share authority, lifecycle, consumers, and reason96 to change, combine them unless their seam enforces one of those boundaries.97- Put state with the lifecycle that mutates it.98- Put a capability type in the module that owns its one-time issuance and lifecycle.99- Put authorization checks beside the private authority representation they inspect.100- Extract a small authority module only for a real shared domain or to break a real module cycle.101- Separate live mutable settings from cold policy snapshotted into newly created state.102- Store and mutate each administrative resource in the module that defines its103 authority, lifecycle, and invariants.104- Do not combine capability custody, ACL membership, cryptographic keys,105 identity, fees, upgrades, or product configuration in a generic `config`,106 `manager`, or `admin` module.107- Avoid generic `config`, `utils`, `manager`, `helper`, or `registry` dumping grounds. Name the domain concern.108109Use these module roles only when needed:110111- package marker module: one-time witness and publisher claim; private `init`;112- `constants`: package-wide protocol constants as package macros;113- `errors`: package error registry with no state or assertions;114- `events`: payload schemas and package-only emitters;115- `events_wrapper`: the package's one envelope and raw emission seam;116- authority or `acl`: root rotation, routine membership, and ephemeral sign-in witness;117- runtime configuration: the smallest live settings;118- cold configuration: bounded policy copied into new domain objects;119- package administration: validated custody of `Publisher` and `UpgradeCap`;120- domain modules: lifecycle transitions and owned invariants;121- pure math modules: deterministic arithmetic without storage or transfer effects;122- adapter packages: sealed venue witness, external dependency, and venue settlement.123124Give an initializer an OTW parameter only when it validates, transforms,125consumes, or stores that witness for its own concern. Other initializers take126`TxContext` only.127128Treat initialization as publication-only. Package upgrades do not rerun module129initializers, so post-upgrade setup belongs in an explicit authorized migration130or activation transition.131132## Make invalid states hard to express133134- Model lifecycle states with an enum and permit only named transitions.135- Use typed capabilities for durable authority and drop-only witnesses for ephemeral authorization.136- Use a non-copyable, non-droppable resource for one-time handoffs and replay prevention.137- Wrap a stored linear resource in `Option<T>` only when a transition must remove it from a borrowed parent; do not pair that option with a redundant claimed flag.138- Use phantom type parameters for domain separation when no value of the type is stored.139- Keep reserves, fees, pending burns, allocations, refunds, and claims in separate balances when their ownership or terminal treatment differs.140- Use the defining module's exclusive construction and field access to enforce the type's invariants. Expose only constructors and mutation operations that create valid state and preserve those invariants.141- Give shared top-level state `key` only unless transfer or nesting is an explicit requirement.142- Add `store`, `copy`, or `drop` only when the intended lifecycle requires it.143- Treat every published struct or enum layout and ability set as fixed across144 compatible upgrades.145- To change stored shape, introduce a new type and an authorized migration, or146 use a dynamic-extension seam designed before publication.147- For every stored field, name its canonical authority and the transition that needs a local copy. Reject a mirror when construction makes disagreement unreachable or no operation can change the copy independently.148- Do not duplicate state already owned by a native registry or canonical object merely to revalidate it later. Read the authority at the transition that needs it, unless a sealed construction proof already establishes the invariant.149- Prefer consuming a unique authorization resource over storing a parallel `claimed` flag or nonce.150- Decide upgrade seams before publication. A compatible upgrade cannot add151 fields to an existing struct, so include version or migration fields only152 when an accepted upgrade plan requires them.153- Otherwise use a new type or a predesigned dynamic-extension seam instead of154 speculative flags, modes, roles, or governance hooks.155156## Design authority by scope157158- A protocol-global admin cap normally carries only its identity. Revocation comes from live shared registry state.159- A per-instance cap carries target IDs only when those IDs define its exact authority domain.160- The module that issues a capability owns its destruction, rotation, or consumption rules.161- Separate root authority from routine administration.162- Do not make object possession or an address check imply broader authority than the type promises.163- Do not pass overlapping capabilities to one operation.164165## Protect dependency integrity166167- Pin Git dependencies to immutable full commit SHAs and commit lockfiles.168- Keep core buildable without mutable external heads.169- Treat external interfaces as hostile versioned boundaries.170- Never rely on an external dependency to enforce core invariants. Validate adapter inputs and outputs, limit delegated authority and custody, and re-check asset, state, and economic invariants before committing settlement.171- Verify the deployed package, original type origin, ABI, and economic assumptions consumed by each adapter.172173## Architecture gate174175Architecture is complete only when every changed fact has a canonical owner,176every public seam has a concrete caller and invariant, every stored value has a177required lifecycle, and every promised off-chain fact has a durable178reconstruction source.179180Reject on-chain logic whose removal would not weaken a consensus transition,181asset invariant, trustless composition seam, or independent reconstruction.182Reject speculative roles, modes, governance hooks, and public APIs without a183concrete caller and invariant.