Mandate — Policy, Caps & Venues
The mandate system defines what the agent is allowed to do. It's enforced entirely on-chain — the agent cannot bypass it regardless of what the backend computes.
Merkle Mandate
The vault stores a Merkle root that represents the complete set of authorized actions:
Merkle root
├── Leaf: (venue_address, instruction_discriminator, disc_len)
├── Leaf: (venue_address, instruction_discriminator, disc_len)
└── ...Every execute instruction must include a Merkle proof showing the target venue+instruction is in the authorized set. If the proof doesn't verify against the stored root → transaction reverts with VenueNotAllowed.
What a Mandate Controls
| Control | Description |
|---|---|
| Allowed venues | Which protocol addresses the agent can interact with |
| Allowed instructions | Which specific functions can be called (by discriminator) |
| Per-action cap | Maximum amount per single allocation |
| Epoch cap | Maximum cumulative allocation within an epoch window |
| Position limit | Maximum total capital deployed across all venues |
Cap Enforcement
Caps are checked on-chain in the execute instruction:
// Pseudo-code (actual Anchor constraint checks)
require!(amount <= policy.per_action_cap, PerActionCapExceeded);
require!(epoch_total + amount <= policy.epoch_cap, EpochCapExceeded);
require!(total_deployed + amount <= policy.position_limit, PositionLimitExceeded);If any cap is exceeded → transaction reverts. The agent's amount is clipped to per-action cap before submission, but on-chain enforcement is the final backstop.
Changing the Mandate
Mandate changes are timelocked to prevent instant privilege escalation:
| Operation | Mechanism |
|---|---|
| Add new venue | queue_venue_add → timelock delay → execute or sentinel veto |
| Increase caps | queue_caps_increase → timelock delay → execute or sentinel veto |
| Lower caps | Sentinel can do immediately (never risky to reduce) |
| Remove venue | Sentinel can do immediately |
Roles
| Role | Powers |
|---|---|
| Owner (user) | Deposit, withdraw, emergency exit, queue changes |
| Executor (Heron keeper) | Execute allocations within mandate, deallocate |
| Sentinel | Lower caps, veto queued changes, pause — can NEVER raise or add |
The executor key is Heron's keeper — it's revocable by the owner and can only act within the Merkle-authorized scope.
Nonce & Expiry
Each execute instruction includes:
- Nonce — replay guard (each nonce usable once)
- Expiry — instruction expires after a timestamp (prevents stale decisions from executing)
Builder creates instruction at time T
→ Sets expiry = T + 60 seconds
→ If submission delayed > 60s → transaction reverts
→ Prevents stale market conditions from driving executionViewing Mandate State
GET /api/overview?chain=solana&wallet=...
→ Returns: mandate type, cap usage, position limits, allocations