<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: verdixapi</title>
    <description>The latest articles on DEV Community by verdixapi (@verdixapi).</description>
    <link>https://dev.to/verdixapi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4169715%2F1af293f6-ac1b-4fd6-91f1-c890fd4a7bae.png</url>
      <title>DEV Community: verdixapi</title>
      <link>https://dev.to/verdixapi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/verdixapi"/>
    <language>en</language>
    <item>
      <title>Stop your AI agent from paying a scammer</title>
      <dc:creator>verdixapi</dc:creator>
      <pubDate>Wed, 07 Oct 2026 19:39:45 +0000</pubDate>
      <link>https://dev.to/verdixapi/stop-your-ai-agent-from-paying-a-scammer-32o9</link>
      <guid>https://dev.to/verdixapi/stop-your-ai-agent-from-paying-a-scammer-32o9</guid>
      <description>&lt;p&gt;If your agent can pay over &lt;a href="https://x402.org" rel="noopener noreferrer"&gt;x402&lt;/a&gt; or sign USDC transfers, it can also pay the wrong address. Nobody has to break in for that to happen. Someone only has to influence which address ends up in the payment.&lt;/p&gt;

&lt;p&gt;This post covers three ways that happens, why the check belongs before the signature, and how to add one with a small open-source package. It also covers the limits of that check.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I build &lt;a href="https://api.verdixapi.com" rel="noopener noreferrer"&gt;Verdix&lt;/a&gt;, the address-risk API that the package below calls.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways an agent ends up paying the wrong address
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Prompt injection
&lt;/h3&gt;

&lt;p&gt;An LLM decides what to do from text, and some of that text comes from people you don't control: web pages, emails, tool results, other agents. Whoever writes that text can steer the agent.&lt;/p&gt;

&lt;p&gt;The best-known public demo is &lt;a href="https://the-decoder.com/hacker-wins-47000-by-tricking-ai-chatbot-with-smart-prompting/" rel="noopener noreferrer"&gt;Freysa&lt;/a&gt;, an agent released in November 2024 with one rule: never approve a transfer of its prize pool. After 481 failed attempts, message 482 got it to do exactly that. The attacker convinced it that &lt;code&gt;approveTransfer&lt;/code&gt; handled &lt;em&gt;incoming&lt;/em&gt; payments, then announced a fake deposit. The agent sent its whole balance, 13.19 ETH (about $47,000).&lt;/p&gt;

&lt;p&gt;The lesson isn't that this model was weak. A rule written in the prompt is enforced only by the model that can be talked out of it. If the payment path has no check outside the model, a good enough injection can get through.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Address poisoning
&lt;/h3&gt;

&lt;p&gt;Addresses are 40 hex characters, and people (and agents) copy them from transaction history. An attacker generates a lookalike address with the same first and last characters as one you've used before, then sends you a tiny or zero-value transfer from it, so it shows up in your history.&lt;/p&gt;

&lt;p&gt;On 3 May 2024, a holder &lt;a href="https://www.coindesk.com/business/2024/05/03/exploiter-steals-68m-worth-of-crypto-through-address-poisoning" rel="noopener noreferrer"&gt;sent 1,155 WBTC, about $68M, to a lookalike address&lt;/a&gt; that matched the first and last six characters of the intended recipient. The funds were later returned after negotiation, which is rare.&lt;/p&gt;

&lt;p&gt;It isn't a one-off. A USENIX Security 2025 paper, &lt;a href="https://arxiv.org/abs/2501.16681" rel="noopener noreferrer"&gt;&lt;em&gt;Blockchain Address Poisoning&lt;/em&gt;&lt;/a&gt;, measured two years on Ethereum and BSC: 270M attack attempts targeting 17M victims, and 6,633 incidents with at least $83.8M in losses.&lt;/p&gt;

&lt;p&gt;An agent that takes "the address we paid last time" from history or logs is exactly the user this attack was built for. It also doesn't get the feeling that something looks off.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Sanctioned addresses
&lt;/h3&gt;

&lt;p&gt;OFAC's SDN list includes crypto addresses. For example, the &lt;a href="https://ofac.treasury.gov/recent-actions/20220414" rel="noopener noreferrer"&gt;14 April 2022 update&lt;/a&gt; added an Ethereum address for the Lazarus Group: &lt;code&gt;0x098B716B8Aaf21512996dC57EB0615e2383E2f96&lt;/code&gt;. The &lt;a href="https://github.com/0xB10C/ofac-sanctioned-digital-currency-addresses" rel="noopener noreferrer"&gt;0xB10C/ofac-sanctioned-digital-currency-addresses&lt;/a&gt; project extracts these addresses from Treasury's XML every night.&lt;/p&gt;

&lt;p&gt;Here nothing has to trick your agent. An address can be perfectly valid, the payment can go through, and you have still paid a party you may be legally barred from paying. Whether that applies to you is a question for your lawyer, but checking the list costs very little.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the check goes before the signature
&lt;/h2&gt;

