Profiles
Tags
All 18 tags: where each applies, its arguments, and whether it is core or experimental.
A tag gives one position of an interface a meaning. Twelve tags are core: the
preregistered eval depends on them, so they are stable within v0. Six are
experimental and may change. The tables below are checked against
spec §5, the JSON Schema
(profile/spec/profile-v0.schema.json,
the args_* definitions) and the resolver's applicability checks
(checks.mjs).
Each tag links to the concept page that explains it.
A tag is one annotation: an address, the tag's name and its arguments. This one is from the ICRC-1 pack:
- at
- icrc1_transfer(0).amount
- tag
amount
- args
- decimals: { "from": "query:icrc1_decimals" }
- symbol: { "from": "query:icrc1_symbol" }
Value tags#
A value tag sits at a value address (method(i).path, method->(i).path or, in a canister
profile, type:Decl.path; see Addresses). opt is transparent: a
tag on fee : opt nat applies to the nat. A tag on a type it does not fit is
EE_TAG_NOT_APPLICABLE; a value tag at a bare method name is EE_TAG_TARGET.
| Tag | Status | Applies to | Arguments |
|---|---|---|---|
amount |
core | nat, int, nat8, nat16, nat32, nat64, int8, int16, int32, int64 |
decimals, required unless token is given: an integer 0–255, or { "from": "query:<method>" }.symbol, optional: text of 1–32 characters, or a query: value.token, EVM only: a relative path to the address field that names the amount's token; decimals and symbol are then left out (below).threshold, optional, display only: a decimal string in base units; a value at or above it displays as message and the symbol.message, optional: 1–32 characters, default "Unlimited"; needs threshold.
|
timestamp |
core | nat, int, nat32, nat64, int64 |
unit, required: ns, us, ms or s.epoch, optional: unix.threshold with message, optional, display only, always together: a value at or above threshold (a decimal string of the tagged unit) displays as message (1–32 characters, no default).labels, optional, display only: 1–16 exact values (decimal strings of the tagged unit) mapped to words of 1–32 characters.
|
duration |
core | as timestamp |
unit, required: ns, us, ms or s. |
account |
core | record { owner : principal; subaccount : opt blob } |
encoding, optional: icrc1. |
subaccount |
core | blob |
none: args is {}. |
bytes |
core | blob |
encoding, required: utf8, hex or base64.max_len, optional: an integer ≥ 0, counted in bytes after encoding.
|
default |
core | any position; the value null only on an opt (EE_DEFAULT_NULL) |
value, required: a Candid textual literal ("null", "opt 0"), or a dynamic value (query:, now, caller).mode, required: auto (filled by the tool layer, never exposed) or suggest (exposed, pre-filled).
|
describe |
core | any position, or a method |
label: 1–80 characters.help: 1–2000 characters.At least one of the two. |
text |
experimental | text |
format: plain, markdown, email or url.max_len: an integer ≥ 0.pattern: a regular expression.At least one of the three. |
range |
experimental | a numeric type, text, a vec |
min, max: decimal strings, in base units.min_len, max_len: integers ≥ 0.At least one of the four. |
ui |
experimental | any position |
order (integer), group (text), hidden (boolean), widget (text); at least one. UI only: agents ignore it.
|
named |
experimental | a position a name from a names set can stand for; its Candid type equals the set's field (EE_NAMED_TYPE) |
set, required: a declared set (EE_NAMED_SET), matching ^[a-z][a-z0-9_]*$, at most 64 characters.by, optional: value (default) or show.
|
Dynamic values#
| Value | Allowed in | Rule |
|---|---|---|
{ "from": "query:<method>" } |
amount.decimals (a number), amount.symbol (text), default.value |
A query or composite query of the same canister, with no arguments and one result of the expected kind: EE_REF_UNRESOLVED, EE_REF_NOT_QUERY, EE_REF_SHAPE, EE_REF_TYPE. |
{ "from": "now" }, { "from": "caller" } |
default.value only |
Anywhere else is EE_DYNAMIC_KIND. now is the canister's clock, never the host's. |
Integers that can exceed 253 (amounts, bounds) are decimal strings, such as "100000000". The
resolver leaves dynamic values unresolved in the resolved form: resolving them needs the network, so it is the
consumer's job (Profile and resolved form).
Whether a default.value literal other than null type-checks against its position. The
spec requires consumers to check it before they use a default.
Words for extreme values#
threshold, message and labels change what a value displays as, never what it
is: input is unchanged, a number at the threshold still parses to that number, and a word is never parsed back
(spec §5.3, D-054 for amount, D-055 for timestamp). On a timestamp a label is
checked first, then the threshold, then the time. A timestamp shown as words is never read as a time: it is not localized, and in an ICRC-21 fields message it is Text, never
TimestampSeconds; an amount at or above its threshold is Text, never a
TokenAmount. The words exist for deadlines whose extreme values are not dates: uint256 max
for "never", which is otherwise out of range, and Permit2's expiration 0 for "this block only", which otherwise reads
as 1970. From the Permit2 profile:
- at
- PermitSingle(0).details.expiration
- tag
timestamp
- args
- unit: "s"
- epoch: "unix"
- threshold: "281474976710655"
- message: "Never"
- labels: { "0": "Only in the block it is used" }
An amount in the token a field names#
experimental EVM only (spec §15.9). amount.token is a relative path from
the argument to an address field of the same argument whose path up to its last [] is the
amount's own: the token is in the same array element as the amount, or outside every array
("token": "details[].token" for details[].amount). decimals and
symbol are not given (the schema refuses them): they are that token's decimals() and
symbol(), which the consumer reads and passes per token address
(tokens: { "0xA0b8…eB48": { "decimals": 6, "symbol": "USDC" } }); the libraries do no I/O. Without an
entry there is no text (EE_REF_UNRESOLVED; §12 rule 10), never a number read in the wrong decimals. A
token's symbol() is its own claim, so its address is always shown beside it. A path that breaks these
rules is EE_TOKEN_PATH, and so is token on a Candid service, whose ledger a field cannot name
yet (open OQ-6, on Open questions). token works on
functions too.
- at
- PermitBatch(0).details[].amount
- tag
amount
- args
- token: "details[].token"
- threshold: "1461501637330902918203684832716283019655932542975"
Method tags#
A method tag sits at a bare method name. intent and effect may also sit at a case address,
an argument address whose last step selects a case of a variant (propose(0).Transfer, D-053); a
result address, a type: address or a path through [] is EE_TAG_TARGET, and two
tagged cases one value can take both are EE_CASE_OVERLAP.
| Tag | Status | Address | Arguments |
|---|---|---|---|
intent |
core | a method, or a case of a variant in its arguments |
title, required: 1–60 characters.template, required: 1–500 characters, the grammar of spec §6 ({relpath} placeholders, [ … ] optional segments, escapes {{ }} [[ ]]).
|
effect |
core | a method, or a case of a variant in its arguments |
kind, required: read write transfer approve admin destroyirreversible, required: boolean.replicated, optional: boolean; true only with kind: "read" and irreversible: false on an update method (a read-only update, D-038).grant, optional: an object, what a transfer or approve lets someone take: spender or to, amount, and ends, optional, as argument relpaths. Only on a method's effect, not a case's (EE_GRANT; below, D-056).
|
idempotency |
core | an update method (EE_IDEMPOTENCY_MODE) |
mode, required: replay_same_payload.stamp, required: an argument relpath to the field filled once per intent; it carries a timestamp tag (EE_STAMP_UNTAGGED).
|
outcome |
core | a method |
classes: { result relpath → class }; every key selects a variant case (EE_OUTCOME_NOT_CASE).default: a class.At least one of the two. Classes: success already_done retry_same retry_new fix_input fail |
list |
experimental | a method |
cursor, required: an argument relpath.next, items, required: result relpaths.
|
names |
experimental | a query, composite query or read-only update that takes no arguments (EE_NAMES_MODE, EE_NAMES_SHAPE) |
set, required: the set's name, declared once (EE_NAMES_SET_DUPLICATE).items, required: a result relpath to a vec of records.name, required: a path inside one item, of type text (EE_NAMES_FIELD).value, required; show, kind, optional: paths inside one item.
|
Defaults and bounds#
| Rule | Spec |
|---|---|
Without an effect tag, a query or composite query is read; an update is write with irreversible: true. The resolved form records these with "origin": "default". |
§5.3 |
A query declared as anything but read is EE_EFFECT_MODE; a read on an update or oneway method is WW_EFFECT_MODE, except the read-only update form. |
§5.3 |
A method's effect bounds its cases': when a case effect is transfer, approve, admin or destroy, the method's effect is one of these four, and when a case effect is irreversible, so is the method's (EE_CASE_EFFECT). |
§5.3, D-053 |
A result case with no class falls back to default (WW_OUTCOME_UNCLASSIFIED at info level), and is a warning when there is no default. Consumers treat a case with no class and no default as fail. |
§5.3 |
classes may be left out when the result has no variant to select; default is then required and classes every reply. |
§5.3, D-049 |
Overlapping outcome keys: the most specific (longest path) wins. |
§5.3 |
What a call grants#
Spec §5.3, D-056. An effect of kind transfer or approve may carry
grant: who may take tokens because of the call or the signature, which token, how many and until when,
so that a consumer can compare a request with terms the person confirmed (Confirm
terms for a task). Its values are relpaths into the method's arguments, written as an intent template's
placeholders are (§6). From the ICRC-2 pack:
- at
- icrc2_approve
- tag
effect
- args
- kind: "approve"
- irreversible: false
- grant: { "spender": "spender", "amount": "amount", "ends": ["expires_at"] }
| Key | Required | The position it names |
|---|---|---|
spender or to |
exactly one | Who may take the tokens. spender is for an allowance, or for a signature someone else submits; to is for a transfer that pays them directly. An approve's grant names a spender. The position is an address (EVM, §15) or carries an account tag. |
amount |
yes | A position with an amount tag, of an unsigned type, where a value at or above the tag's threshold means unlimited; or a bool, where true is unlimited and false grants nothing (DAI's permit, §15.9). |
ends |
no | One to eight positions with a timestamp tag. The grant ends at the latest of them, and one at or above its tag's threshold, or an optional one left out, means it never ends. With no ends, an approve never ends and a transfer ends when it is made. |
spender,toandamountare in every call: neither optional nor inside a case of a variant, and no position of a grant is inside one.- No position steps into
[]: a grant covers one token, so a request that grants several (Permit2'sPermitBatch) has none in v0. - The token is the one the
amounttag names (token, §15.9), else the contract or canister the call is for. - A deadline that bounds only when a signature may be used is not an end of what it grants: EIP-2612's
deadlineand DAI'sexpiryset allowances that never end, so neither is inends. - The
amount,timestampandaccounttags a grant needs may come from another profile of the merge: the resolver checks the grant against the merged tags.
A grant on another kind, on a case effect (§5.2), or whose positions break these rules is EE_GRANT. A pack applied to a service where its grant does not hold keeps its effect and loses the grant (II_PACK_ANNOTATION_SKIPPED): dropping the effect would leave a transfer or approve with the default effect. A grant is the author's claim, like any effect (§11.1). The grants the standard packs carry are on Standard packs.
Tags on an EVM contract#
EVM support is spec §15, phase 1 and the first part of phase 2: profiles, consent text for calldata and for EIP-712 signing requests, and ERC-7730 export. Agent tools, outcome classes over reverts and idempotency by nonce are later phases. See EVM contracts.
An ABI is read as the graph a .did compiles to, so the same tags apply, with these types (spec §15.4):
| Tag | On an EVM contract |
|---|---|
amount |
uint8…uint256, int8…int256. query: refs are eth_calls of a view or pure function of the same contract with no argument and one result. token names the token by an address field instead (§15.9). |
timestamp, duration |
uint32…uint256, int64…int256. timestamp words (§5.3) are for EVM deadlines first. |
bytes |
bytes, bytes1…bytes32 |
text |
string |
range |
an integer type, string, an array |
describe, default, ui, intent, effect |
as on a canister. A template has no [ ] segments (EE_TEMPLATE): an ABI value is never absent. effect defaults as in §5.3; a grant's spender or to is an address. |
account, subaccount |
none (EE_TAG_NOT_APPLICABLE): an address needs no tag; it is shown with its EIP-55 checksum. |
idempotency, outcome, list, names, named |
not yet (EE_TAG_TARGET): they need nonces, receipts and reads that a later phase adds. |
The native value a payable function receives has no position yet (open O2, on
Open questions).
Tags on EIP-712 messages#
experimental Spec §15.9, D-055. A contract profile or an EVM pack may bind EIP-712
struct types in binds.eip712.types (Profile and resolved
form). A message is a type the same document tags with intent at its bare name; its
struct is argument 0, as a function's parameters are, so its members are addressed like a function's:
- at
- PermitSingle
- tag
intent
- args
- title: "Permit2: allow token spending"
- template: "Let {spender} spend up to {details.amount}. Allowance expires: {details.expiration}. Signature expires: {sigDeadline}."
| Tag | On an EIP-712 message |
|---|---|
intent |
At the type's bare name (PermitSingle): this is what makes the type a message. A type without an intent is a type only, and an address that names it is EE_ADDR_UNRESOLVED; its members are reached through a message that contains it (PermitSingle(0).details.amount). |
effect |
A signature runs nothing when it is signed, so effect says what it allows, of any kind: approve for an allowance, transfer for a one-time transfer. Default write, irreversible. replicated does not apply (EE_EFFECT_MODE). Its grant names the struct's members, as its template does (below). |
| every other tag | As on an EVM contract, in the table above (§15.4), at the message's member addresses: the value tags and describe apply, and the tags not yet defined for EVM are refused the same way. amount.token names the token a member holds ("token": "details.token"). |
A message may not share its name with a function, and two profiles may not give one type name different members (the
later profile is invalid): each is EE_EIP712. A signing request matches a message by its type hash, never
by its name: DAI's Permit and EIP-2612's share a name and not a hash. The standard messages are on
Standard packs.
A message's grant#
Spec §15.9, D-056. A message's grant names its positions as its template does, from the struct. Permit2's
PermitSingle:
- at
- PermitSingle
- tag
effect
- args
- kind: "approve"
- irreversible: false
- grant: { "spender": "spender", "amount": "details.amount", "ends": ["details.expiration", "sigDeadline"] }
fact (Uniswap permit2 at main, read 2026-10-30,
src/libraries/Allowance.sol, as spec §15.9 records) An expiration of 0 is stored as the time of the block
the permit is used in, and that block may come as late as sigDeadline; the latest of the two is the end,
so 0 needs no rule of its own. The token is the one amount.token names
(details.token), not the verifying contract.
Where to read more#
How each value tag parses input and displays output, without rounding.
Intent, effects and confirmationThe line a person reads, and which calls need their confirmation.
Outcomes and retriesOutcome classes, the idempotency stamp and the consumer's retry rules.
NamesThe names and named tags and the one matching rule.
The experimental binding: ABIs, calldata, EIP-712 messages and ERC-7730.
Profile diagnosticsEvery code a misplaced or misapplied tag produces.