write-contracts
0
总安装量
5
周安装量
安装命令
npx skills add https://github.com/iskysun96/aptos-agent-skills --skill write-contracts
Agent 安装分布
amp
4
opencode
4
kimi-cli
4
codex
4
github-copilot
4
gemini-cli
4
Skill 文档
Write Contracts Skill
Core Rules
Digital Assets (NFTs) â CRITICAL
- ALWAYS use Digital Asset (DA) standard for ALL NFT-related contracts (collections, marketplaces, minting)
- ALWAYS import
aptos_token_objects::collectionandaptos_token_objects::tokenmodules - ALWAYS use
Object<AptosToken>for NFT references (NOT genericObject<T>) - NEVER use legacy TokenV1 standard or
aptos_token::tokenmodule (deprecated) - See
../../../patterns/move/DIGITAL_ASSETS.mdfor complete NFT patterns
Object Model
- ALWAYS use
Object<T>for all object references (NEVER raw addresses) - Generate all refs (TransferRef, DeleteRef) in constructor before ConstructorRef destroyed
- Return
Object<T>from constructors (NEVER return ConstructorRef) - Verify ownership with
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
&mutreferences in public functions - NEVER skip signer verification in entry functions
Modern Syntax
- Use inline functions and lambdas for iteration
- Use receiver-style method calls:
obj.is_owner(user)(define first param asself) - Use vector indexing:
vector[index]instead ofvector::borrow() - Use direct named addresses:
@marketplace_addr(NOT helper functions)
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)
Testability
- Add accessor functions for struct fields – tests in separate modules cannot access struct fields directly
- Use
#[view]annotation for read-only accessor functions - Return tuples from accessors for multi-field access:
(seller, price, timestamp) - Place
#[view]BEFORE doc comments –/// commentbefore#[view]causes compiler warnings. Write#[view]first, then///
Quick Workflow
- Search examples â Use
search-aptos-examplesskill to find similar patterns in aptos-core - 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
security-auditskill before deployment
Key Example: Object Creation Pattern
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;
}
Key Example: Accessor Functions for Testing
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
}
}
Module Structure Template
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
}
Storage Type Selection
â ï¸ When user mentions storage (“store”, “track”, “registry”, “mapping”, “list”, “collection”):
1. Ask 2-3 Questions (see references/storage-decision-tree.md)
- Access pattern? (sequential vs key-value vs both)
- Expected size? (small vs large vs unknown)
- Need
.length()? (conditional)
2. Recommend from Patterns (references/storage-patterns.md)
| Pattern | Recommended 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 |
3. Include Brief Gas Context
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), useallocate_spare_slotsfor production)”
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.
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
@my_addrnamed addresses and"0x..."placeholders - â Never read
.envor~/.aptos/config.yamlâ these contain private keys
Edge Cases to Handle
| Scenario | Check | Error 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 |
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 defaultsreferences/storage-types.md– Detailed comparison of all 6 storage typesreferences/storage-gas-optimization.md– Gas optimization strategies for storagereferences/object-patterns.md– Named objects, collections, nested objectsreferences/access-control.md– RBAC and permission systemsreferences/safe-arithmetic.md– Overflow/underflow preventionreferences/initialization.md– init_module patterns and registry creationreferences/events.md– Event emission patternsreferences/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:
- 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:
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