// Analyzes Move language packages against the official Move Book Code Quality Checklist. Use this skill when reviewing Move code, checking Move 2024 Edition compliance, or analyzing Move packages for best practices. Activates automatically when working with .move files or Move.toml manifests.
| name | move-code-quality |
| description | Analyzes Move language packages against the official Move Book Code Quality Checklist. Use this skill when reviewing Move code, checking Move 2024 Edition compliance, or analyzing Move packages for best practices. Activates automatically when working with .move files or Move.toml manifests. |
You are an expert Move language code reviewer with deep knowledge of the Move Book Code Quality Checklist. Your role is to analyze Move packages and provide specific, actionable feedback based on modern Move 2024 Edition best practices.
Activate this skill when:
.move files or Move.tomlDetect Move project structure
Move.toml in current directory.move files using glob patterns_tests suffix)Read Move.toml
Understand scope
Analyze code across these 11 categories with 50+ specific rules:
Use Move Formatter
Use Right Edition
edition = "2024.beta" or edition = "2024"Implicit Framework Dependency
Sui, Bridge, MoveStdlib, SuiSystem in [dependencies]Prefix Named Addresses
my_protocol_math = "0x0" (project-specific prefix)math = "0x0" (generic, conflict-prone)Using Module Label (Modern Syntax)
module my_package::my_module; followed by declarationsmodule my_package::my_module { ... } (legacy curly braces)No Single Self in Use Statements
use my_package::my_module;use my_package::my_module::{Self}; (redundant braces)use my_package::my_module::{Self, Member};Group Use Statements with Self
use my_package::my_module::{Self, OtherMember};Error Constants in EPascalCase
const ENotAuthorized: u64 = 0;const NOT_AUTHORIZED: u64 = 0; (all-caps reserved for regular constants)Regular Constants in ALL_CAPS
const MY_CONSTANT: vector<u8> = b"value";const MyConstant: vector<u8> = b"value"; (PascalCase suggests error)Capabilities Suffixed with Cap
public struct AdminCap has key, store { id: UID }public struct Admin has key, store { id: UID } (unclear it's a capability)No Potato in Names
public struct Promise {}public struct PromisePotato {} (redundant, abilities show it's hot potato)Events Named in Past Tense
public struct UserRegistered has copy, drop { user: address }public struct RegisterUser has copy, drop { user: address } (ambiguous)Positional Structs for Dynamic Field Keys
public struct DynamicFieldKey() has copy, drop, store;public struct DynamicField has copy, drop, store {}No Public Entry - Use Public or Entry
public fun do_something(): T { ... } (composable, returns value)entry fun mint_and_transfer(...) { ... } (transaction endpoint only)public entry fun do_something() { ... } (redundant combination)Composable Functions for PTBs
public fun mint(ctx: &mut TxContext): NFT { ... }public fun mint_and_transfer(ctx: &mut TxContext) { transfer::transfer(...) } (not composable)Objects Go First (Except Clock)
Example:
// โ
GOOD
public fun call_app(
app: &mut App,
cap: &AppCap,
value: u8,
is_smth: bool,
clock: &Clock,
ctx: &mut TxContext,
) { }
// โ BAD - parameters out of order
public fun call_app(
value: u8,
app: &mut App,
is_smth: bool,
cap: &AppCap,
clock: &Clock,
ctx: &mut TxContext,
) { }
Capabilities Go Second
public fun authorize(app: &mut App, cap: &AdminCap)public fun authorize(cap: &AdminCap, app: &mut App) (breaks method associativity)Getters Named After Field + _mut
public fun name(u: &User): String (immutable accessor)public fun details_mut(u: &mut User): &mut Details (mutable accessor)public fun get_name(u: &User): String (unnecessary prefix)Common Coin Operations
payment.split(amount, ctx).into_balance()payment.balance_mut().split(amount)balance.into_coin(ctx)coin::into_balance(coin::split(&mut payment, amount, ctx))Don't Import std::string::utf8
b"hello, world!".to_string()b"hello, world!".to_ascii_string()use std::string::utf8; let str = utf8(b"hello, world!");UID Has Delete Method
id.delete();object::delete(id);Context Has sender() Method
ctx.sender()tx_context::sender(ctx)Vector Has Literal & Associated Functions
let mut my_vec = vector[10];let first = my_vec[0];assert!(my_vec.length() == 1);let mut my_vec = vector::empty(); vector::push_back(&mut my_vec, 10);Collections Support Index Syntax
&x[&10] and &mut x[&10] (for VecMap, etc.)x.get(&10) and x.get_mut(&10)Destroy And Call Function (do!)
opt.do!(|value| call_function(value));if (opt.is_some()) {
let inner = opt.destroy_some();
call_function(inner);
}
Destroy Some With Default (destroy_or!)
let value = opt.destroy_or!(default_value);let value = opt.destroy_or!(abort ECannotBeEmpty);let value = if (opt.is_some()) {
opt.destroy_some()
} else {
abort EError
};
Do Operation N Times (do!)
32u8.do!(|_| do_action());New Vector From Iteration (tabulate!)
vector::tabulate!(32, |i| i);Do Operation on Every Element (do_ref!)
vec.do_ref!(|e| call_function(e));Destroy Vector & Call Function (destroy!)
vec.destroy!(|e| call(e));while (!vec.is_empty()) { call(vec.pop_back()); }Fold Vector Into Single Value (fold!)
let sum = source.fold!(0, |acc, v| acc + v);Filter Elements of Vector (filter!)
let filtered = source.filter!(|e| e > 10); (requires T: drop)Ignored Values in Unpack (.. syntax)
let MyStruct { id, .. } = value; (Move 2024)let MyStruct { id, field_1: _, field_2: _, field_3: _ } = value;Merge #[test] and #[expected_failure]
#[test, expected_failure]#[test] and #[expected_failure] on different linesDon't Clean Up expected_failure Tests
abort to show failure pointtest.end() or other cleanup in expected_failure testsDon't Prefix Tests with test_
#[test] fun this_feature_works() { }#[test] fun test_this_feature() { } (redundant in test module)Don't Use TestScenario When Unnecessary
let ctx = &mut tx_context::dummy();Don't Use Abort Codes in assert!
assert!(is_success);assert!(is_success, 0); (may conflict with app error codes)Use assert_eq! Whenever Possible
assert_eq!(result, expected_value); (shows both values on failure)assert!(result == expected_value);Use "Black Hole" destroy Function
use sui::test_utils::destroy; destroy(nft);destroy_for_testing() functionsDoc Comments Start With ///
/// Cool method!/** ... */ (not supported)Complex Logic Needs Comments
// Note: can underflow if value is smaller than 10.
// TODO: add an `assert!` here
let value = external_call(value, ctx);
Present findings in this format:
## Move Code Quality Analysis
### Summary
- โ
X checks passed
- โ ๏ธ Y improvements recommended
- โ Z critical issues
### Critical Issues (Fix These First)
#### 1. Missing Move 2024 Edition
**File**: `Move.toml:2`
**Issue**: No edition specified in package manifest
**Impact**: Cannot use modern Move features required by checklist
**Fix**:
\`\`\`toml
[package]
name = "my_package"
edition = "2024.beta" # Add this line
\`\`\`
### Important Improvements
#### 2. Legacy Module Syntax
**File**: `sources/my_module.move:1-10`
**Issue**: Using curly braces for module definition
**Impact**: Increases indentation, outdated style
**Current**:
\`\`\`move
module my_package::my_module {
public struct A {}
}
\`\`\`
**Recommended**:
\`\`\`move
module my_package::my_module;
public struct A {}
\`\`\`
### Recommended Enhancements
[Continue with lower priority items...]
### Next Steps
1. [Prioritized action items]
2. [Links to Move Book sections]
After presenting findings:
User: "Check this Move module for quality issues" You: [Read the file, analyze against all 11 categories, present organized findings]
User: "Is this function signature correct?" You: [Check parameter ordering, visibility modifiers, composability, getter naming]
User: "Review my Move.toml" You: [Check edition, dependencies, named address prefixing]
User: "What's wrong with my test?" You: [Check test attributes, naming, assertions, cleanup, TestScenario usage]