Skip to main content

Capabilities

What Are Capabilities?​

Capabilities are fine-grained, expirable, metered permissions attached to a wallet. They are the primary gate on what an agent wallet (or any wallet) is allowed to do on the protocol.

A wallet without any capabilities can perform any action its auth groups permit. A wallet with explicit capabilities is restricted to only those actions; capabilities act as a whitelist that overrides the broader auth group permissions.

This makes capabilities the correct tool for scoping autonomous agents: assign the minimum set of actions the agent needs, bound by resource address, invocation count, and block-height expiry, and the protocol enforces that scope at every transaction.


Capability Structure​

Capability {
Action string // The action type (e.g., "contract:invoke", "token:transfer")
Resource string // The target resource address, or "*" for any
Limits CapabilityLimits // Optional: maxAmount, maxInvocations
NotAfter uint64 // Block height after which the capability expires (0 = no expiry)
}

CapabilityLimits {
MaxAmount *big.Int // Maximum token amount per invocation (for token actions)
MaxInvocations uint64 // Maximum number of times this capability can be used
}

Action Types​

ActionScope
contract:invokeCall a smart contract function
token:transferTransfer fungible tokens
token:mintMint new token supply
token:burnBurn token supply
nft:transferTransfer an NFT
data:submitSubmit arbitrary data transactions
wallet:createCreate child wallets
*Wildcard: all actions (use with caution)

The Resource field narrows the scope to a specific contract address, token address, or wallet address. Use "*" only when the action must apply globally; this is an anti-pattern for agent wallets (see Anti-Patterns).


How Capabilities Are Enforced​

Capability checks run at transaction execution time (block processing), not at submission time:

Transaction submitted to pool
↓
Authorization pre-screen (ancestry, blacklists)
↓ [capabilities NOT checked here]
Transaction enters pending pool
↓
Block proposed, transactions ordered
↓
Block execution: for each transaction:
1. Is the sender wallet capability-restricted?
2. Does the transaction action match any held capability?
3. Is the capability's resource "*" or matches the target?
4. Is current block height ≤ capability.NotAfter?
5. If MaxInvocations set: has invocation count been exceeded?
6. If MaxAmount set: does the transaction amount exceed the limit?
→ All checks pass → execute
→ Any check fails → reject transaction

Important: Because capability expiry is checked at execution time, a transaction submitted before the expiry block may still fail if it is not included in a block until after the expiry. Design capability NotAfter values with sufficient buffer.


Attaching Capabilities to a Wallet​

Capabilities are attached via ALTER_WALLET:

{
"walletAddress": "<agent-wallet-address>",
"capabilities": [
{
"action": "contract:invoke",
"resource": "5c83c81199820b91cf759a69f5ede919ea0568be34ec6e26ac8adf4173cbf01048451014b48cc4557474fde91f589c95bf4dd34349d96a83d7f0e15a7b664b83",
"limits": {
"maxInvocations": 1000
},
"notAfter": 200000
}
]
}

Inheritance Constraint​

When creating a child wallet, the child's capabilities must be a strict subset of the parent's capabilities. A parent wallet cannot grant a child permission the parent itself does not hold. This prevents privilege escalation through the hierarchy.


Capability Patterns for Agent Wallets​

Minimal Agent (Single Contract, Bounded)​

[
{
"action": "contract:invoke",
"resource": "<specific-contract-address>",
"limits": { "maxInvocations": 500 },
"notAfter": 50000
}
]

This agent can call exactly one contract, at most 500 times, before block 50000. After block 50000, all transactions are rejected even if invocations remain.

Token-Spending Agent (Bounded Amount)​

[
{
"action": "token:transfer",
"resource": "<specific-token-address>",
"limits": { "maxAmount": "1000000" },
"notAfter": 100000
}
]

This agent can transfer tokens from a specific token contract, but the cumulative transferred amount cannot exceed 1,000,000 units.

Multi-Action Agent (Research + Data)​

[
{
"action": "contract:invoke",
"resource": "<research-contract>",
"limits": { "maxInvocations": 200 },
"notAfter": 75000
},
{
"action": "data:submit",
"resource": "*",
"limits": { "maxInvocations": 500 },
"notAfter": 75000
}
]

This agent can invoke one contract and submit arbitrary data, both bounded by invocation count and block-height expiry.

Renewing Capabilities​

When a capability approaches its notAfter block or maxInvocations limit, the parent wallet submits a new ALTER_WALLET to replace the capability set. This is the explicit renewal mechanism; no auto-renewal occurs.


Capabilities vs. Auth Groups​

DimensionAuth GroupsCapabilities
ScopeWallet-level administrative CRUDAction-level, resource-specific
Who uses themBoth human and agent walletsPrimarily agent wallets
ExpiryNo expiryBlock-height expiry
MeteringNo meteringInvocation count, amount limits
Effect when presentAdds permissionRestricts to whitelist only

Rule: If a wallet has any capabilities assigned, it can only perform actions covered by those capabilities. Auth groups no longer govern its action scope; only capabilities do. Use auth groups for administrative access; use capabilities for operational scope.


Monitoring Capability Status​

The node exposes endpoints for querying wallet capability state:

GET /chains/{chainId}/wallets/{address}/capabilities
→ Returns current capabilities, remaining invocations, expiry status

GET /chains/{chainId}/wallets/{address}/capabilities/status
→ Returns per-capability health: active | expiring_soon | exhausted | expired

Use these endpoints to monitor agent wallets and renew capabilities before they expire.

For common mistakes with capabilities (over-scoping, single-node verification, capability sprawl) see Anti-Patterns.