Delegation PDA
Seeds:[b"delegation", delegator.as_ref(), delegate.as_ref(), delegation_id.to_le_bytes()]
| Field | Type | Description |
|---|---|---|
delegation_id | u64 | Unique ID from the agent’s delegation_count counter. |
delegator | Pubkey | Wallet granting permissions. |
delegate | Pubkey | Wallet receiving permissions. |
nft_mint | Pubkey | Agent context (NFT mint). |
owner_version | u64 | Stored at creation, verified at use. Mismatches invalidate the delegation. |
can_transact | bool | Spend tokens on behalf of the agent. |
can_give_feedback | bool | Submit feedback as the agent. |
can_update_metadata | bool | Update agent metadata URI and hash. |
can_update_pricing | bool | Change payment requirements. |
can_register_services | bool | Add, update, or remove services. |
can_manage | bool | All permissions except transferring ownership. |
can_redelegate | bool | Create sub-delegations. |
max_spend_per_tx | u64 | Maximum tokens per transaction. 0 = no limit. |
max_spend_total | u64 | Maximum cumulative tokens. 0 = no limit. |
spent_total | u64 | Running total of tokens spent via this delegation. |
allowed_tokens | Vec<Pubkey> (max 5) | Allowed token mints. Empty = all tokens allowed. |
allowed_programs | Vec<Pubkey> (max 5) | Allowed CPI targets. Empty = all programs. Reserved for future use. |
expires_at | i64 | Unix timestamp expiry. 0 = no expiry. |
uses_remaining | u64 | Remaining uses. 0 = unlimited. |
total_uses | u64 | Total uses so far. |
active | bool | Whether the delegation is active. |
parent_delegation | Option<Pubkey> | If this is a sub-delegation, points to the parent Delegation PDA. |
depth | u8 | Delegation depth. 0 = direct from owner, 1 = sub, 2 = sub-sub. |
created_at | i64 | Unix timestamp. |
revoked_at | Option<i64> | Timestamp of revocation, if revoked. |
bump | u8 | PDA bump seed. |
Permission flags
Seven boolean flags control what a delegate can do:| Permission | Description |
|---|---|
can_transact | Spend tokens on behalf of the agent (used for x402 settlements). |
can_give_feedback | Submit feedback as the agent. |
can_update_metadata | Update the agent’s metadata URI and hash. |
can_update_pricing | Modify payment requirements for services. |
can_register_services | Add, update, or remove service endpoints. |
can_manage | Grants all permissions except NFT transfer. A shorthand for full control. |
can_redelegate | Create sub-delegations (required for delegation chaining). |
Constraints
Constraints limit what a delegate can do even when they have the right permission:| Constraint | Type | Description |
|---|---|---|
max_spend_per_tx | u64 | Per-transaction spending cap. 0 = unlimited. |
max_spend_total | u64 | Cumulative spending cap. 0 = unlimited. |
allowed_tokens | Vec<Pubkey> | Whitelist of token mints. Empty = all allowed. Max 5. |
allowed_programs | Vec<Pubkey> | Whitelist of CPI targets. Empty = all allowed. Max 5. |
expires_at | i64 | Unix timestamp after which the delegation is invalid. 0 = no expiry. |
uses_remaining | u64 | Number of remaining uses before the delegation expires. 0 = unlimited. |
Delegation depth
Delegations support up to 3 levels of chaining:| Depth | Relationship |
|---|---|
| 0 | Owner delegates to a wallet. |
| 1 | That wallet sub-delegates to another wallet. |
| 2 | The sub-delegate creates a sub-sub-delegation (maximum depth). |
max_spend_totalcannot exceed the parent’s remaining budget (parent.max_spend_total - parent.spent_total).expires_atcannot be later than the parent’s expiry.allowed_tokensmust be a subset of the parent’s allowed tokens.allowed_programsmust be a subset of the parent’s allowed programs.- Permissions must be a subset of the parent’s permissions.
Cascading invalidation via owner_version
When an agent NFT is transferred andclaim_agent is called, the owner_version on the AgentIdentity increments. Every delegation stores the owner_version at creation time. At use time, the program checks:
owner_version check.
DelegationBuilder fluent API
The SDK provides a builder pattern for constructing delegations:Permission methods
| Method | Sets |
|---|---|
.transact() | can_transact = true |
.feedback() | can_give_feedback = true |
.metadata() | can_update_metadata = true |
.pricing() | can_update_pricing = true |
.services() | can_register_services = true |
.manage() | can_manage = true |
.redelegate() | can_redelegate = true |
.allPermissions() | All flags set to true. |
Constraint methods
| Method | Sets |
|---|---|
.spendLimit(perTx, total) | max_spend_per_tx and max_spend_total |
.tokens(mints[]) | allowed_tokens |
.programs(ids[]) | allowed_programs |
.expiry(unix) | expires_at |
.uses(count) | uses_remaining |
.parent(delegationPda) | parent_delegation (for sub-delegations) |
x402 budget integration
Delegations power budget-based x402 payments. Instead of signing every payment transaction, a user sets up a spending budget once and the x84 facilitator auto-debits within the budget constraints.Create delegation and SPL approve in one transaction
The user signs a single transaction containing two instructions:
spl_token::approve— authorizes the x84 facilitator wallet as delegate on the user’s token account (ATA), with an amount matchingmax_spend_total.x84::create_delegation— creates the on-chain delegation withcan_transact = trueand desired constraints.
Facilitator auto-debits on each request
When the user calls an x402-gated agent endpoint, the x402 gate reads the
X-DELEGATION header, loads the Delegation PDA, verifies all constraints, and uses the SPL Token delegate authority to transfer tokens from the user’s ATA. No per-request signature is needed.Revocation
active status during verification.
Delegation verification checks (full list)
Delegation verification checks (full list)
When a delegate attempts to use their delegation, the program runs these checks in order:
delegation.active == truedelegation.delegate == callerdelegation.nft_mint == agent.nft_mintdelegation.owner_version == agent.owner_version- Expiry:
delegation.expires_at == 0ordelegation.expires_at > now - Uses:
delegation.uses_remaining == 0ordelegation.uses_remaining > 0 - Required permission flag is set
- If spending:
amount <= max_spend_per_txandspent_total + amount <= max_spend_total - If token specified:
allowed_tokensis empty or contains the token mint - If program specified:
allowed_programsis empty or contains the program ID - If
parent_delegationis set: recursively verify the parent (max depth ensures termination)