SKUdesk
SKUdesk/Mandate
Robinhood Chain Testnet · Run + live chain reads
Next: Proof

Mandate and guardrails

The rules the vault enforces on the AI agent, and a way to try to break them.

What you are looking at

The owner gave the agent a mandate: spend at most $2,500.00 on one purchase and $5,000.00 of new commitments per UTC day, only accept trades that keep at least 18% margin, and only accept quotes the agent dates as less than 180 seconds old. A smart contract on Robinhood Chain Testnet enforces it, so the agent cannot talk its way past it. The daily cap limits new commitments, not cash-out: commitments do not expire, so ones made on earlier days can be funded later, and across a midnight up to twice the cap can be committed within 24 hours. Cash-out is bounded by the vault’s free balance, and each purchase by the per-purchase cap.

Margin is profit as a share of the sell price. A revert means the contract refuses the transaction and undoes it. Below you can read the live mandate, then play the agent and try to cheat the real contract.

The owner’s mandate

Per-trade cap$2,500.00The most the agent may spend on one purchase.
Per UTC day$5,000.00The most new buying it may commit to between 00:00 and 24:00 UTC. Commitments do not expire, so they can be funded later: this limits commitments, not daily cash-out.
Margin floor18%Net profit as a share of the sell price must be at least this.
Quote freshness180sA price the agent dates as older than this is refused. The agent supplies the date.
Showing the values saved from the run. Reading the contract on Robinhood Chain Testnet now…

Try to cheat the agent ONCHAIN · read-only calls every answer comes from the deployed contract

Pick a preset or change the numbers. Each change is sent to the real contract as a read-only test call, made as the agent’s public address. It tells you whether the contract would accept the commit and, if not, which rule stopped it and why. Nothing is broadcast and no money moves.

Your trade

the contract allows a bounded range
owner’s window is 180s
Off: the agent reports the figure its own library computes.

Held fixed so the quote is complete: duty $0.12, tax $0.08, procurement fee $0.05, payment fee $0.02, marketplace fee 8%, fulfilment $0.65, return reserve 2%, chain cost $0.04 per unit.

Runs your inputs through the real contract on Robinhood Chain Testnet via a read-only call. Nothing is sent: no wallet, no gas, no change to the vault.

ASKING

Sending the call to the deployed contract…

The guard catalogue 19 rules, in the contract FIXED SNAPSHOT · “triggered” marks are from the run

Each rule has an error name (what the contract throws), a plain explanation and the Foundry test that proves it. 6 of the 19 were also triggered in the run on the real chain; the rest are proved by the test suite. Only the product-identity check, whether a listing is really the same item, lives off-chain: the contract sees only a hash of it.

01Economics re-derived on-chain

MathMismatch

The contract recomputes net and margin from the quote. If the agent’s claim differs by even one cent it reverts and shows both numbers.

Proved by testLieAboutNetRevertsWithBothNumbers
Triggered in the run Agent claimed net $3.90 but the contract derived $2.61 from the quote. Rejected. The reverted transaction is on the chain: view the failed tx (opens the block explorer) ↗

02Per-trade cap

SpendCap

Spend is derived as landed cost × units, never supplied by the agent, then compared with the owner’s cap.

Proved by testPerExecutionCapRevertsWithValues
Triggered in the run Spend $2,636.00 exceeds the per-execution cap of $2,500.00. Rejected. Decoded from a read-only call as the agent address, not a mined transaction.

03Daily commitment cap

DailyCap

A running total of new commitments per UTC day; the agent cannot split a big purchase into many small ones to dodge it. It limits commitments, not cash-out: opportunities do not expire, so commitments banked on earlier days can be funded later. Across a midnight, up to twice the cap can be committed within 24 hours. What actually leaves the vault is bounded by the vault’s free balance; each lot is at most the per-trade cap.

Proved by testDailyCapRevertsAndResetsNextDay Covered by tests, not triggered in the run

04Quote freshness (TTL)

Stale

The age check uses the observation time the agent supplies, so it limits how stale the agent’s own claim can be, not the real age of the source data. The snapshot hash binds the data that was shown.

