move-patterns

Move design patterns — events, error handling, one-time witness (OTW), capability pattern, and pure functions/composability.

15 stars

Best use case

move-patterns is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Move design patterns — events, error handling, one-time witness (OTW), capability pattern, and pure functions/composability.

Teams using move-patterns should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/patterns/SKILL.md --create-dirs "https://raw.githubusercontent.com/MystenLabs/sui-dev-skills/main/move/patterns/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/patterns/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How move-patterns Compares

Feature / Agentmove-patternsStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Move design patterns — events, error handling, one-time witness (OTW), capability pattern, and pure functions/composability.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

## 1. Events

Emit events for all state-changing operations that clients need to observe:

```move
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,
});
```

---

## 2. Error Handling

Error constants use `EPascalCase` and `u64` values:

```move
const EInsufficientLiquidity: u64 = 0;
const EZeroAmount: u64 = 1;

assert!(amount > 0, EZeroAmount);
```

### Clever errors

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:

```move
#[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:

```move
// ✅ Valid — line number is embedded automatically
assert!(amount > 0);
```

This is fine for internal invariants where the line number alone is enough context.

---

## 3. One-Time Witness (OTW) Pattern

Use the OTW pattern for modules that need a unique, uncopyable proof-of-publication (e.g., coin types, publisher objects):

```move
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());
}
```

---

## 4. Capability Pattern

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:

```move
// ✅ 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.

---

## 5. Pure Functions and Composability

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:

```move
// ✅ 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.

Related Skills

move-syntax

15
from MystenLabs/sui-dev-skills

Move language syntax — module layout, imports, mutability, visibility, method syntax, enums, macros, and comments.

move-stdlib

15
from MystenLabs/sui-dev-skills

Common Sui Move standard library patterns — strings, Coin/Balance, Option, addresses, UID, TxContext, vectors, and struct unpacking.

move-setup

15
from MystenLabs/sui-dev-skills

Move package setup (Move.toml, edition, dependencies), building, testing, and common pitfalls from other Move dialects.

move-objects

15
from MystenLabs/sui-dev-skills

Sui object model — struct declarations, abilities (key/store/copy/drop), object ownership, naming conventions, and dynamic fields.

move

15
from MystenLabs/sui-dev-skills

Move smart contract development on Sui. Use when writing, reviewing, or debugging Move code, Move.toml configuration, or Sui object model patterns.

sui-ts-sdk

15
from MystenLabs/sui-dev-skills

Sui TypeScript SDK — PTB construction, client setup, transaction execution, and on-chain queries. Use when writing code that interacts with the Sui blockchain via @mysten/sui. These patterns apply in both backend scripts and frontend apps. For frontend-specific setup (dApp Kit, wallet adapters, React hooks), use the sui-frontend skill first or alongside this one.

sui-frontend

15
from MystenLabs/sui-dev-skills

Sui frontend dApp development with @mysten/dapp-kit-react (React) and @mysten/dapp-kit-core (Vue, vanilla JS, other frameworks). Use when building browser apps that connect to Sui wallets, query on-chain data, or execute transactions. Use alongside the sui-ts-sdk skill for PTB construction patterns.

sui-dev

15
from MystenLabs/sui-dev-skills

Full-stack Sui blockchain development — Move smart contracts, TypeScript SDK, and frontend dApp Kit. Routes to the appropriate sub-skill based on what the user is building.

performing-lateral-movement-with-wmiexec

16
from plurigrid/asi

Perform lateral movement across Windows networks using WMI-based remote execution techniques including Impacket wmiexec.py, CrackMapExec, and native WMI commands for stealthy post-exploitation during red team engagements.

performing-lateral-movement-detection

16
from plurigrid/asi

Detects lateral movement techniques including Pass-the-Hash, PsExec, WMI execution, RDP pivoting, and SMB-based spreading using SIEM correlation of Windows event logs, network flow data, and endpoint telemetry mapped to MITRE ATT&CK Lateral Movement (TA0008) techniques.

move-smith-fuzzer

16
from plurigrid/asi

Move Smith Fuzzer Skill

hunting-for-lateral-movement-via-wmi

16
from plurigrid/asi

Detect WMI-based lateral movement by analyzing Windows Event ID 4688 process creation and Sysmon Event ID 1 for WmiPrvSE.exe child process patterns, remote process execution, and WMI event subscription persistence.