entry_functions
Overview#
Entry functions are the primary mechanism for executing Move code via transactions on Aptos. These special functions serve as transaction entry points, allowing external accounts to trigger on-chain logic execution.
Technical Details#
Entry functions are declared with the public entry visibility modifier in Move modules:
public entry fun transfer(from: &signer, to: address, amount: u64) {
// Function implementation
}
Key Characteristics:
- Must be marked with both
publicandentrykeywords - Can be called directly from transactions
- Cannot return values (void return type)
- Can modify blockchain state
- Execute with the transaction sender's authority
- Support generic type parameters
How Entry Functions Work#
When a transaction calls an entry function:
- The Move VM loads the module containing the function
- Arguments are BCS-deserialized from transaction payload
- The function executes with sender's signer capability
- State changes are committed if execution succeeds
- Events are emitted to the blockchain
- Gas is consumed based on computational cost
Entry functions form the external API of Move modules, bridging off-chain applications with on-chain logic.
Practical Examples#
Simple Transfer:
public entry fun simple_transfer(sender: &signer, recipient: address, amount: u64) {
coin::transfer<AptosCoin>(sender, recipient, amount);
}
With Type Parameters:
public entry fun swap<CoinTypeA, CoinTypeB>(
sender: &signer,
amount_in: u64,
min_amount_out: u64
) {
// DEX swap implementation
}
Multiple Operations:
public entry fun batch_mint(admin: &signer, recipients: vector<address>, amounts: vector<u64>) {
let len = vector::length(&recipients);
let i = 0;
while (i < len) {
let recipient = *vector::borrow(&recipients, i);
let amount = *vector::borrow(&amounts, i);
mint_to(admin, recipient, amount);
i = i + 1;
};
}
Use Cases#
Entry functions power diverse blockchain applications:
-
Token Operations: Transfers, minting, burning, and approvals for fungible and non-fungible tokens.
-
DeFi Protocols: Swaps, liquidity provision, staking, and yield farming operations.
-
NFT Marketplaces: Listing, purchasing, bidding, and collection management.
-
Gaming: Character creation, item crafting, battle execution, and reward distribution.
-
Governance: Proposal submission, voting, and execution of governance decisions.
-
Identity: Account creation, key rotation, and permission management.
Best Practices#
Input Validation: Always validate inputs within entry functions to prevent invalid state transitions. Check address validity, amount ranges, and permission requirements explicitly.
Avoid Write Conflicts: Under Block-STM parallel execution, minimize conflicts by:
- Reading shared resources early in execution
- Writing to unique per-account resources when possible
- Using fine-grained resource structures instead of global state
- Leveraging Aggregator V2 for concurrent counters
Gas Efficiency: Keep entry functions focused and delegate complex logic to internal helper functions. This improves code organization and allows better gas optimization.
Error Handling: Use descriptive abort codes and messages to help developers debug failed transactions:
const EINSUFFICIENT_BALANCE: u64 = 1;
const EINVALID_RECIPIENT: u64 = 2;
public entry fun safe_transfer(sender: &signer, to: address, amount: u64) {
assert!(to != @0x0, EINVALID_RECIPIENT);
assert!(coin::balance<AptosCoin>(signer::address_of(sender)) >= amount, EINSUFFICIENT_BALANCE);
coin::transfer<AptosCoin>(sender, to, amount);
}
Signer Requirement: Only include &signer parameters for accounts that must authorize the transaction. Extra signer parameters complicate multi-signature scenarios unnecessarily.
Event Emission: Emit events for significant state changes to enable off-chain indexing and monitoring:
event::emit(TransferEvent {
from: signer::address_of(sender),
to: recipient,
amount
});
Common Patterns#
Admin Functions: Restrict privileged operations with capability checks:
public entry fun admin_mint(admin: &signer, to: address, amount: u64) acquires AdminCap {
let admin_addr = signer::address_of(admin);
assert!(exists<AdminCap>(admin_addr), ENOT_ADMIN);
// Mint logic
}
Batch Operations: Process multiple operations in one transaction for efficiency:
public entry fun batch_transfer(sender: &signer, recipients: vector<address>, amounts: vector<u64>) {
let len = vector::length(&recipients);
let i = 0;
while (i < len) {
coin::transfer<AptosCoin>(sender, *vector::borrow(&recipients, i), *vector::borrow(&amounts, i));
i = i + 1;
};
}
Resource Initialization: Create and move resources to accounts:
public entry fun initialize_account(account: &signer) {
move_to(account, AccountData {
balance: 0,
nonce: 0
});
}
Anti-Patterns to Avoid#
- Returning Values: Entry functions cannot return values. Use view functions for read operations.
- Excessive Computation: Long-running computations cause high gas costs and potential timeouts.
- Global Locks: Accessing single global resources creates bottlenecks under Block-STM.
- Unbounded Loops: Loops over unbounded vectors risk gas exhaustion.
Related Concepts#
- View Functions: Read-only functions for querying state without gas costs
- Script Functions: Legacy transaction entry points, now superseded by entry functions
- Public Functions: Module functions callable by other modules but not directly via transactions
- Inline Functions: Private helper functions for code organization