&lt;p&gt;An x402 payment is an EIP-3009 authorization that the facilitator settles on-chain. A USDC transfer is a transaction. Once either is signed and broadcast, you can't take it back. A post-payment alert only tells you what you lost.&lt;/p&gt;

&lt;p&gt;So the check has to run in the signing path:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;outside the model&lt;/strong&gt;, so a prompt can't talk it out of running;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;on the final recipient&lt;/strong&gt;, the &lt;code&gt;payTo&lt;/code&gt; or &lt;code&gt;to&lt;/code&gt; that will actually be signed, not the name the model was given;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;failing closed&lt;/strong&gt;, so an unanswered check blocks the payment instead of waving it through.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Doing it with verdix-guard
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/verdix-guard" rel="noopener noreferrer"&gt;&lt;code&gt;verdix-guard&lt;/code&gt;&lt;/a&gt; (MIT, &lt;a href="https://github.com/verdixapi/verdix-guard" rel="noopener noreferrer"&gt;source&lt;/a&gt;) hooks into two places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;x402 payments&lt;/strong&gt;: it registers an &lt;code&gt;onBeforePaymentCreation&lt;/code&gt; hook on the official &lt;code&gt;@x402&lt;/code&gt; client, so the &lt;code&gt;payTo&lt;/code&gt; is screened before the payment payload is created.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Any transfer&lt;/strong&gt;: it wraps a viem account and screens native transfers, ERC-20 &lt;code&gt;transfer&lt;/code&gt; / &lt;code&gt;transferFrom&lt;/code&gt; / &lt;code&gt;approve&lt;/code&gt;, and EIP-3009 / EIP-2612 signatures before signing.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;verdix-guard @x402/fetch @x402/evm viem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For x402:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ExactEvmScheme&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@x402/evm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;wrapFetchWithPayment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x402Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@x402/fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;privateKeyToAccount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;viem/accounts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createVerdixGuard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;guardX402Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;verdix-guard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;privateKeyToAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AGENT_KEY&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="s2"&gt;`0x&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;x402Client&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eip155:8453&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ExactEvmScheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nf"&gt;guardX402Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;createVerdixGuard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchWithPayment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wrapFetchWithPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchWithPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://seller.example/paid-endpoint&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For direct transfers with viem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createVerdixGuard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;guardAccount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;VerdixGuardBlockedError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;verdix-guard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createVerdixGuard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;guardedAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;guardAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// use it in your walletClient&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;guardedAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;VerdixGuardBlockedError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;verdict&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A blocked x402 request throws an error containing &lt;code&gt;verdix-guard: &amp;lt;reason&amp;gt;&lt;/code&gt;. A guarded account throws &lt;code&gt;VerdixGuardBlockedError&lt;/code&gt;. In both cases nothing is signed.&lt;/p&gt;

&lt;p&gt;There is no API key. Each check is itself an x402 payment: $0.01 in USDC on Base at the default &lt;code&gt;lite&lt;/code&gt; tier, paid from a wallet you choose. Answers are cached for 24 hours per address, and spending on checks is capped at &lt;code&gt;dailyBudgetUsd&lt;/code&gt; (default $1). USDC payments below &lt;code&gt;minAmountUsd&lt;/code&gt; (default $0.10) aren't checked. Amounts the guard can't price in USD, such as ETH or other tokens, always are.&lt;/p&gt;

