Vault
Mandate & Policy

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

ControlDescription
Allowed venuesWhich protocol addresses the agent can interact with
Allowed instructionsWhich specific functions can be called (by discriminator)
Per-action capMaximum amount per single allocation
Epoch capMaximum cumulative allocation within an epoch window
Position limitMaximum 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:

OperationMechanism
Add new venuequeue_venue_add → timelock delay → execute or sentinel veto
Increase capsqueue_caps_increase → timelock delay → execute or sentinel veto
Lower capsSentinel can do immediately (never risky to reduce)
Remove venueSentinel can do immediately

Roles

RolePowers
Owner (user)Deposit, withdraw, emergency exit, queue changes
Executor (Heron keeper)Execute allocations within mandate, deallocate
SentinelLower 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 execution

Viewing Mandate State

GET /api/overview?chain=solana&wallet=...
→ Returns: mandate type, cap usage, position limits, allocations