aptos_token_objects::collection and aptos_token_objects::token modulesObject<AptosToken> for NFT references (NOT generic Object<T>)aptos_token::token module (deprecated)../../../patterns/move/DIGITAL_ASSETS.md for complete NFT patternsObject<T> for all object references (NEVER raw addresses)Object<T> 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 deploymentmove
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 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
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)#PatternRecommended StorageUser registryTable<address, UserInfo>Staking recordsTable<address, StakeInfo>LeaderboardBigOrderedMap<u64, address>Transaction logSmartVector<TxRecord> or VectorWhitelist (<100)Vector<address>Voting recordsTableWithLength<address, bool>Config (<50)OrderedMap<String, Value>DAO proposalsBigOrderedMap<u64, Proposal>Asset collectionVector<Object<T>> or SmartVector
Example recommendations:
Table<address, StakeInfo> because you'll have unbounded users with concurrent operations
(separate slots enable parallel access)"BigOrderedMap<u64, address> 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 keysScenarioCheckError CodeZero amountsassert!(amount > 0, E_ZERO_AMOUNT)E_ZERO_AMOUNTExcessive amountsassert!(amount <= MAX, E_AMOUNT_TOO_HIGH)E_AMOUNT_TOO_HIGHEmpty vectorsassert!(vector::length(&v) > 0, E_EMPTY_VECTOR)E_EMPTY_VECTOREmpty stringsassert!(string::length(&s) > 0, E_EMPTY_STRING)E_EMPTY_STRINGStrings too longassert!(string::length(&s) <= MAX, E_STRING_TOO_LONG)E_STRING_TOO_LONGZero addressassert!(addr != @0x0, E_ZERO_ADDRESS)E_ZERO_ADDRESSOverflowassert!(a <= MAX_U64 - b, E_OVERFLOW)E_OVERFLOWUnderflowassert!(a >= b, E_UNDERFLOW)E_UNDERFLOWDivision by zeroassert!(divisor > 0, E_DIVISION_BY_ZERO)E_DIVISION_BY_ZEROUnauthorized accessassert!(signer == expected, E_UNAUTHORIZED)E_UNAUTHORIZEDNot object ownerassert!(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