write-contracts

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Write Contracts Skill

合约编写Skill

Core Rules

核心规则

Digital Assets (NFTs) ⭐ CRITICAL

数字资产(NFTs)⭐ 至关重要

  1. ALWAYS use Digital Asset (DA) standard for ALL NFT-related contracts (collections, marketplaces, minting)
  2. ALWAYS import
    aptos_token_objects::collection
    and
    aptos_token_objects::token
    modules
  3. ALWAYS use
    Object<AptosToken>
    for NFT references (NOT generic
    Object<T>
    )
  4. NEVER use legacy TokenV1 standard or
    aptos_token::token
    module (deprecated)
  5. See
    ../../../patterns/move/DIGITAL_ASSETS.md
    for complete NFT patterns
  1. 所有NFT相关合约(集合、市场、铸造)必须使用Digital Asset(DA)标准
  2. 必须导入
    aptos_token_objects::collection
    aptos_token_objects::token
    模块
  3. 必须使用
    Object<AptosToken>
    作为NFT引用(不可使用通用的
    Object<T>
  4. 禁止使用老旧的TokenV1标准或
    aptos_token::token
    模块(已废弃)
  5. 完整的NFT模式可参考
    ../../../patterns/move/DIGITAL_ASSETS.md

Object Model

对象模型

  1. ALWAYS use
    Object<T>
    for all object references (NEVER raw addresses)
  2. Generate all refs (TransferRef, DeleteRef) in constructor before ConstructorRef destroyed
  3. Return
    Object<T>
    from constructors (NEVER return ConstructorRef)
  4. Verify ownership with
    object::owner(obj) == signer::address_of(user)
  1. 所有对象引用必须使用
    Object<T>
    (禁止使用原始地址)
  2. 构造函数中需要在ConstructorRef销毁前生成所有所需的引用(TransferRef、DeleteRef)
  3. 构造函数需要返回
    Object<T>
    (禁止返回ConstructorRef)
  4. 使用
    object::owner(obj) == signer::address_of(user)
    验证所有权

Security

安全要求

  1. ALWAYS verify signer authority in entry functions:
    assert!(signer::address_of(user) == expected, E_UNAUTHORIZED)
  2. ALWAYS validate inputs: non-zero amounts, address validation, string length checks
  3. NEVER expose
    &mut
    references in public functions
  4. NEVER skip signer verification in entry functions
  1. 入口函数必须验证签名者权限
    assert!(signer::address_of(user) == expected, E_UNAUTHORIZED)
  2. 必须做输入校验:非零金额校验、地址合法性校验、字符串长度校验
  3. 禁止在公共函数中暴露
    &mut
    可变引用
  4. 入口函数禁止省略签名者验证步骤

Modern Syntax

现代语法规范

  1. Use inline functions and lambdas for iteration
  2. Use receiver-style method calls:
    obj.is_owner(user)
    (define first param as
    self
    )
  3. Use vector indexing:
    vector[index]
    instead of
    vector::borrow()
  4. Use direct named addresses:
    @marketplace_addr
    (NOT helper functions)
  1. 迭代操作优先使用内联函数和lambda表达式
  2. 使用接收者风格的方法调用:
    obj.is_owner(user)
    (将第一个参数定义为
    self
  3. 使用向量索引写法:
    vector[index]
    替代
    vector::borrow()
  4. 使用直接命名地址:
    @marketplace_addr
    (不要使用辅助函数获取)

Required Patterns

必要模式

  1. Use init_module for contract initialization on deployment
  2. Emit events for ALL significant activities (create, transfer, update, delete)
  3. Define clear error constants with descriptive names (E_NOT_OWNER, E_INSUFFICIENT_BALANCE)
  1. 合约部署时的初始化逻辑使用
    init_module
    实现
  2. 所有重要操作都需要触发事件(创建、转移、更新、删除)
  3. 定义语义清晰的错误常量(如E_NOT_OWNER、E_INSUFFICIENT_BALANCE)

Testability

可测试性要求

  1. Add accessor functions for struct fields - tests in separate modules cannot access struct fields directly
  2. Use
    #[view]
    annotation
    for read-only accessor functions
  3. Return tuples from accessors for multi-field access:
    (seller, price, timestamp)
  4. Place
    #[view]
    BEFORE doc comments
    -
    /// comment
    before
    #[view]
    causes compiler warnings. Write
    #[view]
    first, then
    ///
  1. 为结构体字段添加访问器函数——独立模块中的测试无法直接访问结构体字段
  2. 只读访问器函数添加
    #[view]
    注解
  3. 多字段访问的访问器返回元组:
    (seller, price, timestamp)
  4. #[view]
    注解要写在文档注释之前——如果
    ///
    注释写在
    #[view]
    之前会触发编译器警告,需先写
    #[view]
    再写
    ///
    注释

Quick Workflow

快速工作流

  1. Search examples → Use
    search-aptos-examples
    skill to find similar patterns in aptos-core
  2. Create module structure → Define structs, events, constants, init_module
  3. Implement object creation → Use proper constructor pattern with all refs generated upfront
  4. Add access control → Verify ownership and validate all inputs
  5. Security check → Use
    security-audit
    skill before deployment
  1. 搜索示例 → 使用
    search-aptos-examples
    skill在aptos-core中查找相似的实现模式
  2. 搭建模块结构 → 定义结构体、事件、常量、init_module函数
  3. 实现对象创建逻辑 → 遵循规范的构造函数模式,提前生成所有所需引用
  4. 添加访问控制 → 验证所有权,校验所有输入参数
  5. 安全检查 → 部署前使用
    security-audit
    skill进行审计

Key Example: Object Creation Pattern

核心示例:对象创建模式

move
struct MyObject has key {
    name: String,
    transfer_ref: object::TransferRef,
    delete_ref: object::DeleteRef,
}

// Error constants
const E_NOT_OWNER: u64 = 1;
const E_EMPTY_STRING: u64 = 2;
const E_NAME_TOO_LONG: u64 = 3;

// Configuration constants
const MAX_NAME_LENGTH: u64 = 100;

/// Create object with proper pattern
public fun create_my_object(creator: &signer, name: String): Object<MyObject> {
    // 1. Create object
    let constructor_ref = object::create_object(signer::address_of(creator));

    // 2. Generate ALL refs you'll need BEFORE constructor_ref is destroyed
    let transfer_ref = object::generate_transfer_ref(&constructor_ref);
    let delete_ref = object::generate_delete_ref(&constructor_ref);

    // 3. Get object signer
    let object_signer = object::generate_signer(&constructor_ref);

    // 4. Store data in object
    move_to(&object_signer, MyObject {
        name,
        transfer_ref,
        delete_ref,
    });

    // 5. Return typed object reference (ConstructorRef automatically destroyed)
    object::object_from_constructor_ref<MyObject>(&constructor_ref)
}

/// Update with ownership verification
public entry fun update_object(
    owner: &signer,
    obj: Object<MyObject>,
    new_name: String
) acquires MyObject {
    // ✅ ALWAYS: Verify ownership
    assert!(object::owner(obj) == signer::address_of(owner), E_NOT_OWNER);

    // ✅ ALWAYS: Validate inputs
    assert!(string::length(&new_name) > 0, E_EMPTY_STRING);
    assert!(string::length(&new_name) <= MAX_NAME_LENGTH, E_NAME_TOO_LONG);

    // Safe to proceed
    let obj_data = borrow_global_mut<MyObject>(object::object_address(&obj));
    obj_data.name = new_name;
}
move
struct MyObject has key {
    name: String,
    transfer_ref: object::TransferRef,
    delete_ref: object::DeleteRef,
}

// 错误常量
const E_NOT_OWNER: u64 = 1;
const E_EMPTY_STRING: u64 = 2;
const E_NAME_TOO_LONG: u64 = 3;

// 配置常量
const MAX_NAME_LENGTH: u64 = 100;

/// 遵循规范模式创建对象
public fun create_my_object(creator: &signer, name: String): Object<MyObject> {
    // 1. 创建对象
    let constructor_ref = object::create_object(signer::address_of(creator));

    // 2. 在constructor_ref销毁前生成所有需要用到的引用
    let transfer_ref = object::generate_transfer_ref(&constructor_ref);
    let delete_ref = object::generate_delete_ref(&constructor_ref);

    // 3. 获取对象签名者
    let object_signer = object::generate_signer(&constructor_ref);

    // 4. 把数据存储到对象中
    move_to(&object_signer, MyObject {
        name,
        transfer_ref,
        delete_ref,
    });

    // 5. 返回带类型的对象引用(ConstructorRef会自动销毁)
    object::object_from_constructor_ref<MyObject>(&constructor_ref)
}

/// 带所有权验证的更新操作
public entry fun update_object(
    owner: &signer,
    obj: Object<MyObject>,
    new_name: String
) acquires MyObject {
    // ✅ 强制要求:验证所有权
    assert!(object::owner(obj) == signer::address_of(owner), E_NOT_OWNER);

    // ✅ 强制要求:校验输入
    assert!(string::length(&new_name) > 0, E_EMPTY_STRING);
    assert!(string::length(&new_name) <= MAX_NAME_LENGTH, E_NAME_TOO_LONG);

    // 校验通过后执行逻辑
    let obj_data = borrow_global_mut<MyObject>(object::object_address(&obj));
    obj_data.name = new_name;
}

Key Example: Accessor Functions for Testing

核心示例:用于测试的访问器函数

move
struct ListingInfo has store, drop, copy {
    seller: address,
    price: u64,
    listed_at: u64,
}

/// Accessor function - tests cannot access struct fields directly
/// Use tuple returns for multiple fields
#[view]
public fun get_listing_details(nft_addr: address): (address, u64, u64) acquires Listings {
    let listings = borrow_global<Listings>(get_marketplace_address());
    assert!(table::contains(&listings.items, nft_addr), E_NOT_LISTED);
    let listing = table::borrow(&listings.items, nft_addr);
    (listing.seller, listing.price, listing.listed_at)
}

/// Single-field accessor when only one value needed
#[view]
public fun get_staked_amount(user_addr: address): u64 acquires Stakes {
    let stakes = borrow_global<Stakes>(get_vault_address());
    if (table_with_length::contains(&stakes.items, user_addr)) {
        table_with_length::borrow(&stakes.items, user_addr).amount
    } else {
        0
    }
}
move
struct ListingInfo has store, drop, copy {
    seller: address,
    price: u64,
    listed_at: u64,
}

/// 访问器函数——测试无法直接访问结构体字段
/// 多字段返回使用元组格式
#[view]
public fun get_listing_details(nft_addr: address): (address, u64, u64) acquires Listings {
    let listings = borrow_global<Listings>(get_marketplace_address());
    assert!(table::contains(&listings.items, nft_addr), E_NOT_LISTED);
    let listing = table::borrow(&listings.items, nft_addr);
    (listing.seller, listing.price, listing.listed_at)
}

/// 仅需要单个值时使用单字段访问器
#[view]
public fun get_staked_amount(user_addr: address): u64 acquires Stakes {
    let stakes = borrow_global<Stakes>(get_vault_address());
    if (table_with_length::contains(&stakes.items, user_addr)) {
        table_with_length::borrow(&stakes.items, user_addr).amount
    } else {
        0
    }
}

Module Structure Template

模块结构模板

move
module my_addr::my_module {
    // ============ Imports ============
    use std::signer;
    use std::string::String;
    use aptos_framework::object::{Self, Object};
    use aptos_framework::event;

    // ============ Events ============
    #[event]
    struct ItemCreated has drop, store {
        item: address,
        creator: address,
    }

    // ============ Structs ============
    // Define your data structures

    // ============ Constants ============
    const E_NOT_OWNER: u64 = 1;
    const E_UNAUTHORIZED: u64 = 2;

    // ============ Init Module ============
    fun init_module(deployer: &signer) {
        // Initialize global state, registries, etc.
    }

    // ============ Public Entry Functions ============
    // User-facing functions

    // ============ Public Functions ============
    // Composable functions

    // ============ Private Functions ============
    // Internal helpers
}
move
module my_addr::my_module {
    // ============ 导入依赖 ============
    use std::signer;
    use std::string::String;
    use aptos_framework::object::{Self, Object};
    use aptos_framework::event;

    // ============ 事件定义 ============
    #[event]
    struct ItemCreated has drop, store {
        item: address,
        creator: address,
    }

    // ============ 结构体定义 ============
    // 定义你的数据结构

    // ============ 常量定义 ============
    const E_NOT_OWNER: u64 = 1;
    const E_UNAUTHORIZED: u64 = 2;

    // ============ 模块初始化 ============
    fun init_module(deployer: &signer) {
        // 初始化全局状态、注册表等
    }

    // ============ 公共入口函数 ============
    // 面向用户的函数

    // ============ 公共函数 ============
    // 可组合的通用函数

    // ============ 私有函数 ============
    // 内部辅助函数
}

Storage Type Selection

存储类型选择

⚠️ When user mentions storage ("store", "track", "registry", "mapping", "list", "collection"):
⚠️ 当用户提及存储相关内容("store"、"track"、"registry"、"mapping"、"list"、"collection")时:

1. Ask 2-3 Questions (see
references/storage-decision-tree.md
)

1. 询问2-3个问题(参考
references/storage-decision-tree.md

  • Access pattern? (sequential vs key-value vs both)
  • Expected size? (small vs large vs unknown)
  • Need
    .length()
    ?
    (conditional)
  • 访问模式?(顺序访问、键值访问还是两者都有)
  • 预期数据规模?(小、大还是未知)
  • 是否需要
    .length()
    能力?
    (按需提问)

2. Recommend from Patterns (
references/storage-patterns.md
)

2. 从模式库中推荐方案(参考
references/storage-patterns.md

PatternRecommended Storage
User registry
Table<address, UserInfo>
Staking records
Table<address, StakeInfo>
Leaderboard
BigOrderedMap<u64, address>
Transaction log
SmartVector<TxRecord>
or
Vector
Whitelist (<100)
Vector<address>
Voting records
TableWithLength<address, bool>
Config (<50)
OrderedMap<String, Value>
DAO proposals
BigOrderedMap<u64, Proposal>
Asset collection
Vector<Object<T>>
or
SmartVector
场景推荐存储类型
用户注册表
Table<address, UserInfo>
质押记录
Table<address, StakeInfo>
排行榜
BigOrderedMap<u64, address>
交易日志
SmartVector<TxRecord>
Vector
白名单(<100条)
Vector<address>
投票记录
TableWithLength<address, bool>
配置项(<50条)
OrderedMap<String, Value>
DAO提案
BigOrderedMap<u64, Proposal>
资产集合
Vector<Object<T>>
SmartVector

3. Include Brief Gas Context

3. 补充简要的Gas说明

Example recommendations:
  • "For staking, I recommend
    Table<address, StakeInfo>
    because you'll have unbounded users with concurrent operations (separate slots enable parallel access)"
  • "For leaderboard, I recommend
    BigOrderedMap<u64, address>
    because you need sorted iteration (O(log n), use
    allocate_spare_slots
    for production)"
推荐示例:
  • "对于质押场景,我推荐使用
    Table<address, StakeInfo>
    ,因为会有无限多用户并发操作(独立的存储槽支持并行访问)"
  • "对于排行榜场景,我推荐使用
    BigOrderedMap<u64, address>
    ,因为你需要有序遍历能力(时间复杂度O(log n),生产环境使用时请调用
    allocate_spare_slots
    )"

Storage Types Available

可用存储类型

  • Vector - Small sequential (<100 items)
  • SmartVector - Large sequential (100+ items)
  • Table - Unordered key-value lookups
  • TableWithLength - Table with count tracking
  • OrderedMap - Small sorted maps (<100 items)
  • BigOrderedMap - Large sorted maps (100+ items)
⚠️ NEVER use SmartTable (deprecated, use BigOrderedMap)
Details: See
references/
for decision tree, type comparisons, and gas optimization.
  • Vector - 小规模顺序存储(<100条数据)
  • SmartVector - 大规模顺序存储(100条以上数据)
  • Table - 无序键值查询
  • TableWithLength - 支持计数的Table
  • OrderedMap - 小规模有序映射(<100条数据)
  • BigOrderedMap - 大规模有序映射(100条以上数据)
⚠️ 禁止使用SmartTable(已废弃,改用BigOrderedMap)
详情: 决策树、类型对比和Gas优化方案可参考
references/
目录下的文档。

Anti-patterns

反模式

  1. Never use legacy TokenV1 standard or import
    aptos_token::token
  2. Never use resource accounts (use named objects instead)
  3. Never return ConstructorRef from public functions
  4. Never skip signer verification in entry functions
  5. Never skip input validation (amounts, addresses, strings)
  6. Never deploy without 100% test coverage
  7. Never create helper functions that just return named addresses
  8. Never skip event emission for significant activities
  9. Never use old syntax when V2 syntax is available
  10. Never skip init_module for contracts that need initialization
  11. Never hardcode real private keys or secrets in code — use
    @my_addr
    named addresses and
    "0x..."
    placeholders
  12. Never read
    .env
    or
    ~/.aptos/config.yaml
    — these contain private keys
  1. 禁止使用老旧的TokenV1标准或导入
    aptos_token::token
  2. 禁止使用资源账户(改用命名对象)
  3. 公共函数禁止返回ConstructorRef
  4. 入口函数禁止省略签名者验证
  5. 禁止省略输入校验(金额、地址、字符串)
  6. 测试覆盖率未达100%禁止部署
  7. 禁止创建仅返回命名地址的辅助函数
  8. 重要操作禁止省略事件触发
  9. 有V2语法可用时禁止使用旧语法
  10. 需要初始化的合约禁止省略init_module
  11. 禁止在代码中硬编码真实私钥或密钥——使用
    @my_addr
    命名地址和
    "0x..."
    占位符
  12. 禁止读取
    .env
    ~/.aptos/config.yaml
    ——这些文件包含私钥

Edge Cases to Handle

需要处理的边界场景

ScenarioCheckError Code
Zero amounts
assert!(amount > 0, E_ZERO_AMOUNT)
E_ZERO_AMOUNT
Excessive amounts
assert!(amount <= MAX, E_AMOUNT_TOO_HIGH)
E_AMOUNT_TOO_HIGH
Empty vectors
assert!(vector::length(&v) > 0, E_EMPTY_VECTOR)
E_EMPTY_VECTOR
Empty strings
assert!(string::length(&s) > 0, E_EMPTY_STRING)
E_EMPTY_STRING
Strings too long
assert!(string::length(&s) <= MAX, E_STRING_TOO_LONG)
E_STRING_TOO_LONG
Zero address
assert!(addr != @0x0, E_ZERO_ADDRESS)
E_ZERO_ADDRESS
Overflow
assert!(a <= MAX_U64 - b, E_OVERFLOW)
E_OVERFLOW
Underflow
assert!(a >= b, E_UNDERFLOW)
E_UNDERFLOW
Division by zero
assert!(divisor > 0, E_DIVISION_BY_ZERO)
E_DIVISION_BY_ZERO
Unauthorized access
assert!(signer == expected, E_UNAUTHORIZED)
E_UNAUTHORIZED
Not object owner
assert!(object::owner(obj) == user, E_NOT_OWNER)
E_NOT_OWNER
场景校验逻辑错误码
金额为0
assert!(amount > 0, E_ZERO_AMOUNT)
E_ZERO_AMOUNT
金额超出上限
assert!(amount <= MAX, E_AMOUNT_TOO_HIGH)
E_AMOUNT_TOO_HIGH
空向量
assert!(vector::length(&v) > 0, E_EMPTY_VECTOR)
E_EMPTY_VECTOR
空字符串
assert!(string::length(&s) > 0, E_EMPTY_STRING)
E_EMPTY_STRING
字符串过长
assert!(string::length(&s) <= MAX, E_STRING_TOO_LONG)
E_STRING_TOO_LONG
零地址
assert!(addr != @0x0, E_ZERO_ADDRESS)
E_ZERO_ADDRESS
溢出
assert!(a <= MAX_U64 - b, E_OVERFLOW)
E_OVERFLOW
下溢
assert!(a >= b, E_UNDERFLOW)
E_UNDERFLOW
除零错误
assert!(divisor > 0, E_DIVISION_BY_ZERO)
E_DIVISION_BY_ZERO
未授权访问
assert!(signer == expected, E_UNAUTHORIZED)
E_UNAUTHORIZED
非对象所有者操作
assert!(object::owner(obj) == user, E_NOT_OWNER)
E_NOT_OWNER

References

参考资料

Detailed Patterns (references/ folder):
  • references/storage-decision-tree.md
    - ⭐ Storage type selection framework (ask when storage mentioned)
  • references/storage-patterns.md
    - ⭐ Use-case patterns and smart defaults
  • references/storage-types.md
    - Detailed comparison of all 6 storage types
  • references/storage-gas-optimization.md
    - Gas optimization strategies for storage
  • references/object-patterns.md
    - Named objects, collections, nested objects
  • references/access-control.md
    - RBAC and permission systems
  • references/safe-arithmetic.md
    - Overflow/underflow prevention
  • references/initialization.md
    - init_module patterns and registry creation
  • references/events.md
    - Event emission patterns
  • references/v2-syntax.md
    - Modern Move V2 features (method calls, indexing, lambdas)
  • references/complete-example.md
    - Full annotated NFT collection contract
Pattern Documentation (patterns/ folder):
  • ../../../patterns/move/DIGITAL_ASSETS.md
    - Digital Asset (NFT) standard - CRITICAL for NFTs
  • ../../../patterns/move/OBJECTS.md
    - Comprehensive object model guide
  • ../../../patterns/move/SECURITY.md
    - Security checklist and patterns
  • ../../../patterns/move/MOVE_V2_SYNTAX.md
    - Modern syntax examples
Official Documentation:
Related Skills:
  • search-aptos-examples
    - Find similar examples (use BEFORE writing)
  • generate-tests
    - Write tests for contracts (use AFTER writing contracts)
  • security-audit
    - Audit contracts before deployment
详细模式(references/目录):
  • references/storage-decision-tree.md
    - ⭐ 存储类型选择框架(用户提及存储时参考)
  • references/storage-patterns.md
    - ⭐ 场景化模式和默认推荐
  • references/storage-types.md
    - 6种存储类型的详细对比
  • references/storage-gas-optimization.md
    - 存储的Gas优化策略
  • references/object-patterns.md
    - 命名对象、集合、嵌套对象实现
  • references/access-control.md
    - RBAC和权限系统实现
  • references/safe-arithmetic.md
    - 溢出/下溢预防方案
  • references/initialization.md
    - init_module模式和注册表创建
  • references/events.md
    - 事件触发模式
  • references/v2-syntax.md
    - 现代Move V2特性(方法调用、索引、lambda)
  • references/complete-example.md
    - 带完整注释的NFT集合合约示例
模式文档(patterns/目录):
  • ../../../patterns/move/DIGITAL_ASSETS.md
    - Digital Asset(NFT)标准 - NFT开发必读
  • ../../../patterns/move/OBJECTS.md
    - 完整的对象模型指南
  • ../../../patterns/move/SECURITY.md
    - 安全检查清单和模式
  • ../../../patterns/move/MOVE_V2_SYNTAX.md
    - 现代语法示例
官方文档:
关联Skill:
  • search-aptos-examples
    - 查找相似示例(编写代码前使用)
  • generate-tests
    - 为合约编写测试(编写完合约后使用)
  • security-audit
    - 部署前审计合约