write-contracts
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWrite Contracts Skill
合约编写Skill
Core Rules
核心规则
Digital Assets (NFTs) ⭐ CRITICAL
数字资产(NFTs)⭐ 至关重要
- ALWAYS use Digital Asset (DA) standard for ALL NFT-related contracts (collections, marketplaces, minting)
- ALWAYS import and
aptos_token_objects::collectionmodulesaptos_token_objects::token - ALWAYS use for NFT references (NOT generic
Object<AptosToken>)Object<T> - NEVER use legacy TokenV1 standard or module (deprecated)
aptos_token::token - See for complete NFT patterns
../../../patterns/move/DIGITAL_ASSETS.md
- 所有NFT相关合约(集合、市场、铸造)必须使用Digital Asset(DA)标准
- 必须导入 和
aptos_token_objects::collection模块aptos_token_objects::token - 必须使用 作为NFT引用(不可使用通用的
Object<AptosToken>)Object<T> - 禁止使用老旧的TokenV1标准或模块(已废弃)
aptos_token::token - 完整的NFT模式可参考
../../../patterns/move/DIGITAL_ASSETS.md
Object Model
对象模型
- ALWAYS use for all object references (NEVER raw addresses)
Object<T> - Generate all refs (TransferRef, DeleteRef) in constructor before ConstructorRef destroyed
- Return from constructors (NEVER return ConstructorRef)
Object<T> - Verify ownership with
object::owner(obj) == signer::address_of(user)
- 所有对象引用必须使用(禁止使用原始地址)
Object<T> - 构造函数中需要在ConstructorRef销毁前生成所有所需的引用(TransferRef、DeleteRef)
- 构造函数需要返回(禁止返回ConstructorRef)
Object<T> - 使用验证所有权
object::owner(obj) == signer::address_of(user)
Security
安全要求
- ALWAYS verify signer authority in entry functions:
assert!(signer::address_of(user) == expected, E_UNAUTHORIZED) - ALWAYS validate inputs: non-zero amounts, address validation, string length checks
- NEVER expose references in public functions
&mut - NEVER skip signer verification in entry functions
- 入口函数必须验证签名者权限:
assert!(signer::address_of(user) == expected, E_UNAUTHORIZED) - 必须做输入校验:非零金额校验、地址合法性校验、字符串长度校验
- 禁止在公共函数中暴露可变引用
&mut - 入口函数禁止省略签名者验证步骤
Modern Syntax
现代语法规范
- Use inline functions and lambdas for iteration
- Use receiver-style method calls: (define first param as
obj.is_owner(user))self - Use vector indexing: instead of
vector[index]vector::borrow() - Use direct named addresses: (NOT helper functions)
@marketplace_addr
- 迭代操作优先使用内联函数和lambda表达式
- 使用接收者风格的方法调用:(将第一个参数定义为
obj.is_owner(user))self - 使用向量索引写法:替代
vector[index]vector::borrow() - 使用直接命名地址:(不要使用辅助函数获取)
@marketplace_addr
Required Patterns
必要模式
- Use init_module for contract initialization on deployment
- Emit events for ALL significant activities (create, transfer, update, delete)
- Define clear error constants with descriptive names (E_NOT_OWNER, E_INSUFFICIENT_BALANCE)
- 合约部署时的初始化逻辑使用实现
init_module - 所有重要操作都需要触发事件(创建、转移、更新、删除)
- 定义语义清晰的错误常量(如E_NOT_OWNER、E_INSUFFICIENT_BALANCE)
Testability
可测试性要求
- Add accessor functions for struct fields - tests in separate modules cannot access struct fields directly
- Use annotation for read-only accessor functions
#[view] - Return tuples from accessors for multi-field access:
(seller, price, timestamp) - Place BEFORE doc comments -
#[view]before/// commentcauses compiler warnings. Write#[view]first, then#[view]///
- 为结构体字段添加访问器函数——独立模块中的测试无法直接访问结构体字段
- 只读访问器函数添加注解
#[view] - 多字段访问的访问器返回元组:
(seller, price, timestamp) - 注解要写在文档注释之前——如果
#[view]注释写在///之前会触发编译器警告,需先写#[view]再写#[view]注释///
Quick Workflow
快速工作流
- Search examples → Use skill to find similar patterns in aptos-core
search-aptos-examples - Create module structure → Define structs, events, constants, init_module
- Implement object creation → Use proper constructor pattern with all refs generated upfront
- Add access control → Verify ownership and validate all inputs
- Security check → Use skill before deployment
security-audit
- 搜索示例 → 使用skill在aptos-core中查找相似的实现模式
search-aptos-examples - 搭建模块结构 → 定义结构体、事件、常量、init_module函数
- 实现对象创建逻辑 → 遵循规范的构造函数模式,提前生成所有所需引用
- 添加访问控制 → 验证所有权,校验所有输入参数
- 安全检查 → 部署前使用skill进行审计
security-audit
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
)
references/storage-decision-tree.md1. 询问2-3个问题(参考references/storage-decision-tree.md
)
references/storage-decision-tree.md- Access pattern? (sequential vs key-value vs both)
- Expected size? (small vs large vs unknown)
- Need ? (conditional)
.length()
- 访问模式?(顺序访问、键值访问还是两者都有)
- 预期数据规模?(小、大还是未知)
- 是否需要能力?(按需提问)
.length()
2. Recommend from Patterns (references/storage-patterns.md
)
references/storage-patterns.md2. 从模式库中推荐方案(参考references/storage-patterns.md
)
references/storage-patterns.md| Pattern | Recommended Storage |
|---|---|
| User registry | |
| Staking records | |
| Leaderboard | |
| Transaction log | |
| Whitelist (<100) | |
| Voting records | |
| Config (<50) | |
| DAO proposals | |
| Asset collection | |
| 场景 | 推荐存储类型 |
|---|---|
| 用户注册表 | |
| 质押记录 | |
| 排行榜 | |
| 交易日志 | |
| 白名单(<100条) | |
| 投票记录 | |
| 配置项(<50条) | |
| DAO提案 | |
| 资产集合 | |
3. Include Brief Gas Context
3. 补充简要的Gas说明
Example recommendations:
- "For staking, I recommend because you'll have unbounded users with concurrent operations (separate slots enable parallel access)"
Table<address, StakeInfo> - "For leaderboard, I recommend because you need sorted iteration (O(log n), use
BigOrderedMap<u64, address>for production)"allocate_spare_slots
推荐示例:
- "对于质押场景,我推荐使用,因为会有无限多用户并发操作(独立的存储槽支持并行访问)"
Table<address, StakeInfo> - "对于排行榜场景,我推荐使用,因为你需要有序遍历能力(时间复杂度O(log n),生产环境使用时请调用
BigOrderedMap<u64, address>)"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 for decision tree, type comparisons, and gas optimization.
references/- Vector - 小规模顺序存储(<100条数据)
- SmartVector - 大规模顺序存储(100条以上数据)
- Table - 无序键值查询
- TableWithLength - 支持计数的Table
- OrderedMap - 小规模有序映射(<100条数据)
- BigOrderedMap - 大规模有序映射(100条以上数据)
⚠️ 禁止使用SmartTable(已废弃,改用BigOrderedMap)
详情: 决策树、类型对比和Gas优化方案可参考目录下的文档。
references/Anti-patterns
反模式
- ❌ Never use legacy TokenV1 standard or import
aptos_token::token - ❌ Never use resource accounts (use named objects instead)
- ❌ Never return ConstructorRef from public functions
- ❌ Never skip signer verification in entry functions
- ❌ Never skip input validation (amounts, addresses, strings)
- ❌ Never deploy without 100% test coverage
- ❌ Never create helper functions that just return named addresses
- ❌ Never skip event emission for significant activities
- ❌ Never use old syntax when V2 syntax is available
- ❌ Never skip init_module for contracts that need initialization
- ❌ Never hardcode real private keys or secrets in code — use named addresses and
@my_addrplaceholders"0x..." - ❌ Never read or
.env— these contain private keys~/.aptos/config.yaml
- ❌ 禁止使用老旧的TokenV1标准或导入
aptos_token::token - ❌ 禁止使用资源账户(改用命名对象)
- ❌ 公共函数禁止返回ConstructorRef
- ❌ 入口函数禁止省略签名者验证
- ❌ 禁止省略输入校验(金额、地址、字符串)
- ❌ 测试覆盖率未达100%禁止部署
- ❌ 禁止创建仅返回命名地址的辅助函数
- ❌ 重要操作禁止省略事件触发
- ❌ 有V2语法可用时禁止使用旧语法
- ❌ 需要初始化的合约禁止省略init_module
- ❌ 禁止在代码中硬编码真实私钥或密钥——使用命名地址和
@my_addr占位符"0x..." - ❌ 禁止读取或
.env——这些文件包含私钥~/.aptos/config.yaml
Edge Cases to Handle
需要处理的边界场景
| Scenario | Check | Error Code |
|---|---|---|
| Zero amounts | | E_ZERO_AMOUNT |
| Excessive amounts | | E_AMOUNT_TOO_HIGH |
| Empty vectors | | E_EMPTY_VECTOR |
| Empty strings | | E_EMPTY_STRING |
| Strings too long | | E_STRING_TOO_LONG |
| Zero address | | E_ZERO_ADDRESS |
| Overflow | | E_OVERFLOW |
| Underflow | | E_UNDERFLOW |
| Division by zero | | E_DIVISION_BY_ZERO |
| Unauthorized access | | E_UNAUTHORIZED |
| Not object owner | | E_NOT_OWNER |
| 场景 | 校验逻辑 | 错误码 |
|---|---|---|
| 金额为0 | | E_ZERO_AMOUNT |
| 金额超出上限 | | E_AMOUNT_TOO_HIGH |
| 空向量 | | E_EMPTY_VECTOR |
| 空字符串 | | E_EMPTY_STRING |
| 字符串过长 | | E_STRING_TOO_LONG |
| 零地址 | | E_ZERO_ADDRESS |
| 溢出 | | E_OVERFLOW |
| 下溢 | | E_UNDERFLOW |
| 除零错误 | | E_DIVISION_BY_ZERO |
| 未授权访问 | | E_UNAUTHORIZED |
| 非对象所有者操作 | | E_NOT_OWNER |
References
参考资料
Detailed Patterns (references/ folder):
- - ⭐ Storage type selection framework (ask when storage mentioned)
references/storage-decision-tree.md - - ⭐ Use-case patterns and smart defaults
references/storage-patterns.md - - Detailed comparison of all 6 storage types
references/storage-types.md - - Gas optimization strategies for storage
references/storage-gas-optimization.md - - Named objects, collections, nested objects
references/object-patterns.md - - RBAC and permission systems
references/access-control.md - - Overflow/underflow prevention
references/safe-arithmetic.md - - init_module patterns and registry creation
references/initialization.md - - Event emission patterns
references/events.md - - Modern Move V2 features (method calls, indexing, lambdas)
references/v2-syntax.md - - Full annotated NFT collection contract
references/complete-example.md
Pattern Documentation (patterns/ folder):
- - Digital Asset (NFT) standard - CRITICAL for NFTs
../../../patterns/move/DIGITAL_ASSETS.md - - Comprehensive object model guide
../../../patterns/move/OBJECTS.md - - Security checklist and patterns
../../../patterns/move/SECURITY.md - - Modern syntax examples
../../../patterns/move/MOVE_V2_SYNTAX.md
Official Documentation:
- Digital Asset Standard: https://aptos.dev/build/smart-contracts/digital-asset
- Object Model: https://aptos.dev/build/smart-contracts/object
- Security Guidelines: https://aptos.dev/build/smart-contracts/move-security-guidelines
Related Skills:
- - Find similar examples (use BEFORE writing)
search-aptos-examples - - Write tests for contracts (use AFTER writing contracts)
generate-tests - - Audit contracts before deployment
security-audit
详细模式(references/目录):
- - ⭐ 存储类型选择框架(用户提及存储时参考)
references/storage-decision-tree.md - - ⭐ 场景化模式和默认推荐
references/storage-patterns.md - - 6种存储类型的详细对比
references/storage-types.md - - 存储的Gas优化策略
references/storage-gas-optimization.md - - 命名对象、集合、嵌套对象实现
references/object-patterns.md - - RBAC和权限系统实现
references/access-control.md - - 溢出/下溢预防方案
references/safe-arithmetic.md - - init_module模式和注册表创建
references/initialization.md - - 事件触发模式
references/events.md - - 现代Move V2特性(方法调用、索引、lambda)
references/v2-syntax.md - - 带完整注释的NFT集合合约示例
references/complete-example.md
模式文档(patterns/目录):
- - Digital Asset(NFT)标准 - NFT开发必读
../../../patterns/move/DIGITAL_ASSETS.md - - 完整的对象模型指南
../../../patterns/move/OBJECTS.md - - 安全检查清单和模式
../../../patterns/move/SECURITY.md - - 现代语法示例
../../../patterns/move/MOVE_V2_SYNTAX.md
官方文档:
- Digital Asset标准:https://aptos.dev/build/smart-contracts/digital-asset
- 对象模型:https://aptos.dev/build/smart-contracts/object
- 安全指南:https://aptos.dev/build/smart-contracts/move-security-guidelines
关联Skill:
- - 查找相似示例(编写代码前使用)
search-aptos-examples - - 为合约编写测试(编写完合约后使用)
generate-tests - - 部署前审计合约
security-audit