Proved by testStaleRevertsWithAgeAndTtl
Triggered in the run Quote is 781s old; the policy allows 180s. Rejected. Decoded from a read-only call as the agent address, not a mined transaction.

05No replay

Replay

The opportunity id is keccak256(productHash, quoteHash, snapshotHash), so the same id cannot be committed twice, and each commit is bounded by the per-trade and daily caps. The agent supplies the snapshot hash, so a new snapshot hash is a new id: the caps, not the id, limit how often the same quote can be committed.

Proved by testReplayRevertsEvenWithSameInputs
Triggered in the run This exact opportunity was already committed. Replay blocked. Decoded from a read-only call as the agent address, not a mined transaction.

06Quote commitment

BadQuoteHash

The agent commits a hash of the quote it used; submitting a different quote is refused.

Proved by testBadQuoteHashReverts
Triggered in the run The quote hash does not match the quote that was submitted. Rejected. Decoded from a read-only call as the agent address, not a mined transaction.

07No future timestamps

FutureObservation

The agent cannot claim an observation from the future to defeat the freshness check.

Proved by testFutureObservationReverts Covered by tests, not triggered in the run

08Margin floor

MarginTooLow

Net margin below the owner’s floor is refused. Rounding always favours caution.

Proved by testWeakMarginReverts Covered by tests, not triggered in the run

09Positive net only

NonPositiveNet

A loss-making opportunity is never committed.

Proved by testLossMakingReverts Covered by tests, not triggered in the run

10Input bounds

OutOfBounds

Every quote field and unit count has a hard upper bound, so arithmetic cannot overflow or wrap.

Proved by testQuoteFieldOutOfBoundsReverts Covered by tests, not triggered in the run

11Allowlisted payees only

PayeeNotAllowed

Escrow can only be released to addresses the owner approved. The agent cannot pay itself.

Proved by testPayeeMustBeAllowlisted
Triggered in the run Payee 0x3EC91B7dfF57403aE298e503FAe4f5815B4C1818 is not on the owner's allowlist; the agent cannot send escrow there. Rejected. Decoded from a read-only call as the agent address, not a mined transaction.

12Cannot pay more than escrowed

ExceedsEscrow

Payouts are capped by the lot’s remaining escrow.

Proved by testCannotPayMoreThanEscrow Covered by tests, not triggered in the run

13Allowlisted payers, real transfer

PayerNotAllowed

Settlement pulls the sale proceeds from the owner-approved payer as real tokens on chain, limited by the payer’s balance and allowance. The agent reports the amount, so the contract guarantees the accounting (proceeds paid in are real and counted), not that the reported sale price is true.

Proved by testSettleRequiresAllowlistedPayerAndRealTransfer Covered by tests, not triggered in the run

14Lots need a committed opportunity

UnknownOpportunity

No lot, and so no funds, without passing every check above first.

Proved by testMintRequiresCommittedOpportunity Covered by tests, not triggered in the run

15One lot per opportunity

OpportunityConsumed

A committed opportunity can be turned into one lot only.

Proved by testOpportunityMintsOnlyOnce Covered by tests, not triggered in the run

16Only free funds can move

InsufficientFree

The vault cannot fund a lot beyond its free balance, and the owner cannot withdraw escrowed money.

Proved by testFundRevertsWhenVaultUnderfunded Covered by tests, not triggered in the run

17Legal lot transitions only

BadTransition

A lot moves through a fixed state machine; skipping or reversing a step is refused.

Proved by testIllegalTransitionsRevert Covered by tests, not triggered in the run

18Roles

Unauthorized

Only the agent can act as agent; only the owner can change policy, payees or withdraw. The owner can only become the agent by replacing it with setAgent, which is on chain.

Proved by testOnlyOwnerAdminAndOnlyAgentActions Covered by tests, not triggered in the run

19Kill switch

Paused

The owner can pause every agent action instantly.

Proved by testPausedBlocksAgent Covered by tests, not triggered in the run

The vault’s Foundry tests are in packages/contracts/test; run forge test in packages/contracts to run them. They include fuzz tests, which try random inputs, and invariant tests that check value is never created or lost: tokens held always cover free funds plus escrow, and free plus escrow plus paid out equals deposits plus proceeds.