Claude-skill-registry aptos-framework
Expert on Aptos Framework (0x1 standard library) - account, coin, fungible_asset, object, timestamp, table, event, vector, string, option, error, and other core modules. Triggers on keywords aptos framework, 0x1, account module, table, smarttable, event, timestamp, randomness, aggregator, resource account
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/framework" ~/.claude/skills/majiayu000-claude-skill-registry-aptos-framework && rm -rf "$T"
manifest:
skills/data/framework/SKILL.mdsource content
Aptos Framework Expert
Purpose
Provide comprehensive guidance on the Aptos Framework (0x1 address) - the standard library of core modules that power Aptos blockchain. These modules provide fundamental functionality for accounts, storage, events, randomness, and more.
When to Use
Auto-invoke when users mention:
- Framework Modules - 0x1::, aptos_framework::, standard library
- Account Management - account creation, auth keys, rotation
- Storage - Table, SimpleMap, SmartTable, efficient data structures
- Events - event emission, event handles, indexing
- Randomness - VRF, secure random numbers
- Time - timestamp, block time access
- Resources - resource accounts, deterministic addresses
- Aggregator - parallel execution primitives
Framework Architecture
Core Framework Modules (0x1::)
aptos_framework/ ├── account.move - Account management ├── aptos_account.move - High-level account operations ├── aptos_coin.move - Native APT token ├── aptos_governance.move - On-chain governance ├── coin.move - Fungible token standard (v1) ├── fungible_asset.move - Fungible asset standard (v2) ├── object.move - Object model primitives ├── timestamp.move - Block timestamp access ├── table.move - Key-value storage ├── smart_table.move - Auto-split table ├── event.move - Event emission ├── randomness.move - Secure randomness ├── aggregator.move - Parallel execution ├── aggregator_v2.move - Improved aggregator ├── resource_account.move - Deterministic deployment ├── transaction_fee.move - Fee collection └── staking_contract.move - Validator staking
Standard Library (std::)
move-stdlib/ ├── vector.move - Dynamic arrays ├── option.move - Optional values ├── string.move - UTF8 strings ├── signer.move - Signer operations ├── error.move - Error codes ├── bcs.move - Binary serialization ├── hash.move - Cryptographic hashing └── fixed_point64.move - Fixed-point math
account.move - Account Management
Core Functions
use aptos_framework::account; // Create new account at address public fun create_account(new_address: address) { account::create_account(new_address); } // Get account's sequence number public fun get_sequence_number(addr: address): u64 { account::get_sequence_number(addr) } // Get authentication key public fun get_authentication_key(addr: address): vector<u8> { account::get_authentication_key(addr) } // Check if account exists public fun exists_at(addr: address): bool { account::exists_at(addr) }
Account Rotation
// Rotate authentication key public entry fun rotate_authentication_key( account: &signer, new_auth_key: vector<u8> ) { account::rotate_authentication_key(account, new_auth_key); } // Offer rotation capability to another address public entry fun offer_rotation_capability( account: &signer, rotation_capability_offerer: address ) { account::offer_rotation_capability( account, rotation_capability_offerer, vector::empty() ); }
SignerCapability Pattern
use aptos_framework::account::{Self, SignerCapability}; struct ModuleData has key { signer_cap: SignerCapability } public fun initialize(deployer: &signer) { let (resource_signer, signer_cap) = account::create_resource_account( deployer, b"SEED" ); move_to(&resource_signer, ModuleData { signer_cap }); } public fun use_resource_account() acquires ModuleData { let module_data = borrow_global<ModuleData>(@my_module); let resource_signer = account::create_signer_with_capability(&module_data.signer_cap); // Use resource_signer for operations }
table.move - Scalable Key-Value Storage
Basic Table Operations
use aptos_framework::table::{Self, Table}; struct Registry has key { data: Table<address, UserData> } public fun initialize(account: &signer) { move_to(account, Registry { data: table::new() }); } public fun add_user( registry_addr: address, user_addr: address, user_data: UserData ) acquires Registry { let registry = borrow_global_mut<Registry>(registry_addr); table::add(&mut registry.data, user_addr, user_data); } public fun get_user( registry_addr: address, user_addr: address ): &UserData acquires Registry { let registry = borrow_global<Registry>(registry_addr); table::borrow(®istry.data, user_addr) } public fun update_user( registry_addr: address, user_addr: address ): &mut UserData acquires Registry { let registry = borrow_global_mut<Registry>(registry_addr); table::borrow_mut(&mut registry.data, user_addr) } public fun remove_user( registry_addr: address, user_addr: address ): UserData acquires Registry { let registry = borrow_global_mut<Registry>(registry_addr); table::remove(&mut registry.data, user_addr) } public fun has_user( registry_addr: address, user_addr: address ): bool acquires Registry { let registry = borrow_global<Registry>(registry_addr); table::contains(®istry.data, user_addr) }
Table vs SimpleMap vs SmartTable
| Feature | Vector | SimpleMap | Table | SmartTable |
|---|---|---|---|---|
| Max size | ~1000 | ~1000 | Unlimited | Unlimited |
| Gas cost (read) | O(n) | O(n) | O(1) | O(1) |
| Gas cost (write) | O(n) | O(n) | O(1) | O(1) |
| Storage | On-chain | On-chain | Global storage | Global + auto-split |
| Iteration | ✅ Easy | ✅ Easy | ❌ Not supported | ⚠️ Complex |
| Best for | Small lists | Small maps | Large maps | Very large maps |
SmartTable (Auto-Splitting)
use aptos_framework::smart_table::{Self, SmartTable}; struct LargeRegistry has key { data: SmartTable<address, UserData> } public fun initialize(account: &signer) { move_to(account, LargeRegistry { data: smart_table::new() }); } // Same API as Table public fun add_user(addr: address, data: UserData) acquires LargeRegistry { let registry = borrow_global_mut<LargeRegistry>(@my_module); smart_table::add(&mut registry.data, addr, data); } // SmartTable automatically splits when buckets get large // Better for very large datasets (100k+ entries)
event.move - Event Emission
Event Handles (V1)
use aptos_framework::event::{Self, EventHandle}; struct TransferEvent has drop, store { from: address, to: address, amount: u64, } struct Events has key { transfer_events: EventHandle<TransferEvent> } public fun initialize(account: &signer) { move_to(account, Events { transfer_events: account::new_event_handle<TransferEvent>(account) }); } public fun emit_transfer( from: address, to: address, amount: u64 ) acquires Events { let events = borrow_global_mut<Events>(@my_module); event::emit_event(&mut events.transfer_events, TransferEvent { from, to, amount, }); }
Event API (V2 - Recommended)
use aptos_framework::event; #[event] struct TransferEvent has drop, store { from: address, to: address, amount: u64, } public fun transfer(from: address, to: address, amount: u64) { // Direct emission (no EventHandle needed!) event::emit(TransferEvent { from, to, amount }); }
Event V2 Advantages:
- No EventHandle management
- Cleaner code
- Better indexing support
- Automatic event routing
timestamp.move - Block Time
use aptos_framework::timestamp; public fun get_current_time(): u64 { timestamp::now_seconds() } public fun get_current_time_microseconds(): u64 { timestamp::now_microseconds() } // Time-based logic public fun is_expired(deadline: u64): bool { timestamp::now_seconds() >= deadline } public fun create_with_deadline(duration: u64): u64 { timestamp::now_seconds() + duration }
Important: Block timestamp is set by validators, can have small drift.
randomness.move - Secure Randomness
VRF-based Random Numbers
use aptos_framework::randomness; #[randomness] public entry fun random_mint(user: &signer) { let random_value = randomness::u64_integer(); let rarity = if (random_value % 100 < 1) { // 1% chance - legendary 3 } else if (random_value % 100 < 10) { // 9% chance - rare 2 } else { // 90% chance - common 1 }; mint_nft(user, rarity); } // Random in range #[randomness] public entry fun random_reward(user: &signer) { let amount = randomness::u64_range(100, 1000); // 100 to 999 transfer_reward(user, amount); }
Requirements:
- Must use
attribute#[randomness] - Must be entry function
- Only works on-chain (not in view functions)
Random Bytes
#[randomness] public entry fun random_selection() { let random_bytes = randomness::bytes(32); // 32 random bytes // Use for cryptographic purposes }
resource_account.move - Deterministic Deployment
Creating Resource Accounts
use aptos_framework::resource_account; use aptos_framework::account; public fun create_resource_acct(deployer: &signer) { let seed = b"MY_RESOURCE"; // Create resource account let (resource_signer, signer_cap) = account::create_resource_account( deployer, seed ); // Resource account address is deterministic: // hash(deployer_address, seed) let resource_addr = signer::address_of(&resource_signer); // Store signer capability to use later move_to(&resource_signer, ResourceData { signer_cap }); }
Use Cases for Resource Accounts
- Module Storage - Store module data at predictable address
- Liquidity Pools - Each pool at deterministic address
- Protocol Treasuries - Controlled programmatically
- Registry Systems - Well-known addresses
// Example: Liquidity Pool at deterministic address public fun create_pool<X, Y>(deployer: &signer) { let seed = b"POOL_"; vector::append(&mut seed, type_name<X>()); vector::append(&mut seed, b"_"); vector::append(&mut seed, type_name<Y>()); let (pool_signer, signer_cap) = account::create_resource_account( deployer, seed ); move_to(&pool_signer, Pool<X, Y> { reserve_x: 0, reserve_y: 0, signer_cap, }); }
aggregator_v2.move - Parallel Execution
Aggregators for Concurrent Modification
use aptos_framework::aggregator_v2::{Self, Aggregator}; struct Stats has key { total_users: Aggregator<u64>, total_volume: Aggregator<u64>, } public fun initialize(account: &signer) { move_to(account, Stats { total_users: aggregator_v2::create_aggregator(0), total_volume: aggregator_v2::create_aggregator(0), }); } public fun increment_users() acquires Stats { let stats = borrow_global_mut<Stats>(@my_module); aggregator_v2::add(&mut stats.total_users, 1); } public fun add_volume(amount: u64) acquires Stats { let stats = borrow_global_mut<Stats>(@my_module); aggregator_v2::add(&mut stats.total_volume, amount); } public fun get_total_users(): u64 acquires Stats { let stats = borrow_global<Stats>(@my_module); aggregator_v2::read(&stats.total_users) }
Why Use Aggregators:
- Enable parallel transaction execution
- Multiple transactions can increment same aggregator concurrently
- No conflicts/retries like regular u64 fields
- Critical for high-throughput protocols
option.move - Optional Values
use std::option::{Self, Option}; struct Profile has key { name: String, bio: Option<String>, // Optional field } public fun create_profile(account: &signer, name: String) { move_to(account, Profile { name, bio: option::none() // No bio initially }); } public fun set_bio(account: &signer, bio: String) acquires Profile { let addr = signer::address_of(account); let profile = borrow_global_mut<Profile>(addr); if (option::is_some(&profile.bio)) { // Update existing bio *option::borrow_mut(&mut profile.bio) = bio; } else { // Set bio for first time option::fill(&mut profile.bio, bio); } } public fun get_bio(addr: address): Option<String> acquires Profile { let profile = borrow_global<Profile>(addr); option::clone(&profile.bio) } // Using option value public fun print_bio(addr: address) acquires Profile { let bio_opt = get_bio(addr); if (option::is_some(&bio_opt)) { let bio = option::extract(&mut bio_opt); // Use bio } else { // No bio set } }
string.move - UTF8 Strings
use std::string::{Self, String}; public fun create_message(): String { string::utf8(b"Hello, Aptos!") } public fun concatenate(s1: String, s2: String): String { let mut result = s1; string::append(&mut result, s2); result } public fun substring(s: &String, start: u64, end: u64): String { string::sub_string(s, start, end) } public fun string_length(s: &String): u64 { string::length(s) } // String to bytes public fun to_bytes(s: &String): vector<u8> { *string::bytes(s) }
vector.move - Dynamic Arrays
use std::vector; public fun vector_operations() { let mut v = vector::empty<u64>(); // Add elements vector::push_back(&mut v, 10); vector::push_back(&mut v, 20); vector::push_back(&mut v, 30); // Get length let len = vector::length(&v); // 3 // Access elements let first = *vector::borrow(&v, 0); // 10 // Modify elements let second = vector::borrow_mut(&mut v, 1); *second = 25; // Remove element let last = vector::pop_back(&mut v); // 30 // Check if contains let has_ten = vector::contains(&v, &10); // true // Find index let (found, index) = vector::index_of(&v, &25); // Reverse vector::reverse(&mut v); // Append another vector vector::append(&mut v, vector[40, 50]); // Remove and return element let removed = vector::remove(&mut v, 0); // Swap elements vector::swap(&mut v, 0, 1); }
Common Patterns
Pattern 1: Registry with Table
use aptos_framework::table::{Self, Table}; struct Registry<K: copy + drop, V: store> has key { data: Table<K, V>, count: u64, } public fun initialize<K: copy + drop, V: store>(account: &signer) { move_to(account, Registry<K, V> { data: table::new(), count: 0, }); } public fun register<K: copy + drop, V: store>( registry_addr: address, key: K, value: V ) acquires Registry { let registry = borrow_global_mut<Registry<K, V>>(registry_addr); assert!(!table::contains(®istry.data, key), ERROR_ALREADY_EXISTS); table::add(&mut registry.data, key, value); registry.count = registry.count + 1; }
Pattern 2: Event-Driven State Changes
#[event] struct StateChanged has drop, store { old_state: u8, new_state: u8, timestamp: u64, } public fun change_state(new_state: u8) acquires State { let state = borrow_global_mut<State>(@my_module); let old = state.value; state.value = new_state; event::emit(StateChanged { old_state: old, new_state, timestamp: timestamp::now_seconds(), }); }
Pattern 3: Time-Locked Operations
struct TimeLock has key { unlock_time: u64, amount: u64, } public fun create_timelock( account: &signer, amount: u64, lock_duration: u64 ) { let unlock_time = timestamp::now_seconds() + lock_duration; move_to(account, TimeLock { unlock_time, amount, }); } public fun withdraw(account: &signer) acquires TimeLock { let addr = signer::address_of(account); let timelock = move_from<TimeLock>(addr); assert!( timestamp::now_seconds() >= timelock.unlock_time, ERROR_STILL_LOCKED ); let TimeLock { unlock_time: _, amount } = timelock; // Transfer amount to user }
Pattern 4: Resource Account Pool
struct Pool<phantom X, phantom Y> has key { reserve_x: u64, reserve_y: u64, signer_cap: SignerCapability, } public fun create_pool<X, Y>(creator: &signer) { let seed = b"POOL"; let (pool_signer, signer_cap) = account::create_resource_account( creator, seed ); coin::register<X>(&pool_signer); coin::register<Y>(&pool_signer); move_to(&pool_signer, Pool<X, Y> { reserve_x: 0, reserve_y: 0, signer_cap, }); } public fun swap<X, Y>(amount_in: u64): u64 acquires Pool { let pool_addr = account::create_resource_address(&@my_module, b"POOL"); let pool = borrow_global_mut<Pool<X, Y>>(pool_addr); // Swap logic using signer_cap for transfers let pool_signer = account::create_signer_with_capability(&pool.signer_cap); // ... }
Framework Module Reference
Quick Reference Table
| Module | Key Functions | Use Case |
|---|---|---|
| account | create_account, rotate_authentication_key | Account management |
| coin | transfer, balance, register | Fungible tokens |
| fungible_asset | mint, burn, transfer | Advanced tokens |
| object | create_object, transfer | Object model |
| table | add, borrow, remove | Large key-value stores |
| smart_table | add, borrow, remove | Very large stores |
| event | emit, emit_event | Event emission |
| timestamp | now_seconds | Time access |
| randomness | u64_integer, bytes | Secure randomness |
| aggregator_v2 | create, add, read | Parallel execution |
| resource_account | create_resource_account | Deterministic addresses |
Best Practices
✅ Do
- Use SmartTable for large datasets - Better than Table for 100k+ entries
- Use Event V2 API - Simpler than EventHandle
- Use Aggregator for counters - Enables parallel execution
- Use resource accounts for protocols - Deterministic addresses
- Check timestamp carefully - Validator-set, can have drift
- Use randomness for fair selection - VRF-based security
❌ Avoid
- Don't iterate over Tables - Not supported, use vector/map if needed
- Don't trust timestamp for exact timing - Block-level granularity
- Don't use randomness in view functions - Not supported
- Don't forget to handle Option::none - Check before unwrapping
- Don't create too many event handles - Use Event V2 instead
Response Style
- Module-focused - Reference specific framework modules
- Pattern-driven - Show common framework usage patterns
- Performance-aware - Mention gas implications
- Practical - Real-world examples with framework modules
- Reference docs - Link to specific module documentation
Follow-up Suggestions
After helping with framework modules, suggest:
- Gas optimization for storage structures
- Event indexing strategies
- Parallel execution with aggregators
- Resource account architectures
- Time-based protocol designs
- Random number generation patterns