What you’ll build
- An understanding of the two checks in
AgentVault.pay: the daily limit and the recipient allowlist. - Foundry tests that move time forward to prove the limit resets at 00:00 UTC.
vault-status.ts, a script that shows the limit, what’s spent and when it resets.
Prerequisites
- The
AgentVaultcontract and tests from Agent vaults. - A deployed vault, for the last step.
Steps
1
Choose the limit in token units
The vault stores the limit in the token’s smallest unit. USDT on BOT Chain has 6 decimals, so 1 USDT is The first prints
1000000. Converting by hand is error prone, so let a tool do it:5, the second prints 5000000. In TypeScript, use parseUnits("5", 6) and formatUnits(value, 6) from viem. For more on decimals, see USDT and decimals.2
Understand the daily window
The vault counts spending per UTC day. It keeps only two numbers: the day it last spent on, and how much it spent that day. When a payment arrives on a new day, the counter starts again from 0. There’s no job that resets it at midnight.
today() divides the block timestamp by 86,400 seconds, so the number changes at 00:00 UTC.src/AgentVault.sol
remaining is computed with a check instead of plain subtraction. If the owner lowers the limit below what’s already spent today, remaining is 0 instead of the call reverting with an underflow.A calendar day is simple and cheap, but it allows up to twice the limit in a short span: the full limit at 23:59 UTC and again at 00:00 UTC. If that matters, set the limit to half of what you’re willing to lose in one burst, or use a rolling 24 hour window, which needs more storage per payment.
3
Understand the allowlist
The allowlist is a mapping the owner edits. The allowlist is the stronger of the two checks. A daily limit caps how much a bad decision costs. An allowlist means the money can only go to places you chose. Prompt injection usually works by getting the agent to pay the attacker, and the allowlist blocks that outright.Every change emits
pay checks it before it touches the limit, so a payment to an unknown address fails even if it’s tiny.src/AgentVault.sol
RecipientAllowed, so you can rebuild the current list from the vault’s logs on BOTScan.4
Test the limit across midnight
Foundry’s The vault in these tests has a 10 USDT limit. Run them:
vm.warp sets the block timestamp, so you can test the reset without waiting a day. These tests from the agent vault suite cover the limit and the allowlist:test/AgentVault.t.sol
5
Read the limit from your app
Your agent’s tools should check The reset time comes from the latest block’s timestamp, not your computer’s clock, because that’s what the contract uses.
remainingToday() before they try to pay, so the model gets a clear answer instead of a failed transaction. Add VAULT to your bot-defi .env, then save:vault-status.ts
Verify it worked
The four tests pass:Output
vault-status.ts prints:
Output
Troubleshooting
The limit didn't reset at midnight in my time zone
The limit didn't reset at midnight in my time zone
The vault uses UTC. Midnight where you are is a different moment unless you’re on UTC. The
Resets at line shows the exact time.remainingToday is 0 but nothing was spent
remainingToday is 0 but nothing was spent
Check
dailyLimit(). A limit of 0 blocks every payment, and a limit set without decimals, like 5 instead of 5000000, allows only 0.000005 USDT.vm.warp moved time but remainingToday didn't change
vm.warp moved time but remainingToday didn't change
The new timestamp is still on the same UTC day. Warp to the start of the next day with
(block.timestamp / 1 days + 1) * 1 days, as test_LimitResetsAtUtcMidnight does.The agent needs to pay a new address
The agent needs to pay a new address
Only the owner can add it, with
setRecipientAllowed. That’s the point: the agent can ask, but a person decides. See Human approval for asking in the flow instead.Next steps
Tool calling
Give a model tools that respect these limits.
Identity hooks
Add a trust check on recipients.