aptos_token_objects::collection and aptos_token_objects::token modulesObject for NFT references (NOT generic Object)aptos_token::token module (deprecated)../../../patterns/move/DIGITAL_ASSETS.md for complete NFT patternsObject for all object references (NEVER raw addresses)Object from constructors (NEVER return ConstructorRef)object::owner(obj) == signer::address_of(user)object::generate_signer(&constructor_ref) for object signersobject::create_named_object(creator, seed) assert!(signer::address_of(user) == expected, E_UNAUTHORIZED)
&mut references in public functionsobj.is_owner(user) (define first param as self)vector[index] instead of vector::borrow()@marketplace_addr (NOT helper functions)#[view] annotation for read-only accessor functions(seller, price, timestamp)#[view] BEFORE doc comments - /// comment before #[view] causes compiler warnings. Write #[view] first, then ///
security-audit skill before deploymentstruct 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;
}
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 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
}
⚠️ When user mentions storage ("store", "track", "registry", "mapping", "list", "collection"):
references/storage-decision-tree.md).length()? (conditional)references/storage-patterns.md)| Pattern | Recommended Storage |
|---|---|
| ---------------- | ------------------------------------ |
| User registry | Table |
| Staking records | Table |
| Leaderboard | BigOrderedMap |
| Transaction log | SmartVector or Vector |
| Whitelist (<100) | Vector |
| Voting records | TableWithLength |
| Config (<50) | OrderedMap |
| DAO proposals | BigOrderedMap |
| Asset collection | Vector or SmartVector |
Example recommendations:
Table because you'll have unbounded users with concurrent operations(separate slots enable parallel access)"
BigOrderedMap because you need sorted iteration (O(log n), use allocate_spare_slots for production)"
⚠️ NEVER use SmartTable (deprecated, use BigOrderedMap)
Details: See references/ for decision tree, type comparisons, and gas optimization.
aptos_token::token@my_addr named addresses and "0x..."placeholders
.env or ~/.aptos/config.yaml — these contain private keys| 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 |
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 contractPattern 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 examplesOfficial Documentation:
Related Skills:
search-aptos-examples - Find similar examples in aptos-core (optional)generate-tests - Write tests for contracts (use AFTER writing contracts)security-audit - Audit contracts before deployment共 1 个版本