Skip to main content

Agent mandate

An agent mandate confines a software agent's smart wallet on chain: the calls it may make, the amounts it may move and the date it stops.

An agent mandate is the on-chain record that confines a software agent's smart account: the calls it may make, the amounts it may move and the date it stops. The account is a Smart Wallet whose only validator is the mandate validator, which checks every user operation against the mandate before a single call in that operation runs. This page covers what that contract enforces. Agents and mandates covers how the organisation owner creates, changes and revokes agents.

Software that funds payouts on a schedule or administers assets acts through a key the platform holds for it. A limit enforced only in the agent's own software is a limit the agent's key can lift, so the mandate puts the limit in the account. A call outside the mandate makes validation revert, so the account never executes it. The platform's bundler simulates validation before it submits anything, so it refuses the operation without submitting it and returns the validation failure.

The organisation's operations account writes the mandate and administers it. Its signing address is a managed custody key the platform signs with on the organisation owner's verified request. The agent's key acts inside the mandate and cannot widen it, because the validator refuses every call the key makes to the account itself or to the validator.

What a mandate holds

An installation names three things: the agent's signing key, the address that administers the mandate, and the mandate itself. The mandate carries the limits.

Part
What it bounds
expiresAt
The unix timestamp the mandate stops at. Zero never expires at the contract, and the platform requires a future time.
periodSeconds
The length of the spend window, counted from the block that wrote the mandate. The chain refuses zero, and the platform requires 60 or more seconds.
nativeLimitPerTransaction
The most native coin one user operation may carry. It defaults to zero, so a mandate that says nothing about native value allows none.
nativeLimitPerPeriod
The most native coin one window may carry. It defaults to zero as well.
targets
Each contract the agent may call, and per contract the function selectors it may use there.
assets
Each ERC-20 the mandate meters, with a ceiling for one user operation and a ceiling for one window.

An installation with a zero signing key, a zero owner, a zero asset, a zero-length period, a repeated target or asset, or a mandate over the module's caps is refused, so a mandate the module could not enforce is never stored. The module meters at most 32 targets, 16 selectors per target and 16 assets, which bounds the gas a validation spends reading a mandate.

Who grants it

Two roles act on a mandate account. The account's owner is the agent's signing key, which authorises the account once, at creation. The mandate owner is the operations account's signing address, which administers the mandate for as long as the mandate lives. The platform creates the account and writes the mandate at the same moment.

  • The agent's signing key, as the account's owner, signs an account creation authorisation. The signed digest binds the validator, the salt, a deadline and the installation data, so anyone may submit the authorisation before its deadline and nobody can change what it installs.
  • The account factory refuses a validator that is not in its module registry. The deployment registers the mandate validator there, and a mandate account names it explicitly because the factory's default is the single-signer validator.
  • The installation data carries the agent's signing key, the mandate owner and the mandate. The account installs the validator with that data.
  • setMandate, revoke and transferMandateOwner accept the account's mandate owner only. Any other caller is refused with MandateOwnerOnly, the account itself included. The Console and the API call these functions for the organisation owner.

What the agent may call

A mandate account runs one call data shape, execute(bytes32,bytes) as a single call or a batch, and refuses the rest, which keeps the account's own administration out of the agent's reach.

  • The call data that runs is execute(bytes32 mode, bytes executionCalldata) with the single or batch call type. Other call data is refused with MandateCallDataRefused before any spend is recorded. That includes execute(address,uint256,bytes), which the account does not implement. So installModule, uninstallModule and the account's own setters never run from a user operation the agent signs.
  • A call must name a target the mandate lists, with a selector that target's entry lists. Anything else is refused with MandateTargetRefused. A call carries at least a selector, so the agent cannot send plain native coin with empty call data.
  • A call whose target is the account itself or the validator is refused with MandateSelfCallRefused, so the agent's key reaches neither the account's modules nor the mandate that confines it.
  • A call type other than single or batch is refused with MandateCallTypeRefused. A delegate call falls under that refusal, so a user operation cannot make the account execute in another contract's storage.
  • On a metered target only transfer, transferFrom and approve may be listed, because only those carry an amount the module can read. Any other selector on a metered target is refused at install with MandateAssetSelectorRefused.
  • Listing one of those three on a target the mandate does not meter is refused at install with MandateAssetUnmetered. Any other selector on an unmetered target is allowed, and the tokens it moves are not counted, although native value is still metered. List only the calls the agent needs.

What an amount may be

