Verifiable Policy Controls for Autonomous Financial Agents
Give agents authority.
Not unlimited authority.
MANDATE sits between an AI agent and financial execution. Every action must prove that it complies with the user's predefined policy before it can reach the execution layer.

The problem
Agents need authority, but not unlimited authority
AI agents are increasingly able to place trades, initiate payments, manage portfolio positions, and interact with protocols directly. Prompt instructions, backend permissions, and human approvals alone are not enough — enforcement happens at a layer the agent can influence, that a third party cannot independently verify, or that requires trusting a centralized operator.
| Risk | Example | Without MANDATE |
|---|---|---|
| Prompt injection | Malicious input instructs agent to place an oversized order | Instruction may override original policy |
| Strategy bug | Agent miscalculates position and submits an oversized trade | No circuit-level constraint |
| Unauthorized market | Agent selects an asset not on the approved list | No enforceable whitelist |
| Policy manipulation | Local execution limits differ from registered policy | No on-chain commitment to verify against |
| Compromised runtime | Agent output is tampered with before submission | No cryptographic receipt of compliance |
| State inconsistency | Off-chain portfolio data is stale or falsified | No state-root verification |
Key insight
Definition
What MANDATE is
A verifiable policy layer placed between an AI agent and a financial execution rail. It takes a user-defined mandate, registers a cryptographic fingerprint of it on-chain, and requires every execution action to carry a zero-knowledge proof that it satisfies those rules.
MANDATE is
- + Policy enforcement infrastructure
- + An agent authorization layer
- + A proof generation and verification path
- + An integration layer for execution systems
- + Verifiable delegated authority
- + A protection layer for autonomous agents
MANDATE is not
- − An AI model or trading strategy
- − A general-purpose wallet
- − A replacement for MoonPay
- − A centralized risk API
- − A simple transaction simulator
- − A standard multi-signature scheme
Core principle
How it works
Five steps, one enforcement point
MANDATE allows the agent to act autonomously, but only inside a policy the agent cannot rewrite.
User policy
Policy commitment
AI agent action
MANDATE SDK
ZK compliance proof
Valid proof
Contract verifies → order submitted to the batch auction.
Invalid proof
Proof cannot be generated → order rejected, nothing on-chain.
- 1 · User
Define the mandate
Approved markets, size limits, position caps, and loss limits — set once.
- 2 · MANDATE
Register policy + session key
A policy commitment is registered on-chain; the agent is issued a scoped session key.
- 3 · Agent
Propose an order
The agent decides an action based on its own logic or a user instruction.
- 4 · MANDATE
Generate a compliance proof
The order is checked against the policy; a valid proof is produced only if every constraint holds.
- 5 · System
Execute — or reject
Only orders accompanied by a valid proof are submitted to the batch auction.
Execution flow visualization
A visualization of the documented local MVP demo — not a live transaction simulator.
Agent proposes order
Check against policy
Generate ZK compliance proof
Submit to batch auction
- Side
- buy
- Quantity
- 250
- Limit price
- 3,500
- Policy limit
- 1,000,000
- Notional (qty × price)
- 875,000
- Proof generated
- Yes
Result
Submitted
Formula: order notional = quantity × limit price.
Architecture
From agent call to on-chain proof
The agent interacts only with the MCP server. Proof generation and on-chain commitment happen entirely inside the SDK and contract layer.
MoonPay Agent
user-facing
MANDATE MCP Server
3 tools only
MANDATE SDK
proves + submits
Noir Compliance Proof
Noir / UltraHonk
Registry Contract
policy + session key
Auction Contract
proof-gated, on-chain
MandateAccount
funds + escape hatch
Sequencer
epoch loop, state
Local Ethereum Chain
Anvil
The agent interacts only with the MCP server. Proof generation and on-chain commitment happen entirely inside the SDK and contract layer.
Security model
Defense in depth
Eight independent layers. Failure at any single layer is sufficient to prevent an order from being submitted.
- 1
Schema validation
Malformed inputs are rejected before any processing (Zod-validated MCP tool inputs).
- 2
Session-key authorization
The agent must hold a registered key scoped to a specific mandate. The session key has zero authority over funds — no MandateAccount function is callable by it.
- 3
Policy-commitment verification
MandateClient checks the local plaintext policy opens the on-chain registered commitment before ever proving — refusing to prove with a stale or wrong mandate.
- 4
Portfolio-state-root verification
Confirms the sequencer-reported portfolio matches the on-chain committed state root. The agent cannot fabricate a healthier portfolio. The sequencer/operator is trusted to post correct state roots after clearing — state-transition proofs are deferred.
- 5
Zero-knowledge compliance proof
The Noir/UltraHonk circuit cryptographically proves all policy constraints are satisfied simultaneously — whitelist, notional, position, daily loss, and breaker mode.
- 6
On-chain contract verification
The bb-generated HonkVerifier re-verifies the proof on-chain. The five public inputs (policy commitment, state root, order commitment, epoch, breaker bit) are assembled by the contract from registry state, not taken from calldata.
- 7
Batch-auction epoch commitment
An order is committed only within the valid commit window of the current epoch; the order commitment binds the epoch, so a proof cannot be replayed against a different one.
- 8
Owner escape hatch
MandateAccount provides an owner-only withdrawal that depends on nothing — not the agent, sequencer, proof, or auction being alive.
Failure at any single layer is sufficient to prevent an order from being submitted.
Trust assumptions and current limitations
- This system is experimental and has not undergone an external security audit.
- It runs entirely on local infrastructure (a local Anvil chain) — there is no public testnet or mainnet deployment.
- All keys and values are test-only. This is not production custody infrastructure.
- Session keys and policy salts are secrets and must never be committed to version control.
- Do not give the same agent unrestricted wallet, swap, transfer, or trading tools alongside mandate_submit_order — proof-gating only works if it is the only execution path.
Developers
Prove and submit an order in a few lines
@0xgks/mandate-sdk — the same MandateClient the MCP server and the demo scripts use internally. Full reference at /docs/sdk.
1. Install
npm install @0xgks/mandate-sdk2. Import and configure
import { MandateClient } from "@0xgks/mandate-sdk";
import type { MandateClientConfig, PolicyParams, MandateOrder } from "@0xgks/mandate-sdk";3. Construct the client
// Construct the client's config from environment variables.
// agentId is Poseidon2(session pk, salt) — the agent's on-chain identity.
const policy: PolicyParams = {
whitelistRoot: BigInt(process.env.MANDATE_WHITELIST_ROOT!),
maxOrderNotional: BigInt(process.env.MANDATE_MAX_ORDER_NOTIONAL!), // e.g. 1_000_000n
maxPosition: BigInt(process.env.MANDATE_MAX_POSITION!),
maxDailyLoss: BigInt(process.env.MANDATE_MAX_DAILY_LOSS!),
policySalt: BigInt(process.env.MANDATE_POLICY_SALT!),
};
const config: MandateClientConfig = {
agentId: process.env.MANDATE_AGENT_ID as `0x${string}`,
sequencerUrl: process.env.MANDATE_SEQUENCER_URL ?? "http://127.0.0.1:8787",
rpcUrl: process.env.MANDATE_RPC_URL ?? "http://127.0.0.1:8545",
auctionAddress: process.env.MANDATE_AUCTION_ADDRESS as `0x${string}`,
registryAddress: process.env.MANDATE_REGISTRY_ADDRESS as `0x${string}`,
sessionPrivateKey: process.env.MANDATE_SESSION_KEY as `0x${string}`,
policy,
circuitDir: process.env.MANDATE_CIRCUIT_PATH ?? "./circuits/policy_check",
};
const mandate = new MandateClient(config);4. Prove and submit an order
// The order an agent/strategy hands to the SDK — human-friendly,
// string-encoded fields (see MandateOrder in types.ts).
const order: MandateOrder = {
market: "1", // circuit market id, as a decimal string
side: "buy",
amount: "250", // whole circuit units, as a decimal string
limitPrice: "3500", // whole circuit units, as a decimal string
};
const result = await mandate.proveAndSubmit(order);
// result: { epoch, orderCommitment, proof, publicInputs, txHash }
console.log(`submitted in epoch ${result.epoch}: ${result.txHash}`);5. Handle a policy rejection
import {
MandateViolationError,
PolicyMismatchError,
PortfolioMismatchError,
EpochClosedError,
} from "@0xgks/mandate-sdk";
try {
await mandate.proveAndSubmit({
market: "1",
side: "buy",
amount: "10000", // 10,000 * 3,500 = 35,000,000 notional
limitPrice: "3500",
});
} catch (err) {
if (err instanceof MandateViolationError) {
// The order cannot be proven: nargo execute failed on a circuit
// assertion (e.g. "order notional exceeds mandate maximum").
// Nothing was submitted on-chain. err.reason is the bare circuit
// assertion text; err.order is the rejected order.
console.error(err.reason);
} else if (err instanceof PolicyMismatchError) {
// Local plaintext policy doesn't open the on-chain registered
// commitment — refusing to prove with a stale or wrong mandate.
} else if (err instanceof PortfolioMismatchError) {
// Sequencer's reported state root disagrees with the on-chain
// anchored root — refusing to prove against untrusted state.
} else if (err instanceof EpochClosedError) {
// The commit window closed while proving; not submitted.
}
}MCP integration
A deliberately narrow tool surface
The MANDATE MCP server exposes exactly three tools. Limiting the tool surface area is itself a security property — an agent cannot do something the server doesn't expose, regardless of what it's instructed to attempt.
mandate_get_epoch
Get MANDATE auction epoch state
mandate_get_portfolio
Get MANDATE portfolio
mandate_submit_order
Submit a MANDATE proof-gated order
Comparison
Where MANDATE sits
MANDATE is not intended to replace human approval or wallet-level security. It complements them — the core difference is independent verifiability.
| Approach | Strength | Limitation |
|---|---|---|
| Prompt instructions | Easy to implement, low overhead | Model may ignore, misunderstand, or be manipulated |
| Human approval | Strong oversight, high trust | Slows or prevents autonomous operation |
| Backend rules | Practical, flexible, widely used | User must trust the operator; not independently verifiable |
| Wallet spending limits | Useful financial boundary | Cannot express strategy-level or multi-variable rules |
| Multisig | Strong ownership model | Not designed for high-frequency autonomous execution |
| MANDATE | Verifiable, programmable, proof-based | Current MVP is local and experimental |
Research
The larger protocol vision
MANDATE.pdf describes a long-term protocol: hidden strategies and portfolios, public compliance proofs, pre-committed risk mandates, proof-carrying agent history, and privacy-preserving reputation. None of this is implemented yet — the current MVP demonstrates only the core proof-gated enforcement loop.
Current working MVP
Real Noir/UltraHonk proofs, on-chain policy commitments, a local batch auction, and a working MCP integration with the MoonPay Agent.
Long-term protocol design
Threshold-encrypted order flow, decentralized keyper committees, Nova-style folded reputation credentials, proved state transitions, and cross-chain settlement via ERC-7683.
Documentation
Two documents, two purposes
Protocol Design Document
MANDATE.pdf
A research-oriented protocol design covering private agent execution, verifiable mandates, cryptographic primitives, market structure, settlement, and privacy-preserving reputation.
Product Overview
mandate-product-overview.pdf
A practical walkthrough of the working local MVP, SDK, MCP integration, architecture, security model, demo policy, and verified test results.