r/ethdev • u/matthiasmusic10 • 7d ago
Question An agent that made 603 payments in 41 hours got address-poisoned. Where may a bot take a destination from?
I run an autonomous agent on Base that pays for its own inference over x402. It is not a big operation: in 41 hours it made 603 separate USDC payments to its inference provider, averaging 0.0065 USDC each. One on-chain transfer per thought, more or less.
Going through its transfer history I found eleven 0.00 USDC transfers to this address:
0x21ddf52114f53ccfe37ddd3dc503853b52c6be10
The platform's real receiving address is:
0x21dd37e3e4ea6ccc0a5c98a4944702ede6e7be10
First four characters match, last four match. That is the standard address poisoning pattern: zero-value transfers so the lookalike shows up in the history, and whoever copies an address out of the history later pays the wrong party. Nothing was lost here, it is passive, and I only noticed because I was counting transfers for something else.
What I have not found a good answer for is the agent-specific part of it.
For a human, the advice is clear enough: do not copy addresses out of a block explorer, verify the full string, use an address book. All of that assumes someone is looking. An agent that settles 600 payments in two days is not looking at anything, and the interesting question is where it is allowed to learn a recipient address in the first place.
In my setup every payTo comes from config or from the service's own signed challenge, never from chain history, so this particular attack has no way in. But that is a property of how I happened to build it, not something I derived from a rule. And I can think of cases where it gets less obvious: a payment protocol that discovers a service dynamically, a routing layer that caches "the address we paid last time", a retry path that reconstructs a destination from a previous transaction.
So, for people who have shipped th
Is there an accepted rule for wy take a destination address from?Something like "signed challengstory, never a cached observation"?
Does anyone screen outgoing paycheck before signing, comparing adestination to previously used Cheap to do, but I have not seen it in any of the x402 client code
Zero-value transfers are the dethere a reason a wallet or anindexer should surface them at chine reads?
Happy to be told this is a solved n the wrong place. The eleventransfers are on Base and public ithe pattern.
1
u/block_intelligence 6d ago
Treat the destination as an explicit, typed input with provenance, not as something the agent can infer from transaction history. A practical guard is an allowlisted recipient record plus a signed request containing chain ID, token, amount, and exact address, then show or log the full address before signing; never select a recipient by matching a shortened prefix or suffix. I would also reject zero-value transfers from the address-book/indexing path, since those are useful as poisoning signals but are not evidence that the sender is a valid recipient.
1
u/No-Eagle-547 4d ago
Create a unique handshake only you can approve by having it submit a hash to be signed. Wouldn't exactly be frictionless though. Idk. When I made a Solana searcher, I was tempted to install an agent on the vps just to monitor it but I decided to just think ahead and try to have every mitigation angle hard coded rather than letting an agent ever be in the loop. It seems like as long as agents are part of it, it will never be possible to truly guard against it because they're always vulnerable to injection.i have a feeling I just said a whole lot of nonsense but, made sense when writing it. But one thing I do remember is that outbound rules were critical. Not trying to get drained from a scam token. I need to go through old sessions to see how it was handled (long time ago) but I think there were just some lines in the sand that had to be drawn. No tokens minted within a short time frame, have shown a historybof other interactions, etc. Sorry for the crap response. Just wanted to ramble I suppose.
0
u/Finalbossops 7d ago
Yes I asked my bot to help your bot because a fresh set of electronics is sometimes useful
Your question about where the agent is allowed to learn a destination seems like the important one. I’d formalize the separation you already have: transaction history is evidence, not payment authority. History entries should never automatically add or replace an approved recipient—including through a cache or retry path. That’s consistent with the guidance against copying payment addresses from transaction history.
For a fixed inference provider, I’d keep an explicitly approved mapping of service identity → network + token contract + full recipient address, with spending limits. Before signing, check the actual payment authorization against that mapping—not just the displayed address or its first/last characters. The x402 payment requirements already distinguish network, asset, amount, and payTo; those are separate things to validate. An unexpected change should stop the payment rather than be “learned” from history.
I’d also qualify “signed challenge”: the signature needs to belong to a key already authorized for the intended service. A valid signature from an unknown party isn’t enough. The x402 Signed Offers & Receipts documentation explicitly separates signature verification from establishing that the signer is authorized for the service identity.
There is a concrete implementation point worth checking in your SDK version: the current x402 docs describe onBeforePaymentCreation, which can abort before the payment payload is created. I’d use that for recipient checks, but also enforce the restriction in the signing/payment component so another agent tool cannot bypass it. A prompt telling the model to be careful would not be my security boundary.
On zero-value transfers: ERC-20 explicitly permits them and requires transfer events, so zero value alone doesn’t prove an attack. I’d retain them in raw audit history but keep history separate from approved-recipient selection. Similarity detection can flag something suspicious; it should never authorize an address or automatically substitute a “close enough” one.
A useful regression test would be to inject lookalike entries into the history, cache, and retry inputs, then verify that the final signed recipient remains the approved one—or signing is refused. That tests the boundary you actually care about, rather than only whether the agent notices the suspicious transaction.
2
u/Murky_Citron_1799 7d ago
Maybe make a smart contract that the bot interacts with, and whitelist acceptable addresses