How it works
The vault doesn’t know about any particular identity protocol. It asks one question through a one-function interface:src/IAgentIdentity.sol
pay calls isTrusted(to) and reverts with RecipientNotTrusted(to) if the answer is false. When the identity is address(0), the check is skipped.
src/AgentVault.sol
isTrusted by reading from it. The vault never changes.
What you’ll build
TrustList, the simplest source: a list one owner maintains.ScoreAdapter, an adapter that trusts addresses whose score in an external registry meets a minimum.- Foundry tests that prove each one gates payments.
Prerequisites
- The
AgentVaultproject from Agent vaults.
Steps
1
Write a trust list
src/TrustList.sol
public mapping creates an isTrusted(address) getter, which is exactly what the interface asks for. A trust list is useful when a team or a partner, not the vault owner, decides who’s trusted.2
Write an adapter for a score registry
Most identity and reputation systems expose something richer than yes or no. An adapter turns that into the vault’s yes or no.
src/ScoreAdapter.sol
IScoreRegistry is a stand-in. Replace it with the interface of the system you use, and change isTrusted to whatever rule fits it: a minimum score, a valid attestation, a credential that hasn’t expired.Keep adapters view and simple. The vault calls isTrusted on every payment, and if it reverts, the payment reverts.3
Test both
test/IdentityHooks.t.sol
4
Connect it to your vault
Deploy the trust list, add a recipient, then point the vault at it from the owner wallet:To turn the check off, set the identity to
0x0000000000000000000000000000000000000000.Verify it worked
All four tests pass:Output
vault-pay.ts from Agent vaults. On 2026-10-02, with a trust list on testnet connected to the vault, a payment to an allowed but untrusted recipient was refused. After the owner called setTrusted, the same payment went through:
Output
ScoreAdapter was tested with forge test only, because it needs a score registry to point at.
Choose a source carefully
The identity contract can stop every payment, so treat it as part of your security setup.- Who controls it? Whoever can change the source decides who your agent can pay. Prefer sources whose rules and admins you understand.
- Can it be upgraded? If the source sits behind a proxy, its behavior can change without your vault changing. Read it before you trust it.
- What if it breaks? If
isTrustedstarts reverting, the agent can’t pay anyone. The owner can switch the hook off withsetIdentity(address(0))and still withdraw at any time. - Is it on BOT Chain? The vault can only call contracts on the same chain. A source on another chain needs a bridge or an oracle to bring its data over.
Troubleshooting
Vault refused: RecipientNotTrusted
Vault refused: RecipientNotTrusted
The identity source returned
false for that address. Check with cast call YOUR_IDENTITY_ADDRESS "isTrusted(address)(bool)" THE_ADDRESS --rpc-url bot_testnet.Vault refused: RecipientNotAllowed, but the address is trusted
Vault refused: RecipientNotAllowed, but the address is trusted
The allowlist is checked first and still applies. Add the address with
setRecipientAllowed.Every payment reverts after setting the identity
Every payment reverts after setting the identity
The identity address may not be a contract that implements
isTrusted, or the adapter’s registry call is reverting. Call isTrusted directly with cast call to see the error. Set the identity to address(0) while you fix it.Next steps
Security checklist
Review your setup before real funds.
Spending limits
How the daily cap works.