You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ICP uses the ICRC standard family for tokens and token-related operations. This page covers the token standards (ICRC-1 through ICRC-37) and wallet signer standards (ICRC-21 through ICRC-49) that developers need to build DeFi applications, wallets, and token integrations.
10
10
11
-
<!-- Content Brief -->
12
-
Reference for ICP token standards. Document ICRC-1 (fungible token transfers, balances, metadata), ICRC-2 (approve/transferFrom), ICRC-3 (transaction log/history), ICRC-7 (NFT standard), and related standards (ICRC-21/25/27/29/49 for wallet signers). Include standard interfaces, canonical ledger canister IDs, and compliance requirements.
11
+
ICRC stands for Internet Computer Request for Comments. Standards are proposed by the [ICRC working group](https://github.com/dfinity/ICRC), refined through community consensus, and adopted or rejected through NNS governance proposals.
|[ICRC-29](#wallet-signer-standards)| Window PostMessage transport | -- | Adopted |
26
+
|[ICRC-49](#wallet-signer-standards)| Call canister via signer | -- | Adopted |
27
+
28
+
## ICRC-1: Fungible tokens
29
+
30
+
ICRC-1 is the base standard for fungible tokens on ICP. It defines transfer, balance, and metadata interfaces. The standard intentionally excludes certain features — transaction notifications, block structure, and pre-signed transactions — which are provided by extension standards (ICRC-2, ICRC-3).
31
+
32
+
A ledger can report which extensions it supports through the `icrc1_supported_standards` endpoint.
33
+
34
+
### Account model
35
+
36
+
An ICRC-1 account consists of two parts:
37
+
38
+
-**`owner`** — a `Principal` identifying the account holder
39
+
-**`subaccount`** — an optional 32-byte `Blob` that defaults to all zeros when omitted
40
+
41
+
This means a single principal can control up to 2^256 distinct accounts by varying the subaccount.
42
+
43
+
```candid
44
+
type Account = record {
45
+
owner : principal;
46
+
subaccount : opt blob;
47
+
};
48
+
```
49
+
50
+
### Core methods
51
+
52
+
| Method | Signature | Description |
53
+
|--------|-----------|-------------|
54
+
|`icrc1_transfer`|`(TransferArg) -> (variant { Ok : nat; Err : TransferError })`| Transfer tokens between accounts |
55
+
|`icrc1_balance_of`|`(Account) -> (nat) query`| Return the balance of an account |
56
+
|`icrc1_total_supply`|`() -> (nat) query`| Return the total token supply |
57
+
|`icrc1_metadata`|`() -> (vec record { text; Value }) query`| Return token metadata entries |
58
+
|`icrc1_name`|`() -> (text) query`| Return the token name |
59
+
|`icrc1_symbol`|`() -> (text) query`| Return the token symbol |
60
+
|`icrc1_decimals`|`() -> (nat8) query`| Return the number of decimals |
61
+
|`icrc1_fee`|`() -> (nat) query`| Return the default transfer fee |
62
+
|`icrc1_minting_account`|`() -> (opt Account) query`| Return the minting account |
63
+
|`icrc1_supported_standards`|`() -> (vec record { name : text; url : text }) query`| Return supported standard extensions |
64
+
65
+
### Transfer arguments
66
+
67
+
```candid
68
+
type TransferArg = record {
69
+
from_subaccount : opt blob;
70
+
to : Account;
71
+
amount : nat;
72
+
fee : opt nat;
73
+
memo : opt blob;
74
+
created_at_time : opt nat64;
75
+
};
76
+
```
77
+
78
+
Setting `created_at_time` enables deduplication — the ledger rejects duplicate transfers submitted within a 24-hour window. Without it, identical transfers both execute.
79
+
80
+
### Transfer errors
81
+
82
+
```candid
83
+
type TransferError = variant {
84
+
BadFee : record { expected_fee : nat };
85
+
BadBurn : record { min_burn_amount : nat };
86
+
InsufficientFunds : record { balance : nat };
87
+
TooOld;
88
+
CreatedInFuture : record { ledger_time : nat64 };
89
+
Duplicate : record { duplicate_of : nat };
90
+
TemporarilyUnavailable;
91
+
GenericError : record { error_code : nat; message : text };
92
+
};
93
+
```
94
+
95
+
### Metadata entries
96
+
97
+
| Key | Type | Example |
98
+
|-----|------|---------|
99
+
|`icrc1:symbol`|`Text`|`"ICP"`|
100
+
|`icrc1:name`|`Text`|`"Internet Computer"`|
101
+
|`icrc1:decimals`|`Nat`|`8`|
102
+
|`icrc1:fee`|`Nat`|`10000`|
103
+
104
+
### Common ICRC-1 ledgers
105
+
106
+
The following are DFINITY-maintained ledgers. Many other ICRC-1 tokens exist on ICP — see the [ICP Dashboard token list](https://dashboard.internetcomputer.org/tokens) for a comprehensive registry. Anyone can deploy an ICRC-1 compliant ledger.
107
+
108
+
| Token | Ledger canister ID | Decimals |
109
+
|-------|-------------------|----------|
110
+
| ICP |`ryjl3-tyaaa-aaaaa-aaaba-cai`| 8 |
111
+
| ckBTC |`mxzaz-hqaaa-aaaar-qaada-cai`| 8 |
112
+
| ckETH |`ss2fx-dyaaa-aaaar-qacoq-cai`| 18 |
113
+
114
+
> Fees can change at any time. Always call `icrc1_fee` to get the current fee rather than hardcoding values.
115
+
116
+
Index canisters (for transaction history):
117
+
118
+
| Token | Index canister ID |
119
+
|-------|------------------|
120
+
| ICP |`qhbym-qaaaa-aaaaa-aaafq-cai`|
121
+
| ckBTC |`n5wcd-faaaa-aaaar-qaaea-cai`|
122
+
| ckETH |`s3zol-vqaaa-aaaar-qacpa-cai`|
123
+
124
+
[Read the full ICRC-1 standard](https://github.com/dfinity/ICRC-1/tree/main/standards/ICRC-1)
125
+
126
+
## ICRC-2: Approve and transfer-from
127
+
128
+
ICRC-2 extends ICRC-1 with an approve/transfer-from workflow, similar to ERC-20 allowances on Ethereum. An account owner delegates spending authority to a third party, who can then transfer tokens on the owner's behalf.
129
+
130
+
The workflow has two steps:
131
+
132
+
1. The account owner calls `icrc2_approve` to authorize a spender for up to X tokens.
133
+
2. The spender calls `icrc2_transfer_from` to move tokens from the owner's account. Multiple transfers are allowed as long as the total does not exceed the approved amount.
134
+
135
+
### Methods
136
+
137
+
| Method | Signature | Description |
138
+
|--------|-----------|-------------|
139
+
|`icrc2_approve`|`(ApproveArg) -> (variant { Ok : nat; Err : ApproveError })`| Authorize a spender for a token amount |
140
+
|`icrc2_transfer_from`|`(TransferFromArg) -> (variant { Ok : nat; Err : TransferFromError })`| Transfer tokens on behalf of the owner |
141
+
|`icrc2_allowance`|`(AllowanceArg) -> (Allowance) query`| Query the remaining allowance for a spender |
142
+
143
+
### Approve arguments
144
+
145
+
```candid
146
+
type ApproveArg = record {
147
+
from_subaccount : opt blob;
148
+
spender : Account;
149
+
amount : nat;
150
+
expected_allowance : opt nat;
151
+
expires_at : opt nat64;
152
+
fee : opt nat;
153
+
memo : opt blob;
154
+
created_at_time : opt nat64;
155
+
};
156
+
```
157
+
158
+
The `expected_allowance` field provides protection against race conditions — the call fails if the current allowance does not match the expected value. The `expires_at` field sets a deadline (in nanoseconds since the Unix epoch) after which the approval is no longer valid.
159
+
160
+
### Approve errors
161
+
162
+
```candid
163
+
type ApproveError = variant {
164
+
BadFee : record { expected_fee : nat };
165
+
InsufficientFunds : record { balance : nat };
166
+
AllowanceChanged : record { current_allowance : nat };
167
+
Expired : record { ledger_time : nat64 };
168
+
TooOld;
169
+
CreatedInFuture : record { ledger_time : nat64 };
170
+
Duplicate : record { duplicate_of : nat };
171
+
TemporarilyUnavailable;
172
+
GenericError : record { error_code : nat; message : text };
173
+
};
174
+
```
175
+
176
+
### Transfer-from arguments
177
+
178
+
```candid
179
+
type TransferFromArg = record {
180
+
spender_subaccount : opt blob;
181
+
from : Account;
182
+
to : Account;
183
+
amount : nat;
184
+
fee : opt nat;
185
+
memo : opt blob;
186
+
created_at_time : opt nat64;
187
+
};
188
+
```
189
+
190
+
### Transfer-from errors
191
+
192
+
```candid
193
+
type TransferFromError = variant {
194
+
BadFee : record { expected_fee : nat };
195
+
BadBurn : record { min_burn_amount : nat };
196
+
InsufficientFunds : record { balance : nat };
197
+
InsufficientAllowance : record { allowance : nat };
198
+
TooOld;
199
+
CreatedInFuture : record { ledger_time : nat64 };
200
+
Duplicate : record { duplicate_of : nat };
201
+
TemporarilyUnavailable;
202
+
GenericError : record { error_code : nat; message : text };
203
+
};
204
+
```
205
+
206
+
### Allowance query
207
+
208
+
```candid
209
+
type AllowanceArg = record {
210
+
account : Account;
211
+
spender : Account;
212
+
};
213
+
214
+
type Allowance = record {
215
+
allowance : nat;
216
+
expires_at : opt nat64;
217
+
};
218
+
```
219
+
220
+
### Common use cases
221
+
222
+
-**DEX integrations** — a DEX canister is approved to pull tokens from a user's account during a swap.
223
+
-**Subscription payments** — a service canister is approved for recurring token withdrawals.
224
+
-**Escrow** — an intermediary canister holds approval to release tokens when conditions are met.
225
+
226
+
ICP, ckBTC, and ckETH all implement ICRC-2.
227
+
228
+
[Read the full ICRC-2 standard](https://github.com/dfinity/ICRC-1/tree/main/standards/ICRC-2)
229
+
230
+
## ICRC-3: Transaction log
231
+
232
+
ICRC-3 extends ICRC-1 with a standardized transaction log interface. It defines how ledgers expose their block history, enabling clients and index canisters to retrieve and verify transaction records.
233
+
234
+
### Archive model
235
+
236
+
Ledgers store recent blocks directly and move older blocks to **archive canisters** to manage memory. When fetching blocks, `icrc3_get_blocks` returns blocks the ledger holds directly plus callbacks to fetch archived blocks from the appropriate archive canister. Use `icrc3_get_archives` to discover all archive canisters and the block ranges they hold.
237
+
238
+
### Methods
239
+
240
+
| Method | Signature | Description |
241
+
|--------|-----------|-------------|
242
+
|`icrc3_get_blocks`|`(vec record { start : nat; length : nat }) -> (GetBlocksResult) query`| Retrieve blocks by index range; returns callbacks for archived blocks |
243
+
|`icrc3_get_archives`|`(GetArchivesArgs) -> (vec record { canister_id; start; end }) query`| List archive canisters and the block ranges they hold |
244
+
|`icrc3_get_tip_certificate`|`() -> (opt DataCertificate) query`| Return a certificate for the last block hash and index |
245
+
|`icrc3_supported_block_types`|`() -> (vec record { block_type : text; url : text }) query`| List the block types the ledger produces |
246
+
247
+
### Block schema
248
+
249
+
ICRC-3 blocks use a generic `Value` representation that preserves all data for verification. Each block contains:
250
+
251
+
-**`phash`** — hash of the previous block (absent for the genesis block)
252
+
-**`btype`** — block type string (e.g., `"1xfer"` for ICRC-1 transfers, `"2approve"` for ICRC-2 approvals)
253
+
-**`ts`** — timestamp in nanoseconds
254
+
-**Transaction-specific fields** — vary by block type (e.g., `from`, `to`, `amt` for transfers)
255
+
256
+
### Adopted block types
257
+
258
+
Block type identifiers follow the naming convention `<icrc-number><operation>` (e.g., `1xfer` for ICRC-1 transfer). Anyone can define new block types for custom standards following this convention.
259
+
260
+
| Block type | Standard | Description |
261
+
|------------|----------|-------------|
262
+
|`1xfer`| ICRC-1 | Transfer |
263
+
|`1burn`| ICRC-1 | Burn |
264
+
|`1mint`| ICRC-1 | Mint |
265
+
|`2approve`| ICRC-2 | Approval |
266
+
|`2xfer`| ICRC-2 | Transfer-from |
267
+
268
+
### Proposed block types
269
+
270
+
The following block types are currently in the ICRC proposal process and not yet adopted:
[Read the full ICRC-3 standard](https://github.com/dfinity/ICRC-1/tree/main/standards/ICRC-3)
282
+
283
+
## ICRC-7: Non-fungible tokens
284
+
285
+
ICRC-7 defines the base standard for non-fungible tokens (NFTs) on ICP. It can be used to create and manage NFT collections. Like ICRC-1 for fungible tokens, ICRC-7 is intentionally minimal and excludes transaction notifications, block structure, and pre-signed transactions — these can be added through extensions.
286
+
287
+
ICRC-7 uses the same account model as ICRC-1 (principal + optional 32-byte subaccount).
288
+
289
+
### Core methods
290
+
291
+
| Method | Signature | Description |
292
+
|--------|-----------|-------------|
293
+
|`icrc7_collection_metadata`|`() -> (vec record { text; Value }) query`| Return collection metadata |
294
+
|`icrc7_name`|`() -> (text) query`| Return collection name |
295
+
|`icrc7_symbol`|`() -> (text) query`| Return collection symbol |
296
+
|`icrc7_total_supply`|`() -> (nat) query`| Return total number of tokens |
|`icrc7:logo`|`Text`| URL of the collection logo |
313
+
|`icrc7:total_supply`|`Nat`| Current number of tokens |
314
+
|`icrc7:supply_cap`|`Nat`| Maximum number of tokens (optional) |
315
+
316
+
[Read the full ICRC-7 standard](https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-7/ICRC-7.md)
317
+
318
+
## ICRC-37: NFT approvals
319
+
320
+
ICRC-37 extends ICRC-7 with an approval workflow for NFTs, analogous to how ICRC-2 extends ICRC-1 for fungible tokens. It adds support for creating approvals, revoking approvals, querying approval state, and executing transfers based on approvals.
321
+
322
+
A ledger that implements ICRC-37 must also implement all ICRC-7 methods. Support for ICRC-37 is optional for ICRC-7 ledgers.
323
+
324
+
### Methods
325
+
326
+
| Method | Signature | Description |
327
+
|--------|-----------|-------------|
328
+
|`icrc37_approve_tokens`|`(vec ApproveTokenArg) -> (vec opt ApproveTokenResult)`| Approve a spender for specific token IDs |
329
+
|`icrc37_approve_collection`|`(vec ApproveCollectionArg) -> (vec opt ApproveCollectionResult)`| Approve a spender for all tokens in the collection |
330
+
|`icrc37_revoke_token_approvals`|`(vec RevokeTokenApprovalArg) -> (vec opt RevokeTokenApprovalResult)`| Revoke approvals for specific tokens |
|`icrc37_transfer_from`|`(vec TransferFromArg) -> (vec opt TransferFromResult)`| Transfer tokens using an approval |
336
+
337
+
### Additional metadata
338
+
339
+
| Key | Type | Description |
340
+
|-----|------|-------------|
341
+
|`icrc37:max_approvals_per_token_or_collection`|`Nat`| Maximum active approvals allowed per principal or token |
342
+
|`icrc37:max_revoke_approvals`|`Nat`| Maximum approvals that can be revoked in one call (optional) |
343
+
344
+
[Read the full ICRC-37 standard](https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-37/ICRC-37.md)
345
+
346
+
## Wallet signer standards
347
+
348
+
The ICRC signer standards define how wallets interact with dApps on ICP. They use a popup-based model where every action requires explicit user approval, communicated via JSON-RPC 2.0 over `window.postMessage`.
349
+
350
+
| Standard | Purpose |
351
+
|----------|---------|
352
+
|**ICRC-21**| Canister call consent messages — enables canisters to provide human-readable descriptions of what a call will do, displayed to the user before signing |
353
+
|**ICRC-25**| Signer interaction standard — defines the permission lifecycle (`granted`, `denied`, `ask_on_use`) for signer methods |
354
+
|**ICRC-27**| Account discovery — allows dApps to request the list of accounts available in the wallet |
355
+
|**ICRC-29**| Window PostMessage transport — defines the communication channel between dApp and signer using `window.postMessage`|
356
+
|**ICRC-49**| Call canister — allows dApps to request the signer to execute a canister call on behalf of the user |
357
+
358
+
These standards are distinct from delegation-based authentication (such as Internet Identity). The signer model requires per-action user approval and does not create sessions or delegated identities.
359
+
360
+
For implementation details and code examples, see the [wallet integration guide](../guides/defi/wallet-integration.md).
361
+
362
+
## Next steps
363
+
364
+
-[Token ledgers guide](../guides/defi/token-ledgers.md) — deploy and interact with ICRC-1/ICRC-2 ledgers
365
+
-[Chain-key tokens guide](../guides/defi/chain-key-tokens.md) — work with ckBTC, ckETH, and other chain-key tokens
366
+
-[Wallet integration guide](../guides/defi/wallet-integration.md) — integrate wallet signer standards into your dApp
367
+
-[ICRC-1 standard specification](https://github.com/dfinity/ICRC-1/tree/main/standards/ICRC-1) — full specification on GitHub
368
+
-[ICRC-7 standard specification](https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-7/ICRC-7.md) — full NFT specification on GitHub
0 commit comments