Resource Management
Resource management is one of Move's most distinctive and powerful features, providing built-in safety guarantees that prevent common blockchain vulnerabilities. Move's resource model ensures that digital assets cannot be copied, accidentally lost, or double-spent through compiler-enforced linear type semantics and explicit resource handling patterns.
Overview#
Resources in Move are special types marked with the key ability that represent digital assets or critical state. Unlike regular data structures, resources have strict lifecycle rules: they cannot be copied or implicitly destroyed, must be explicitly moved between storage locations, and can only exist in one place at a time. This linear type system eliminates entire classes of vulnerabilities that plague other blockchain platforms.
Resource Lifecycle#
Resources follow a strict lifecycle from creation to destruction:
module 0x1::resource_example {
use std::signer;
// Resource definition - key ability required for global storage
struct Vault has key {
balance: u64,
owner: address
}
// Creating a resource
public entry fun create_vault(account: &signer, initial_balance: u64) {
let vault = Vault {
balance: initial_balance,
owner: signer::address_of(account)
};
// Move resource into global storage
move_to(account, vault);
}
// Accessing a resource - requires acquires annotation
public fun get_balance(addr: address): u64 acquires Vault {
let vault_ref = borrow_global<Vault>(addr);
vault_ref.balance
}
// Modifying a resource
public entry fun deposit(account: &signer, amount: u64) acquires Vault {
let addr = signer::address_of(account);
let vault_ref = borrow_global_mut<Vault>(addr);
vault_ref.balance = vault_ref.balance + amount;
}
// Moving resource out of storage
public entry fun destroy_vault(account: &signer) acquires Vault {
let addr = signer::address_of(account);
let Vault { balance: _, owner: _ } = move_from<Vault>(addr);
// Resource is destructured and destroyed
}
}
Acquires Annotation#
The acquires keyword is mandatory for functions that access global resources. It serves both as documentation and as a compiler check to prevent reentrancy vulnerabilities:
// Function that reads from one resource and writes to another
public fun transfer_between_vaults(
from: address,
to: address,
amount: u64
) acquires Vault {
let from_vault = borrow_global_mut<Vault>(from);
from_vault.balance = from_vault.balance - amount;
let to_vault = borrow_global_mut<Vault>(to);
to_vault.balance = to_vault.balance + amount;
}
// Multiple resource types require listing all
public fun complex_operation(addr: address) acquires Vault, UserProfile, Settings {
// Can access all three resource types
}
Resource Patterns#
Capability Pattern#
Use capability resources to control access to privileged operations:
struct MintCapability has key, store {
total_minted: u64
}
public fun mint_with_cap(
cap: &mut MintCapability,
recipient: address,
amount: u64
) {
cap.total_minted = cap.total_minted + amount;
// Mint logic here
}
Witness Pattern#
Use one-time witnesses for initialization guarantees:
struct COIN has drop {}
public fun initialize(witness: COIN, account: &signer) {
// Can only be called once because witness is consumed
}
Hot Potato Pattern#
Create types without drop ability to force handling:
struct Receipt {
amount: u64,
must_be_consumed: bool
}
public fun create_receipt(): Receipt {
Receipt { amount: 100, must_be_consumed: true }
}
public fun consume_receipt(receipt: Receipt) {
let Receipt { amount: _, must_be_consumed: _ } = receipt;
}
Real-World Use Cases#
-
Token Implementations: Manage token balances as resources to guarantee conservation of supply and prevent unauthorized minting or burning through compiler-enforced linearity.
-
NFT Ownership: Represent unique digital assets as resources that cannot be duplicated, ensuring true scarcity and provable ownership on-chain.
-
Vault Systems: Build secure storage mechanisms where assets can only be accessed by authorized parties, leveraging resource semantics for safety.
-
Permission Systems: Create capability resources that grant specific permissions, ensuring that access rights cannot be forged or duplicated.
-
Escrow Services: Hold resources in escrow with guaranteed atomic transfers, where assets must either complete the full transfer or remain in the original location.
-
Gaming Assets: Represent in-game items as resources with unique properties that cannot be duplicated or destroyed outside intended game mechanics.
Best Practices#
Always Document Acquires: Explicitly list all resource types a function accesses in the acquires clause, even when it seems obvious. This helps prevent subtle bugs.
Check Resource Existence: Use exists<T>(address) before borrowing resources to handle cases where resources might not be initialized.
Avoid Cross-Module Resource Access: Design modules to manage their own resources rather than accessing resources defined in other modules when possible.
Use Immutable Borrows: Prefer borrow_global over borrow_global_mut when only reading data to reduce potential for concurrent access issues.
Structured Destruction: When moving resources out of storage, always destructure them completely to ensure all fields are properly handled.
Resource Initialization: Provide clear initialization functions and document preconditions for resource creation to prevent misuse.
Capability Distribution: Carefully control how and when capability resources are created and distributed to maintain system security.
Common Patterns#
// Check-then-access pattern
if (exists<Vault>(addr)) {
let vault = borrow_global<Vault>(addr);
// Use vault
};
// Modify with validation
public entry fun safe_withdraw(account: &signer, amount: u64) acquires Vault {
let addr = signer::address_of(account);
assert!(exists<Vault>(addr), EVAULT_NOT_FOUND);
let vault = borrow_global_mut<Vault>(addr);
assert!(vault.balance >= amount, EINSUFFICIENT_BALANCE);
vault.balance = vault.balance - amount;
}
Related Concepts#
- Module Structure - Organize resource definitions effectively
- Formal Verification - Prove resource invariants mathematically
- Object Model - Alternative resource composition approach
- Testing - Test resource lifecycle operations