Mandates
Bounded authority: an allocator arms ETH behind an agent, inside hard limits the contract checks on every execution.
What a mandate is
A mandate lets an agent spend your ETH on your behalf, but only inside limits you set and the contract enforces. You (the allocator) arm ETH into the MandateVault contract and name an agent (the originator). The agent decides when and what to buy. It can never withdraw your ETH, move it anywhere else, or keep the tokens: every fill sends tokens straight to your wallet.
It is the standing version of a cluster. Instead of approving each trade, you approve a policy once, and every execution the agent runs clears all participating mandates together at one uniform price, the agent's own size included.
The limits
| Limit | Onchain field | What it stops |
|---|---|---|
| Per-trade cap | perExecWei | Any single execution from taking more than this from your mandate |
| Daily cap | dailyCapWei | A busy day from spending more than this. Must be at least the per-trade cap. Resets 24 hours after the current day window started |
| Max impact | ceilingBps | Your ETH joining an execution that allows more price impact than you accept (10 to 2,000 bps) |
| Expiry | expiry | The mandate outliving your intent. Unix seconds; 0 means no expiry |
| Armed balance | balance | The agent ever touching more than you put in |
Limits are checked by the contract on every execution, per mandate. A mandate that would breach any of them simply contributes less, or nothing, to that execution.
How an execution uses a mandate
Wants 0.30 ETH
zero if revoked, expired, wrong originator, or its ceiling is stricter than the execution's
Admitted 0.24 ETH
at an 80% fill ratio; the rest stays armed
- The originator calls
execute(market, ceilingBps, ids, minTokensOut)with up to 64 mandate ids in strictly increasing order and, optionally, its own ETH.minTokensOutis a floor on the whole execution's fill. - For each mandate the vault computes what it can contribute: the smallest of the per-trade cap, what is left of the daily cap, and the armed balance. It is zero if the mandate is revoked, expired, names a different originator, has a stricter ceiling than the execution, is bound to a different market, or the pre-trade spot is above its price cap.
- The venue reports capacity under the execution's ceiling. The originator's own ETH is admitted first; mandates share the remaining room by one common fill ratio.
- The vault makes one venue buy, takes the 10 bps fee, and transfers tokens to every allocator and the originator in proportion to what each put in: one uniform price for all.
Each fill emits a Filled event with your mandate id, the ETH taken and the tokens sent. The API groups them into MandateExecution records.
Arm, top up, revoke
| Action | Contract call | Notes |
|---|---|---|
| Arm | arm((originator, perExecWei, dailyCapWei, ceilingBps, expiry, market, maxSpotWad)) with ETH | Creates the mandate with your ETH as its balance. market binds it to one market (zero address = any market the router accepts); maxSpotWad skips it when spot is above that price (0 = no cap) |
| Top up | topUp(id) with ETH | Adds to the balance. Limits stay as armed |
| Revoke | revoke(id) | Instant and unconditional. The whole remaining balance comes back in the same transaction. A revoked mandate can't be reactivated; arm a new one |
There is no operator switch that can pause revocation or move armed ETH. Only you can revoke, and only an execution by the originator you named can spend, inside your limits.
Doing it over the API
The API builds the transaction; your wallet signs it. Amounts are decimal ETH strings. The response is a PreparedTx with to, data and value.
curl -X POST $NOFOMO/api/v1/tx/arm \
-H 'content-type: application/json' \
-d '{
"originator": "0xAgent...",
"amountEth": "1.0",
"perExecEth": "0.1",
"dailyCapEth": "0.3",
"ceilingBps": 300,
"expiry": 0,
"market": "0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC",
"maxSpotEth": "0.09"
}'POST /api/v1/tx/top-up takes { mandateId, amountEth } and POST /api/v1/tx/revoke takes { mandateId }. Read your mandates with GET /api/v1/mandates?allocator=0x… and their fills with GET /api/v1/executions?allocator=0x…. Full details in the API reference.
What to watch
A mandate bounds how much an agent can spend and how much impact it can accept. It does not judge whether the agent picks good assets. An originator could also hold the same token elsewhere and benefit from buying it with mandate capital. Watch its history (GET /api/v1/agents/0x…), start with small caps, and use expiry.