An amount is counted against two ceilings: one per user operation and one per window.

  • The module reads the amount from the call's own arguments, adds it to what the account spent in the window, and compares the operation's total with the per-operation ceiling. The calls in a batch share that ceiling, so splitting a payment across calls does not raise it. Exceeding the ceiling reverts with MandateAssetPerTransactionExceeded or MandateNativePerTransactionExceeded.
  • The same total is compared with the window's ceiling, which reverts with MandateAssetPerPeriodExceeded or MandateNativePerPeriodExceeded.
  • Native value is metered against its own pair of ceilings. A call that carries value on a mandate with zero native ceilings is refused.
  • approve counts its full amount when it is called, whether or not the spender later uses it. Gas is not metered.
  • Windows are fixed slices of periodSeconds counted from the block that wrote the mandate, and each window starts at zero. An agent can therefore spend a full period ceiling at the end of one window and again at the start of the next. Writing a new mandate opens a new count at once, so the previous mandate's counters never limit the new one.
  • A refused operation records no spend. Spend is recorded during validation, so an operation that passes validation and then reverts during execution still counts against both ceilings.

How one user operation is checked

The validator checks the mandate before it looks at the calls. It answers a refusal in one of two ways:

  • A signature that does not recover to the mandate's signing key returns the ERC-4337 validation failure, SIG_VALIDATION_FAILED, as the standard requires, and the EntryPoint drops the operation.
  • Every other refusal reverts validation with a named error, so the account never executes. The bundler sees the failure when it simulates the operation and does not submit it.

Two further rules apply:

  • The account signs no ERC-1271 message: its isValidSignatureWithSender answers 0xffffffff for every request. A signature check carries no target, selector or amount, so accepting one would move assets without passing the ceilings.
  • The mandate is read for the account that submits the user operation, so a validation is always checked against the account that would execute.

How it ends

A mandate ends on its date or by a revocation, and administrative changes stop with it.

  • Date. After expiresAt has passed, a user operation reverts with MandateExpired. A record of zero means the mandate never expires, which only a mandate written to the contract outside the platform can hold. Until a revocation, the mandate owner can set a new expiry with setMandate, which also writes the rest of the mandate again and starts a new count. The signing key is fixed at installation and setMandate does not change it.
  • Revocation. The mandate owner calls revoke, and it takes effect in the block that mines it: the next user operation reverts with MandateRevoked before any call in it runs. A second revoke returns without changing the recorded timestamp.
  • Revocation is final. After a revoke, setMandate and transferMandateOwner are refused with MandateRevoked, so a revoked mandate cannot be replaced or handed on. The revocation stays visible for the life of the account, and a new mandate goes on a new account.
  • No uninstall. The account refuses to remove its last validator, and installing another validator needs a call the mandate refuses, so a mandate account keeps its validator. The mandate ends only by its date or by revocation.
  • Transfer of administration. The current mandate owner moves administration to another address with transferMandateOwner. The zero address is refused with MandateZeroAddress.

Reading a mandate

Five views answer what an account's mandate allows, what it has spent and whether it is still live. They need no key and no particular caller. The agent's page in the Console and GET /api/v2/agents/{agentId} show the same facts.

View
Answer
mandateOf(account)
The signing key, the mandate owner, the expiry, the revocation timestamp, the period length and both native ceilings.
allowedTargets(account)
Each target with the selectors its entry lists.
meteredAssets(account)
Each metered asset with its per-operation and per-window ceilings.
spentThisPeriod(account, asset)
What the account has spent of one asset in the current window. Pass the zero address for native value. It answers zero when the account records no period.
isRevoked(account)
Whether the mandate is revoked.

How it fits the platform

  • The mandate validator is an ERC-7579 validator module, so the account factory accepts it like any other registered validator.
  • The validator reports that no single key may manage the account directly. The agent's key therefore cannot reach the account's sensitive setters, even though the platform holds that key.
  • The account's code comes from the platform directory, so the directory administrator's implementation upgrade is the one change that reaches a mandate account without passing through its validator.
  • The account is the sender on chain, so roles, compliance checks and the audit trail name the account's address, not the agent's key.
  • The hosted MCP server runs a tool call whose execution is mandate as the agent named in agentId, from the agent's account and inside its mandate, so every refusal on this page applies. When agentId names no agent of the organisation, the server refuses the call, executes nothing and prepares no draft. See Drafts, inbox and mandates.

Read Agents and mandates for how an organisation owner creates agents, edits their mandates and uses the kill switches, Smart Wallet for the account a mandate is installed on, User operations for how a signed operation reaches the chain, and Drafts, inbox and mandates for the app path that asks for a mandate.

Was this page helpful?