원클릭으로
sui-move-patterns
Move design patterns — events, error handling, one-time witness (OTW), capability pattern, and pure functions/composability.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Move design patterns — events, error handling, one-time witness (OTW), capability pattern, and pure functions/composability.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
This skill should be used when the user says "run the sprint", "autonomous sprint", "run all stories", "execute sprint plan", "complete all epics", or asks to autonomously execute all stories in the current sprint. Drives Claude Code through create-story, dev-story, quality gates, and code-review in a continuous loop without human intervention until all stories are done or blocked.
This skill should be used when the user asks to "upgrade a Solana program through Squads", "set up squads multisig deployment", "create a Squads upgrade proposal", "configure squads-program-action", "deploy via Squads multisig", "set up Squads CI/CD pipeline", "squads GitHub Actions workflow", or mentions Solana program upgrades involving Squads v4 multisig governance.
This skill should be used when the user asks to "create a diagram", "make an architecture diagram", "design a system diagram", "draw an architecture", "create a technical diagram", "make a deployment diagram", "visualize infrastructure", "create a data flow diagram", "draw system components", "make a network diagram", "diagram a cloud architecture", "draw an AWS diagram", "create a Kubernetes diagram", or mentions architecture, system design, infrastructure diagrams, component relationships, or data flow. Do NOT trigger for cover images, step-by-step process flows, or infographics.
This skill should be used when the user asks to "write something satirical about work", "roast a corporate event", "write a vignette about a bad meeting", "satirise startup culture", "write a LinkedIn post parody", "make fun of OKRs", "write a scene about a reorg", "satirise remote work rituals", "mock team-building activities", "write a satirical all-hands recap", or wants any comedic or ironic depiction of organisational life, management behaviour, or workplace absurdity across offices, startups, remote teams, or any setting where hierarchy produces comedy.
Write technical documentation, blog articles, code reviews, and product writeups in a storytelling style that's direct, humble, and problem-first. Use this skill whenever the user wants to publish articles, write technical reviews, document processes, or explain systems in a way that's concrete and human. This includes API docs, how-to guides, product comparisons, incident postmortems, feature writeups, and code reviews. The skill applies whether you're writing for public blogs, internal teams, or personal knowledge vaults. Provide both public-facing (slightly more scaffolding) and internal (tighter) versions when requested.
This skill should be used when the user asks to "create a cover image", "generate a blog cover", "make an OG image", "design a social preview", "create a thumbnail for a post", "add a cover to my article", "make a featured image", "create a header image for a post", "generate an open graph image", or mentions cover images, OG images, social preview images, or blog post thumbnails. Do NOT trigger for diagrams, architecture diagrams, or process flows.
| name | sui-move-patterns |
| description | Move design patterns — events, error handling, one-time witness (OTW), capability pattern, and pure functions/composability. |
Emit events for all state-changing operations that clients need to observe:
use sui::event;
public struct LiquidityAdded has copy, drop {
pool_id: ID,
amount_x: u64,
amount_y: u64,
lp_minted: u64,
}
// Inside function:
event::emit(LiquidityAdded {
pool_id: object::id(pool),
amount_x,
amount_y,
lp_minted,
});
Error constants use EPascalCase and u64 values:
const EInsufficientLiquidity: u64 = 0;
const EZeroAmount: u64 = 1;
assert!(amount > 0, EZeroAmount);
Annotating a constant with #[error] allows it to carry a human-readable message. The value can be any valid constant type — vector<u8> is most common for string messages:
#[error]
const EInsufficientLiquidity: vector<u8> = b"Insufficient liquidity in pool";
assert!(reserves > 0, EInsufficientLiquidity);
abort EInsufficientLiquidity
At runtime, the Sui CLI and GraphQL server automatically decode these into a readable message:
Error from '0x2::amm::swap' (line 42), abort 'EInsufficientLiquidity': "Insufficient liquidity in pool"
Gotcha: clever error abort codes encode the source line number, so their u64 value can change if the file is reformatted or lines shift. Don't hardcode clever error abort codes in tests or off-chain tooling — match by constant name instead.
**assert! without an abort code** is also valid and auto-derives a clever abort code from the source line:
// ✅ Valid — line number is embedded automatically
assert!(amount > 0);
This is fine for internal invariants where the line number alone is enough context.
Use the OTW pattern for modules that need a unique, uncopyable proof-of-publication (e.g., coin types, publisher objects):
public struct MY_MODULE has drop {}
fun init(otw: MY_MODULE, ctx: &mut TxContext) {
// The OTW name must exactly match the module name in ALL_CAPS
let publisher = package::claim(otw, ctx);
transfer::public_transfer(publisher, ctx.sender());
}
Use capability objects to gate privileged functions instead of checking ctx.sender(). This is more composable and testable — the capability can be held by a contract, not just a wallet:
// ✅ Capability-gated
public struct AdminCap has key, store { id: UID }
public fun set_fee(_: &AdminCap, pool: &mut Pool, new_fee: u64) {
pool.fee_bps = new_fee;
}
// ❌ Sender check — not composable with other contracts
public fun set_fee(pool: &mut Pool, ctx: &TxContext) {
assert!(ctx.sender() == pool.admin, ENotAdmin);
}
Note the parameter order: the object (pool) comes before the primitive (new_fee), and _: &AdminCap follows the objects-then-capabilities ordering from the syntax skill's §3 Visibility.
Keep core logic functions pure — they take objects by reference/value and return values. Do not call transfer::transfer to deliver results to the caller:
// ✅ Pure — composable with other protocols
public fun swap<X, Y>(
pool: &mut Pool<X, Y>,
coin_in: Coin<X>,
ctx: &mut TxContext,
): Coin<Y> {
// ... swap logic
}
// ❌ Transfer inside core logic breaks composability
public fun swap<X, Y>(pool: &mut Pool<X, Y>, coin_in: Coin<X>, ctx: &mut TxContext) {
let coin_out = /* ... */;
transfer::public_transfer(coin_out, ctx.sender()); // ❌
}
Internal transfers are acceptable when they serve the function's own mechanics (e.g., burning tokens to @0x0, sharing a newly created object). The key rule is: don't transfer the caller's results — return them instead so the caller can compose.
Return excess coins even if their value is zero — let the caller decide what to do with them.
| Task | Load |
|---|---|
| Structs, abilities, dynamic fields | sui-move-object |
| Coin / Balance for pure-function examples | sui-move-stdlib |
| Visibility rules, parameter ordering | sui-move-syntax |
| Package config, building, testing | sui-move-setup |