Sui Move Source Style
Make each file clearly communicate what it owns, who may change it, and how its state is created, updated, and retired, without requiring the reader to trace unrelated modules or call sites.
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 package's edition, compiler and dependency pins, publication
status, local instructions, and existing section vocabulary. Context is
complete when each is known or recorded as an evidence gap.
- Inspect every changed declaration and function in production and test code,
including observable evaluation, abort, mutation, transfer, and emission
order.
- Read every matching reference below, then apply all relevant rules while
preserving target behavior and published compatibility.
- Finish only when every changed file has been checked for ownership,
organization, imports, names, visibility, abilities, signatures, errors,
syntax, locals, API vocabulary, documentation, and test-only seams.
Order module sections
Use this order and omit empty sections:
- module documentation and declaration;
- grouped
use declarations;
// === Constants === or // === Package Constants ===;
// === Errors === when the module defines error constants or macros;
// === Public Types === or // === Public Structs ===;
// === Private Initialization === when applicable;
// === Public Functions ===, subdivided by product operation in large modules;
// === Public Aliases === when aliases materially improve the API;
// === Package Functions ===;
// === Private Functions ===;
// === Test-Only Types === when needed, then // === Test-Only Functions ===.
Use title case. Group large public surfaces by domain operation, not only visibility.
Put locally declared error constants and a dedicated errors.move module's
macros under // === Errors ===. Omit the section when a module only raises
fully qualified errors defined elsewhere.
Format function bodies as logical paragraphs, with one blank line between validation, derivation, mutation or transfer, emission, and return. Do not blank-line every statement or pad tiny helpers.
Organize imports
- Consolidate imports from the same root address into one nested declaration per module. Do not repeat a root on separate lines; follow the repository's root ordering consistently.
- Do not import a member solely to shorten one call. Keep package error macros and one-off framework functions fully qualified; import members used repeatedly. Test modules may import ubiquitous
std::unit_test::{assert_eq, destroy} helpers.
- Do not explicitly import Move 2024 implicit prelude modules such as
std::vector, std::option, sui::object, sui::transfer, or sui::tx_context.
- Use aliases for clear receiver methods or real collisions, not to hide domain distinctions.
Load matching references
- Library reuse: Read
Pinned library reuse completely before
adding, replacing, or reviewing arithmetic, conversion, collection,
iteration, object, transfer, macro, or framework-wrapper logic.
- Move 2024 syntax: Read
Move 2024 source syntax completely before
changing or reviewing receiver aliases, index syntax, direct UID access, or
partial generic inference.
- Errors: Read Errors completely before changing or
reviewing errors, assertions, abort ownership, guard precedence, or diagnostic
compatibility.
- Function shape: Read
Function shape and locals
completely before splitting or simplifying a multi-phase function, extracting
helpers, inlining locals, or changing pass-through parameters.
Read every matching reference; keep the loaded set to the triggered branches.
Follow the target package's pinned edition and compiler when a reference
example differs.
Name by domain meaning
- Use lower snake case for packages, modules, files, functions, variables, and package macros.
- Use UpperCamelCase for structs and enums.
- Use uppercase snake case for ordinary constants.
- Use
EUpperCamelCase for named error constants.
- End durable authority objects in
Cap.
- End ephemeral authorization proofs in
Witness.
- Name linear handoffs and hot potatoes for their domain fact:
Request, Receipt, Payload, Price, or FlashLoan.
- Name phantom type parameters for their role:
Asset, Quote, CoinType, or Venue, not T when the role matters.
- Name fixtures for what they provide:
ClaimFixture, UpgradeFixture.
- Name tests as behavioral facts:
constructor_rejects_zero_allocation.
- Include rounding direction in math names:
apply_down, mul_div_up, to_assets_down.
Keep visibility narrow
- Use
public fun only for real transaction or composition surfaces.
- Use a non-public
entry function only when an operation must be directly callable by transactions but intentionally unavailable to other Move packages. Keep it thin, place endpoint-specific delivery there, and use a public function when cross-package Move composition is intended.
- Use
public(package) fun for cross-module package internals.
- Use plain
fun for same-module implementation details.
- Keep public ABI minimal; an upgrade cannot erase old callable bytecode.
- Treat every existing public function signature as fixed across compatible
upgrades, except for permitted relaxation of generic ability constraints.
- Treat every published struct or enum layout and ability set as fixed.
- Add a new function or type instead of changing a published one. When stored
shape changes, provide an explicit authorized migration.
- Treat
init as initial-publication-only because upgrades do not rerun it. Put post-upgrade setup in a named authorized migration or activation function.
- Keep struct fields private.
- Do not expose production getters, constructors, cleanup, or mutation solely for tests or frontend convenience.
Assign abilities deliberately
- Give each type only the abilities its lifecycle requires.
- Use
key only for values intended to become top-level owned or shared objects.
- Add
store to a capability only when public transfer outside its defining module or nesting is intended.
- Avoid
copy and drop on resources that must be consumed exactly once.
- Use
phantom when the type parameter separates domains without a stored value.
- Move 2024 currently requires every struct type to be public. Do not confuse public type visibility with public authority: fields remain accessible only to the defining module, and constructors and operations should remain private or
public(package) when external use is not intended.
Order parameters
Use this order for public functions:
- primary receiver or state object;
- authorizing capability or witness;
- other domain objects and values;
Clock or other framework reads;
TxContext last.
Use & for reads, &mut for actual mutation, and by value for resources the function consumes. Return newly created owned objects, coins, capabilities, and refunds. Transfer inside core only when delivery is the explicit endpoint contract.
Keep construction and publication separate when possible: new returns the owned value and share consumes it into shared state.
When a returned object has key but not store, provide its consuming sink in the defining module, such as share(self) for shared state or keep(self, ctx) for sender delivery. Do not strand a value that callers cannot transfer or share themselves.
Use precise API vocabulary
- Use
new for the module's primary constructor.
- Use descriptive constructors when variants matter:
new_cancelable, new_irrevocable, new_kinked.
- Name reads exactly for the domain value returned.
- Use
is_* and has_* for predicates.
- Reserve
into_* for a real type conversion and name its destination.
- Use
destroy when a consuming wrapper deletes itself and releases contents.
- Store
Balance inside objects and accept or return Coin at public asset boundaries unless a different custody model is deliberate.
- Use the exact domain verb for a mutation:
grant_admin, claim_fees, begin_migration.
- Reject vague production names such as
fields, parts, data, info, handle, and process unless one is the precise domain term.
- Permit
<event>_fields only as a narrow test-only payload snapshot convention.
Document the contract
- Document public types, functions, invariants, hard bounds, rounding, and irreversible effects.
- Explain the authority and consumption semantics of capabilities, witnesses, and hot potatoes.
- Put all test constructors, malformed-value seams, field snapshots, and mutation hooks at the end with
#[test_only].
- Use a narrow lint suppression only after reproducing the warning under the pinned compiler. Put the rationale adjacent to it.
1---2name: sui-move-source-style3description: Source conventions for Sui Move organization, names, visibility, abilities, signatures, errors, syntax, pinned-library reuse, locals, API vocabulary, documentation, and test-only seams. Use when writing, simplifying, refactoring, or reviewing `.move` production or test files.4---56# Sui Move Source Style78Make each file clearly communicate what it owns, who may change it, and how its state is created, updated, and retired, without requiring the reader to trace unrelated modules or call sites.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 package's edition, compiler and dependency pins, publication15 status, local instructions, and existing section vocabulary. Context is16 complete when each is known or recorded as an evidence gap.172. Inspect every changed declaration and function in production and test code,18 including observable evaluation, abort, mutation, transfer, and emission19 order.203. Read every matching reference below, then apply all relevant rules while21 preserving target behavior and published compatibility.224. Finish only when every changed file has been checked for ownership,23 organization, imports, names, visibility, abilities, signatures, errors,24 syntax, locals, API vocabulary, documentation, and test-only seams.2526## Order module sections2728Use this order and omit empty sections:29301. module documentation and declaration;312. grouped `use` declarations;323. `// === Constants ===` or `// === Package Constants ===`;334. `// === Errors ===` when the module defines error constants or macros;345. `// === Public Types ===` or `// === Public Structs ===`;356. `// === Private Initialization ===` when applicable;367. `// === Public Functions ===`, subdivided by product operation in large modules;378. `// === Public Aliases ===` when aliases materially improve the API;389. `// === Package Functions ===`;3910. `// === Private Functions ===`;4011. `// === Test-Only Types ===` when needed, then `// === Test-Only Functions ===`.4142Use title case. Group large public surfaces by domain operation, not only visibility.43Put locally declared error constants and a dedicated `errors.move` module's44macros under `// === Errors ===`. Omit the section when a module only raises45fully qualified errors defined elsewhere.46Format function bodies as logical paragraphs, with one blank line between validation, derivation, mutation or transfer, emission, and return. Do not blank-line every statement or pad tiny helpers.4748## Organize imports4950- Consolidate imports from the same root address into one nested declaration per module. Do not repeat a root on separate lines; follow the repository's root ordering consistently.51- Do not import a member solely to shorten one call. Keep package error macros and one-off framework functions fully qualified; import members used repeatedly. Test modules may import ubiquitous `std::unit_test::{assert_eq, destroy}` helpers.52- Do not explicitly import Move 2024 implicit prelude modules such as `std::vector`, `std::option`, `sui::object`, `sui::transfer`, or `sui::tx_context`.53- Use aliases for clear receiver methods or real collisions, not to hide domain distinctions.5455## Load matching references5657- **Library reuse:** Read58 [Pinned library reuse](references/pinned-library-reuse.md) completely before59 adding, replacing, or reviewing arithmetic, conversion, collection,60 iteration, object, transfer, macro, or framework-wrapper logic.61- **Move 2024 syntax:** Read62 [Move 2024 source syntax](references/move-2024-syntax.md) completely before63 changing or reviewing receiver aliases, index syntax, direct UID access, or64 partial generic inference.65- **Errors:** Read [Errors](references/errors.md) completely before changing or66 reviewing errors, assertions, abort ownership, guard precedence, or diagnostic67 compatibility.68- **Function shape:** Read69 [Function shape and locals](references/function-shape-and-locals.md)70 completely before splitting or simplifying a multi-phase function, extracting71 helpers, inlining locals, or changing pass-through parameters.7273Read every matching reference; keep the loaded set to the triggered branches.74Follow the target package's pinned edition and compiler when a reference75example differs.7677## Name by domain meaning7879- Use lower snake case for packages, modules, files, functions, variables, and package macros.80- Use UpperCamelCase for structs and enums.81- Use uppercase snake case for ordinary constants.82- Use `EUpperCamelCase` for named error constants.83- End durable authority objects in `Cap`.84- End ephemeral authorization proofs in `Witness`.85- Name linear handoffs and hot potatoes for their domain fact: `Request`, `Receipt`, `Payload`, `Price`, or `FlashLoan`.86- Name phantom type parameters for their role: `Asset`, `Quote`, `CoinType`, or `Venue`, not `T` when the role matters.87- Name fixtures for what they provide: `ClaimFixture`, `UpgradeFixture`.88- Name tests as behavioral facts: `constructor_rejects_zero_allocation`.89- Include rounding direction in math names: `apply_down`, `mul_div_up`, `to_assets_down`.9091## Keep visibility narrow9293- Use `public fun` only for real transaction or composition surfaces.94- Use a non-public `entry` function only when an operation must be directly callable by transactions but intentionally unavailable to other Move packages. Keep it thin, place endpoint-specific delivery there, and use a `public` function when cross-package Move composition is intended.95- Use `public(package) fun` for cross-module package internals.96- Use plain `fun` for same-module implementation details.97- Keep public ABI minimal; an upgrade cannot erase old callable bytecode.98- Treat every existing public function signature as fixed across compatible99 upgrades, except for permitted relaxation of generic ability constraints.100- Treat every published struct or enum layout and ability set as fixed.101- Add a new function or type instead of changing a published one. When stored102 shape changes, provide an explicit authorized migration.103- Treat `init` as initial-publication-only because upgrades do not rerun it. Put post-upgrade setup in a named authorized migration or activation function.104- Keep struct fields private.105- Do not expose production getters, constructors, cleanup, or mutation solely for tests or frontend convenience.106107## Assign abilities deliberately108109- Give each type only the abilities its lifecycle requires.110- Use `key` only for values intended to become top-level owned or shared objects.111- Add `store` to a capability only when public transfer outside its defining module or nesting is intended.112- Avoid `copy` and `drop` on resources that must be consumed exactly once.113- Use `phantom` when the type parameter separates domains without a stored value.114- Move 2024 currently requires every struct type to be public. Do not confuse public type visibility with public authority: fields remain accessible only to the defining module, and constructors and operations should remain private or `public(package)` when external use is not intended.115116## Order parameters117118Use this order for public functions:1191201. primary receiver or state object;1212. authorizing capability or witness;1223. other domain objects and values;1234. `Clock` or other framework reads;1245. `TxContext` last.125126Use `&` for reads, `&mut` for actual mutation, and by value for resources the function consumes. Return newly created owned objects, coins, capabilities, and refunds. Transfer inside core only when delivery is the explicit endpoint contract.127128Keep construction and publication separate when possible: `new` returns the owned value and `share` consumes it into shared state.129130When a returned object has `key` but not `store`, provide its consuming sink in the defining module, such as `share(self)` for shared state or `keep(self, ctx)` for sender delivery. Do not strand a value that callers cannot transfer or share themselves.131132## Use precise API vocabulary133134- Use `new` for the module's primary constructor.135- Use descriptive constructors when variants matter: `new_cancelable`, `new_irrevocable`, `new_kinked`.136- Name reads exactly for the domain value returned.137- Use `is_*` and `has_*` for predicates.138- Reserve `into_*` for a real type conversion and name its destination.139- Use `destroy` when a consuming wrapper deletes itself and releases contents.140- Store `Balance` inside objects and accept or return `Coin` at public asset boundaries unless a different custody model is deliberate.141- Use the exact domain verb for a mutation: `grant_admin`, `claim_fees`, `begin_migration`.142- Reject vague production names such as `fields`, `parts`, `data`, `info`, `handle`, and `process` unless one is the precise domain term.143- Permit `<event>_fields` only as a narrow test-only payload snapshot convention.144145## Document the contract146147- Document public types, functions, invariants, hard bounds, rounding, and irreversible effects.148- Explain the authority and consumption semantics of capabilities, witnesses, and hot potatoes.149- Put all test constructors, malformed-value seams, field snapshots, and mutation hooks at the end with `#[test_only]`.150- Use a narrow lint suppression only after reproducing the warning under the pinned compiler. Put the rationale adjacent to it.