Resource Accounts
Resource accounts are special autonomous accounts on Aptos that have no private key and can only be controlled by smart contracts. They enable developers to create stable, deterministic addresses for protocols while maintaining programmatic control over account operations, making them essential for DeFi protocols, DAOs, and autonomous systems.
Overview#
Resource accounts solve the problem of protocol-controlled accounts that need stable addresses but shouldn't have private keys that could be lost or compromised. They are created with deterministic addresses derived from a source account and seed, allowing protocols to own assets, publish modules, and execute operations entirely through smart contract logic.
Creating Resource Accounts#
module 0x1::protocol {
use std::signer;
use aptos_framework::resource_account;
use aptos_framework::account;
struct ResourceAccountCap has key {
cap: account::SignerCapability
}
// Create a resource account during protocol initialization
public entry fun initialize(deployer: &signer, seed: vector<u8>) {
// Create resource account with deterministic address
let (resource_signer, signer_cap) = account::create_resource_account(
deployer,
seed
);
// Store capability to use resource account later
move_to(deployer, ResourceAccountCap { cap: signer_cap });
// Resource account can now hold assets, publish modules
// Address is deterministic: derived from deployer + seed
}
// Use resource account for protocol operations
public entry fun protocol_transfer(
amount: u64,
recipient: address
) acquires ResourceAccountCap {
let cap = borrow_global<ResourceAccountCap>(@deployer);
let resource_signer = account::create_signer_with_capability(&cap.cap);
// Resource account executes transfer
coin::transfer<AptosCoin>(&resource_signer, recipient, amount);
}
}
Deterministic Addresses#
Resource account addresses are deterministically computed:
// Address formula: hash(source_address, seed, 0xFF)
let resource_addr = account::create_resource_address(&source_address, seed);
This enables:
- Predictable protocol addresses before deployment
- Consistent addresses across different networks
- Easy verification of protocol authenticity
Real-World Use Cases#
-
DeFi Protocols: Create liquidity pools, vaults, and treasury accounts that are controlled by protocol logic rather than private keys, eliminating single points of failure.
-
DAO Treasuries: Establish autonomous treasuries where funds can only be moved through governance proposals and smart contract execution.
-
Escrow Services: Build trustless escrow systems where locked assets are held by resource accounts with programmatic release conditions.
-
Protocol Upgrades: Deploy protocol modules to resource accounts, enabling controlled upgrade paths through governance rather than admin keys.
-
Cross-Chain Bridges: Operate bridge accounts that custody locked assets with release controlled by multi-sig validation logic.
-
Automated Market Makers: Run AMM contracts where liquidity provider funds are held in resource accounts managed by protocol mathematics.
Best Practices#
Secure Signer Capabilities: Store SignerCapability in a protected resource with appropriate access controls. Never expose it directly.
Use Meaningful Seeds: Choose descriptive seeds that indicate the resource account's purpose (e.g., b"liquidity_pool_v2").
Implement Access Control: Add authorization logic to functions that use resource account capabilities.
Test Address Generation: Verify resource account addresses are computed correctly before mainnet deployment.
Document Ownership: Clearly document which modules control which resource accounts for auditing and verification.
Capability Rotation: Consider patterns for safely transferring or revoking resource account control if needed.
Avoid Seed Collisions: Use unique seeds to prevent accidentally creating multiple resource accounts at the same address.
Advanced Patterns#
Multi-Tier Resource Accounts#
// Create hierarchy of resource accounts
public fun create_protocol_structure(creator: &signer) {
let (treasury_signer, treasury_cap) = account::create_resource_account(
creator,
b"treasury"
);
let (rewards_signer, rewards_cap) = account::create_resource_account(
creator,
b"rewards"
);
let (insurance_signer, insurance_cap) = account::create_resource_account(
creator,
b"insurance"
);
// Store capabilities for different protocol functions
}
Controlled Capability Distribution#
struct GovernanceCap has key {
treasury_cap: account::SignerCapability,
withdraw_limit: u64,
last_withdraw: u64
}
public fun governed_withdraw(
amount: u64,
recipient: address
) acquires GovernanceCap {
let gov = borrow_global_mut<GovernanceCap>(@governance);
// Enforce time-locks and limits
assert!(amount <= gov.withdraw_limit, EEXCEEDS_LIMIT);
let resource_signer = account::create_signer_with_capability(&gov.treasury_cap);
coin::transfer(&resource_signer, recipient, amount);
gov.last_withdraw = timestamp::now_seconds();
}
Security Considerations#
Capability Storage: The SignerCapability is extremely powerful - treat it like a master private key and protect it appropriately.
Access Control: Implement robust access control for any function that uses resource account capabilities.
Upgrade Safety: If resource accounts publish modules, carefully manage upgrade policies to prevent malicious changes.
Resource Exhaustion: Resource accounts need gas for operations, so ensure they maintain sufficient APT balances.
Deterministic Generation: Understand that anyone can compute your resource account address from the source and seed.
Querying Resource Accounts#
#[view]
public fun get_resource_account_address(source: address, seed: vector<u8>): address {
account::create_resource_address(&source, seed)
}
#[view]
public fun get_protocol_treasury(): address {
account::create_resource_address(&@deployer, b"treasury")
}
Comparison with Regular Accounts#
Resource Accounts:
- No private key
- Controlled by smart contracts
- Deterministic addresses
- Perfect for protocols
Regular Accounts:
- Have private keys
- User-controlled
- Random addresses
- For individual users
Related Concepts#
- Multi-Agent Transactions - Coordinate with resource accounts
- Sponsored Transactions - Pay gas for resource accounts
- Module Structure - Deploy modules to resource accounts
- Key Rotation - Not applicable to resource accounts