&lt;p&gt;What it decides by default:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Answer&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;danger&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;block, always&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;caution&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;block (change with &lt;code&gt;onCaution&lt;/code&gt;: &lt;code&gt;'allow'&lt;/code&gt; or &lt;code&gt;'ask'&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;no_known_risk&lt;/code&gt; / &lt;code&gt;safe&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;allow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;no usable answer&lt;/td&gt;
&lt;td&gt;block (&lt;code&gt;failMode: 'allow'&lt;/code&gt; to change)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  A real run
&lt;/h2&gt;

&lt;p&gt;To check the published package end to end, I installed &lt;code&gt;verdix-guard@0.1.0&lt;/code&gt; from npm into an empty directory. I wrapped a test wallet with &lt;code&gt;guardAccount&lt;/code&gt;, using &lt;code&gt;tier: 'lite'&lt;/code&gt;, &lt;code&gt;minAmountUsd: 0&lt;/code&gt; and &lt;code&gt;dailyBudgetUsd: 0.01&lt;/code&gt;, and asked it to sign a 0.20 USDC transfer to &lt;code&gt;0x32fb1bedd95bf78ca2c6943ae5aeaeaafc0d97c1&lt;/code&gt;. That's an exploit contract on Base, listed in DeFiHackLabs as &lt;a href="https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2024-12/CloberDEX_exp.sol" rel="noopener noreferrer"&gt;CloberDEX (2024-12)&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verdix answered &lt;code&gt;danger&lt;/code&gt; with the reason &lt;code&gt;deployer_flagged&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The guard threw &lt;code&gt;VerdixGuardBlockedError&lt;/code&gt;, and no signature was produced.&lt;/li&gt;
&lt;li&gt;The send function was a local stub, called only after a successful signature. It was never called, and the test wallet's nonce on Base stayed at 0.&lt;/li&gt;
&lt;li&gt;The only money that moved was the check itself: 0.01 USDC, tx &lt;a href="https://basescan.org/tx/0xd3c4a29f0dbac9aab012deeeaf20aa535f788595c9995c2e54c30e1795eb54a0" rel="noopener noreferrer"&gt;&lt;code&gt;0xd3c4a29f…eb54a0&lt;/code&gt;&lt;/a&gt; (Base block 52220766).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One practical note if you reproduce this: a viem wallet client may call its transport (for example &lt;code&gt;eth_fillTransaction&lt;/code&gt;) &lt;em&gt;before&lt;/em&gt; signing. To prove that nothing left the machine, sign with the guarded account directly, not through the wallet client.&lt;/p&gt;

&lt;h2&gt;
  
  
  The limits, honestly
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;safe&lt;/code&gt; isn't a guarantee.&lt;/strong&gt; It means the checks that ran found nothing. A brand-new scam address that is on no list yet and has no on-chain history will pass. Keep confirming large or unusual payments with a human.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;lite&lt;/code&gt; never says &lt;code&gt;safe&lt;/code&gt;.&lt;/strong&gt; The default tier checks sanctions, scam and phishing lists, address-poisoning lookalikes, burn addresses, phishing tokens and flagged deployers. It skips address age and some contract checks. A clean result is &lt;code&gt;no_known_risk&lt;/code&gt;, which says less than &lt;code&gt;safe&lt;/code&gt;. If you need &lt;code&gt;safe&lt;/code&gt;, use &lt;code&gt;tier: 'quick'&lt;/code&gt; ($0.02).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failing closed has a cost.&lt;/strong&gt; With the default &lt;code&gt;failMode: 'block'&lt;/code&gt;, your agent stops paying while Verdix or one of its data sources is unavailable. &lt;code&gt;failMode: 'allow'&lt;/code&gt; keeps it running, but those payments go through unchecked. An answer that already says &lt;code&gt;danger&lt;/code&gt; is blocked in both modes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base only.&lt;/strong&gt; Payments on other networks pass through unchecked by default (&lt;code&gt;otherNetworks: 'allow'&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not everything is decoded.&lt;/strong&gt; Raw &lt;code&gt;sign&lt;/code&gt; / &lt;code&gt;signMessage&lt;/code&gt;, EIP-7702 authorizations and contract calls that aren't a known transfer or approval pass through unchanged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State is in memory.&lt;/strong&gt; The cache and the daily budget reset when your process restarts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It doesn't fix prompt injection.&lt;/strong&gt; It narrows what an injected agent can do with money. Keep your other controls too: allowlists for known payees, per-payment caps in the wallet, and a human approval step above a threshold.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using MCP, Vercel AI SDK or LangChain?
&lt;/h2&gt;

&lt;p&gt;The same check is also available as an agent tool. Every package below has a lite tool ($0.01) whose clean answer is &lt;code&gt;no_known_risk&lt;/code&gt;, never &lt;code&gt;safe&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP:&lt;/strong&gt; &lt;code&gt;verdix-mcp&lt;/code&gt; (&lt;a href="https://www.npmjs.com/package/verdix-mcp" rel="noopener noreferrer"&gt;npm&lt;/a&gt;, &lt;a href="https://github.com/verdixapi/verdix-mcp" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;), run with &lt;code&gt;npx -y verdix-mcp&lt;/code&gt;. A one-file &lt;code&gt;.mcpb&lt;/code&gt; bundle for Claude Desktop is attached to the &lt;a href="https://github.com/verdixapi/verdix-mcp/releases/latest" rel="noopener noreferrer"&gt;latest GitHub release&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vercel AI SDK:&lt;/strong&gt; &lt;code&gt;verdix-ai-sdk&lt;/code&gt; (&lt;a href="https://www.npmjs.com/package/verdix-ai-sdk" rel="noopener noreferrer"&gt;npm&lt;/a&gt;, &lt;a href="https://github.com/verdixapi/verdix-ai-sdk" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;). It also has &lt;code&gt;verdixNeedsApproval&lt;/code&gt;, a &lt;code&gt;needsApproval&lt;/code&gt; for your own transfer tool that asks the user only when Verdix finds a risk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LangChain:&lt;/strong&gt; &lt;code&gt;langchain-verdix&lt;/code&gt; (&lt;a href="https://pypi.org/project/langchain-verdix/" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt;, &lt;a href="https://github.com/verdixapi/langchain-verdix" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;If your agent can sign payments, put a check between the decision and the signature: one the model can't skip and that blocks when it can't get an answer. The package above is one way to do that. Whatever you use, test it with a payment to an address you know is bad, before an attacker does it for you.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>web3</category>
      <category>security</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
