⚠️Blast API (blastapi.io) ends Oct 31. Migrate to Dwellir and skip Alchemy's expensive compute units.
Switch Today →
Skip to main content

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 public and entry keywords
  • 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:

  1. The Move VM loads the module containing the function
  2. Arguments are BCS-deserialized from transaction payload
  3. The function executes with sender's signer capability
  4. State changes are committed if execution succeeds
  5. Events are emitted to the blockchain
  6. 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:

  1. Token Operations: Transfers, minting, burning, and approvals for fungible and non-fungible tokens.

  2. DeFi Protocols: Swaps, liquidity provision, staking, and yield farming operations.

  3. NFT Marketplaces: Listing, purchasing, bidding, and collection management.

  4. Gaming: Character creation, item crafting, battle execution, and reward distribution.

  5. Governance: Proposal submission, voting, and execution of governance decisions.

  6. 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.
  • 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