ICP Ledger Canister
The ICP Ledger canister is the core token ledger for the Internet Computer Protocol. It implements the ICRC-1 and ICRC-2 token standards and manages all ICP token transfers and balances.Overview
The ICP Ledger provides:- Token Transfers: Send ICP tokens between accounts
- Balance Queries: Check account balances
- Transaction History: Query historical blocks and transactions
- ICRC-1 Standard: Full implementation of the ICRC-1 fungible token standard
- ICRC-2 Standard: Approve/transfer_from pattern for delegation
- Archive Canisters: Automatic archival of historical blocks for scalability
Account Model
The ICP Ledger supports two account identifier formats:Legacy AccountIdentifier
A 32-byte blob that is the SHA-224 hash of:- Domain separator (“\x0Aaccount-id”)
- Principal
- Subaccount (32 bytes, defaults to zeros)
ICRC-1 Account
A record with:owner: Principal that owns the accountsubaccount: Optional 32-byte blob (defaults to zeros)
Token Units
ICP uses e8s (10^-8 ICP) as its base unit:- 1 ICP = 100,000,000 e8s
- Transfer fee: 10,000 e8s (0.0001 ICP)
Core Methods
ICRC-1 Standard Methods
icrc1_transfer
Transfer ICP tokens to another account.Source subaccount (defaults to all zeros)
Destination account with owner and optional subaccount
Amount to transfer in e8s
Transaction fee in e8s (defaults to standard fee of 10,000 e8s)
Optional memo (max 32 bytes)
Transaction creation timestamp in nanoseconds (for deduplication)
Transfer result:
Ok: Block index of the transactionErr: Transfer error with details
BadFee: Incorrect fee amount providedInsufficientFunds: Source account has insufficient balanceTooOld: Transaction is older than 24 hoursCreatedInFuture: Transaction timestamp is in the futureDuplicate: Transaction with same memo and timestamp already existsTemporarilyUnavailable: Ledger is temporarily unavailableGenericError: Other error with description
icrc1_balance_of
Query the balance of an account.Account to query with owner and optional subaccount
Account balance in e8s
icrc1_metadata
Get ledger metadata.List of metadata key-value pairs including:
icrc1:name: Token name (“Internet Computer”)icrc1:symbol: Token symbol (“ICP”)icrc1:decimals: Decimal places (8)icrc1:fee: Transfer fee
icrc1_supported_standards
List supported token standards.List of supported standards:
- ICRC-1: Fungible token standard
- ICRC-2: Approve and transfer from
- ICRC-10: Supported standards query
- ICRC-21: Canister call consent messages
icrc1_name
Get the token name.Token name: “Internet Computer”
icrc1_symbol
Get the token symbol.Token symbol: “ICP”
icrc1_decimals
Get the number of decimal places.Decimal places: 8
icrc1_fee
Get the transfer fee.Transfer fee in e8s: 10,000
icrc1_total_supply
Get the total token supply.Total ICP supply in e8s
icrc1_minting_account
Get the minting account.The account that can mint new tokens (governance minting account)
ICRC-2 Standard Methods
icrc2_approve
Approve a spender to transfer tokens on your behalf.Source subaccount (defaults to all zeros)
Account to approve as spender
Amount to approve in e8s
Current allowance (for safety, prevents race conditions)
Expiration timestamp in nanoseconds
Transaction fee (defaults to 10,000 e8s)
Optional memo
Transaction creation timestamp
Approve result:
Ok: Block index of the approvalErr: Approval error
BadFee: Incorrect feeInsufficientFunds: Insufficient balance to pay feeAllowanceChanged: Current allowance doesn’t match expected_allowanceExpired: Expiration time is in the pastTooOld: Transaction too oldCreatedInFuture: Transaction timestamp in futureDuplicate: Duplicate transactionTemporarilyUnavailable: Service temporarily unavailableGenericError: Other error
icrc2_transfer_from
Transfer tokens from an approved account.Spender’s subaccount (defaults to all zeros)
Account to transfer from (must have approval)
Destination account
Amount to transfer in e8s
Transaction fee
Optional memo
Transaction creation timestamp
Transfer result:
Ok: Block indexErr: Transfer error
InsufficientAllowance: Not enough allowance for transfer + feeBadFee: Incorrect feeInsufficientFunds: Source account has insufficient balanceTooOld: Transaction too oldCreatedInFuture: Transaction in futureDuplicate: Duplicate transactionTemporarilyUnavailable: Service unavailableGenericError: Other error
icrc2_allowance
Query the allowance between accounts.Account that granted the allowance
Spender account
Current allowance amount in e8s
Expiration timestamp (if set)
Legacy Methods
transfer
Legacy transfer method using AccountIdentifier format.Transaction memo (64-bit number)
Amount to transfer with e8s field
Transaction fee (must be 10,000 e8s)
Source subaccount
Destination account identifier (32-byte blob)
Transaction timestamp with timestamp_nanos field
Transfer result:
Ok: Block indexErr: Transfer error
account_balance
Query account balance using AccountIdentifier.Account identifier (32-byte blob)
Account balance with e8s field
account_identifier
Convert an ICRC-1 Account to AccountIdentifier format.ICRC-1 account to convert
32-byte AccountIdentifier blob
Query Methods
query_blocks
Query a range of blocks from the ledger.Starting block index
Number of blocks to fetch
Total number of blocks in the ledger
List of blocks in the specified range
Index of the first block in the response
References to archive canisters for older blocks
Certificate for the response (in non-replicated queries)
archives
Get list of archive canisters.List of archive canister IDs storing historical blocks
tip_of_chain
Get the latest block index.Index of the most recent block
Certificate for the tip
transfer_fee
Get the current transfer fee.Current transfer fee (10,000 e8s)
Archive Canisters
As the ledger grows, old blocks are moved to archive canisters to keep the main ledger efficient.Archive Query Methods
get_blocks
Query blocks from an archive canister.Starting block index
Number of blocks to fetch
Ok: BlockRange with blocksErr: Error if start index is invalid
get_encoded_blocks
Query encoded blocks (more efficient for large queries).Starting block index
Number of blocks to fetch
Ok: Vec of encoded block blobsErr: Error details
Data Types
Block
Represents a transaction block in the ledger.Hash of the parent block
The transaction in this block
Block creation timestamp in nanoseconds
Transaction
Legacy memo (64-bit number)
ICRC-1 memo (up to 32 bytes)
The operation performed:
Transfer: Transfer between accountsMint: Mint new tokensBurn: Burn tokensApprove: Set allowance for spender
Transaction creation timestamp
Operation
TransferSource account
Destination account
Transfer amount
Transaction fee paid
Spender if using transfer_from
Recipient account
Minted amount
Source account
Burned amount
Spender if using burn_from
Account granting approval
Account receiving approval
Approved amount
Approval fee
Expiration time
Expected current allowance
Transaction Deduplication
The ledger implements deduplication to prevent double-spending:- Each transaction can include a
created_at_timetimestamp - Transactions are deduplicated based on
(sender, memo, created_at_time) - Deduplication window is 24 hours
- If a duplicate is detected, the original block index is returned
Best Practices
For Developers
- Use ICRC-1 Format: Prefer ICRC-1 Account format over legacy AccountIdentifier
- Set Timestamps: Always set
created_at_timefor deduplication - Handle Errors: Properly handle all error variants
- Use Query Methods: Use query methods for read operations (faster, cheaper)
- Archive Access: Check
archived_blockswhen querying old transactions - Fee Checking: Always check current fee with
icrc1_fee()before transfers
For Users
- Transaction IDs: Save block indices for transaction tracking
- Subaccounts: Use subaccounts to manage multiple addresses under one principal
- Memo Usage: Use memos to track payment purposes
- Approval Limits: Set reasonable allowance limits and expiration times