Built for **Total Recall — Tatum × Walrus, Consumer Finance Apps track.
Demo video (2:39): https://youtu.be/sRa2cWtP8ok ·
Code: https://github.com/fadillaoegi/mandate-walrusxtatum
The problem: every agent starts from zero
AI agents are starting to pay for things on their own. With protocols like x402, an agent can call an API, get an
HTTP 402 Payment Required, pay a few cents in USDC and receive the data, all without a human in the loop.
That is great until you hire your second agent, or restart the first one. Each agent starts from zero:
- it doesn't know your budget (fifty cents per item? two dollars a day?),
- it doesn't know which vendors let you down last week,
- it doesn't know what it already bought, so it happily buys the same report twice.
So you end up with two bad options: approve every purchase by hand, or let agents spend without any real control.
The idea: your money rules as portable memory
Mandate keeps the owner's spending rules and purchase history as memory on Walrus. Any agent you authorise with
a delegate key reads the same memory, in any session. Agents pay with x402 on Sui, and Mandate independently
checks each signed transaction's execution status through Tatum. A pending receipt reserves the purchase and
budget until the chain result is known, so an unavailable read does not invite a duplicate payment.
In one sentence: your money rules, remembered by every agent you hire.
What it looks like
The dashboard walks through six steps (you can watch them in the video):
- Set the rules. Max $0.50 per item, $2.00 per day, ShadyData blocked. Saving writes a rule memory to Walrus.
- The first agent has to ask. The Research Agent has no history, so every purchase lands in the owner inbox. I approve OpenStats (paid over x402, verified via Tatum), reject DataHarbor as too expensive, and reject + flag QuickFacts for stale data. Each answer becomes a memory.
- A new session remembers. Working memory is wiped. The agent recalls from Walrus and decides five cases alone: a duplicate is refused, OpenStats is trusted, QuickFacts is flagged, DataHarbor is above my learned price ceiling, ShadyData is blocked. Zero questions to me.
- Another agent obeys too. The Shopping Agent was never taught anything. It reads the same memory through the delegate key and follows the rules immediately.
- Never trust the merchant. A greedy merchant's 402 asks $0.25 for an item I approved at $0.15. The agent refuses to sign, nothing is charged. At the right price it pays, and Mandate reads the transaction through Tatum: the chain, not the merchant, determines the execution status.
- The owner controls access. The dashboard's Revoke action clears sessions and blocks agents in this application. It does not remove the delegate from the Walrus dashboard. Actual delegate removal is a separate operation; Mandate also cuts off sessions when a normal relayer request returns HTTP 401.
How Walrus Memory is used
Every meaningful thing that happens is written as one immutable memory event with the Walrus Memory SDK
(@mysten-incubation/memwal) against the Mainnet relayer:
| Event | Example |
|---|---|
mandate_set |
max $0.50 / item, $2.00 / day, blocked: ShadyData |
owner_verdict |
rejected DataHarbor $0.45 — too expensive |
vendor_flag |
QuickFacts flagged: stale data |
purchase_recorded |
bought os-rainfall-yield from OpenStats for $0.25, tx 9RtU…
|
purchase_status |
that tx is verified (or failed) according to Tatum |
Each memory is a readable sentence plus a small JSON payload, so it works both for semantic recall and for exact
parsing:
[mandate:v1] Owner rejected $0.45 purchase of dh-market-deep-dive from DataHarbor: too expensive || {"kind":"owner_verdict",...}
When a session starts, the agent recalls the namespace and rebuilds its knowledge by folding events in time order.
Because Walrus memories are append-only, a correction is never an edit: it is a new event (for example a
purchase_status: failed after the chain disagreed with a receipt).
Two practical lessons:
-
Writes are slow, so the app is asynchronous. A Walrus write takes about 20 seconds. The agent knows a new memory
immediately; the write runs in the background (
pending → stored) with an idempotency key, so retries never duplicate the same write. A new session waits for pending writes, then recalls from Walrus. The recorded run stored all 13 memories successfully; a failed write remains visibly failed and can be retried. -
Access rejection needs no polling. If a normal recall or write returns
401, Mandate clears active sessions. An unregistered key was tested and rejected. Removing a previously registered delegate was not tested, and an already-running process cannot notice an external removal until its next authenticated request.
In the recorded run, 13 memories were stored on Walrus Mainnet in namespace mandate-demo-run10.
How Tatum is used
Mandate uses Tatum as its source of truth for payments. After an agent pays, Mandate does not believe the
merchant's X-PAYMENT-RESPONSE; it reads the transaction from Sui through Tatum's gRPC gateway and stores that
execution status in memory. Audit receipts re-checks each remembered transaction's status against the chain;
when a known chain result disagrees with memory, a correction event is written.
A practical lesson: the first JSON-RPC read path did not work in our environment, so we implemented a native
gRPC adapter. Tatum's Mainnet and Testnet gRPC gateways worked in the recorded checks, with transaction reads
around 50–260 ms. The reader sits behind a ChainReader port, keeping the application independent of the transport.
How agents pay: x402 with guardrails
Payments use the x402 exact scheme on Sui (@t2000/sui-x402): the merchant answers HTTP 402 with terms, the agent
signs a gasless USDC transfer, and the merchant settles it. Before signing, Mandate's agent checks the merchant's
terms against what the policy approved:
- the amount must equal the approved price,
- the recipient must be the vendor's known address,
- the network must match.
Anything else is refused before a signature exists. One challenge allows one payment, so replays are refused too.
In the recorded run, four x402 payments settled on Sui Testnet and each was verified through Tatum, for example
Ht7Du4hHcm3e9DGA2bDMNttdjuPK3BedPFBYDM9aYdeE (the $0.15 purchase made right after the greedy 402 was refused).
Learning over time
"Learning" in Mandate is deliberately simple and explainable: it is deterministic policy over remembered events,
not a model guessing.
- An approval makes a vendor trusted within limits.
- A rejection marked too expensive sets a shared price ceiling: the current policy rejects later requests at or above the lowest price rejected this way, across vendors.
- A flag marks a vendor as bad for every agent.
- A receipt makes the item a duplicate, unless the chain later says the payment failed.
The recorded dashboard comparison shows 14 purchase requests and 3 owner prompts. Its no-memory baseline
evaluates each request with empty knowledge, which produces an owner prompt for each request; this is a policy
comparison, not a separate live control experiment. Later sessions and the second agent use recalled events to
decide without asking again. The current demo tasks are scripted and policy decisions are deterministic.
Architecture in one paragraph
Clean Architecture in TypeScript: a pure domain (policy and memory events) behind ports (MemoryStore,
ChainReader, PaymentRail, DelegateAccess), with adapters for Walrus Memory, Tatum gRPC, the x402 rail and a
labelled demo merchant. Express API, React + Vite + Tailwind dashboard in a pixel style, PostgreSQL for app data only.
37 automated tests run without any keys (npm install && npm test).
Honest limits
- Payments in the demo run on Sui Testnet (test USDC). Memories are on Walrus Mainnet.
- The merchant and its vendors are our own demo services, clearly labelled; agent task plans are scripted.
- The managed Walrus Memory relayer sees plaintext, so the demo uses sample data only.
- Chain verification currently checks execution status for the signed digest. Independently matching the transfer's token, amount, recipient and payer against the approved purchase remains a hardening task.
- Recall is semantic and bounded (up to 100 hits per query, with extra queries at the cap), so this is a small demo rather than a guarantee of complete recovery for an arbitrarily large purchase history.
What's next
A language model as the agent's planner (it may only suggest purchases; the policy still decides), more vendors,
spending reports for the owner, and moving the payment rail to Mainnet once it is safe to do so.
Watch the demo: https://youtu.be/sRa2cWtP8ok
Read the code: https://github.com/fadillaoegi/mandate-walrusxtatum
On-chain evidence: Recorded verification and guided walkthrough
Built for Total Recall — Tatum × Walrus.
Top comments (0)