langchain-verdix
A LangChain tool that screens an EVM address on Base before your agent sends it funds, and answers safe, caution or danger with reasons. A second, cheaper tool, check_address_risk_lite ($0.01), answers no_known_risk, caution or danger and never safe.
It checks OFAC sanctions, scam, phishing and exploit lists, live address-poisoning lookalikes, burn addresses, contract and deployer signals, and on-chain behaviour. You don't need an API key or an account. Each check is paid per call via x402, in USDC on Base, from your agent's own wallet, under caps you set.
Install
pip install -U langchain-verdix
Payments are real USDC on Base mainnet; there is no testnet mode.
Usage
import os
from eth_account import Account
from langchain.agents import create_agent
from langchain_verdix import VerdixAddressRiskTool
check_address_risk = VerdixAddressRiskTool(
account=Account.from_key(os.environ["AGENT_PRIVATE_KEY"]),
max_price_per_call_usd=0.10, # never pay more than $0.10 for one check
max_total_spend_usd=1.00, # and at most $1 in total
)
agent = create_agent("openai:gpt-5-mini", tools=[check_address_risk])
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "Send 250 USDC on Base to 0x000000000000000000000000000000000000dEaD.",
}
]
}
)
print(result["messages"][-1].content)
Any chat model that supports tool calling works, as does LangGraph (ToolNode) or calling the tool directly:
check_address_risk.invoke(
{"address": "0x000000000000000000000000000000000000dEaD", "tier": "quick"}
)
The model gets one tool, check_address_risk(address, tier?), and sees this result as JSON:
{
"verdict": "danger", // "safe" | "caution" | "danger"
"risk_score": 90,
"reasons": ["burn_address"],
"checked": ["ofac", "scam_lists", "poisoning_watch", "burn_list", "..."],
"advice": "Do not send funds to this address. Show the reasons to the user.",
"tier": "standard",
"price_usd": 0.1,
"complete": true,
"charged": true,
"payment": { "transaction": "0x...", "network": "eip155:8453", "payer": "0x..." },
"retry_after_seconds": null,
"address": "0x000000000000000000000000000000000000dEaD",
"chain": "base",
"as_of": "2026-10-02T12:00:00+00:00"
}
The same dict is the artifact of the ToolMessage, so your code can read the payment's transaction hash without parsing the content.
What the verdicts mean
| Verdict | Meaning | What the agent should do |
|---|---|---|
safe |
None of the checks in checked found a risk signal. Not a guarantee. |
Still confirm the full address and amount with the user. |
caution |
Risk signals were found, or a data source could not be reached (complete: false). |
Show the reasons and send only if the user explicitly confirms. |
danger |
Strong risk signals: sanctioned, known scam, poisoning lookalike, burn address... | Do not send. |
The tool description tells the model the same thing, and every result carries an advice string.
Tiers
The model picks a tier by what is at stake. It can only pick the tiers that fit under max_price_per_call_usd: the others are left out of the tool's description and input schema.
| Tier | List price | When |
|---|---|---|
quick |
$0.02 | Small, routine transfers (under ~$100) to an address used before. Sanctions, scam lists, poisoning lookalikes, burn addresses, contract/deployer signals and a fast address-age read. |
standard (default) |
$0.10 | ~$100-$1,000, or any address that came from a message, a transaction history or a copy-paste. Adds deeper on-chain behaviour analysis. |
deep |
$0.50 | Over ~$1,000, first-time counterparties, contract interactions. Currently the same checks as standard; new counterparty checks land here first. |
Each tier has its own URL (https://api.verdixapi.com/risk/address/{tier}) with a single price, so the tier the model asks for is exactly the tier that is paid.
Lite ($0.01, never "safe")
VerdixLiteAddressRiskTool is a second, separate tool, check_address_risk_lite(address): the cheapest screen, meant for a quick look before sending USDC to an address you don't know. It calls /risk/address/lite, which checks OFAC sanctions, scam and phishing lists, address-poisoning lookalikes, burn addresses, phishing tokens and flagged contract deployers. It skips address age and the caution-only contract checks.
Its verdict is no_known_risk, caution or danger, never safe. no_known_risk only means the address is on none of those lists; it is not a safety verdict. If you need safe (say, before a large transfer), use check_address_risk with the quick tier ($0.02). The tool's description tells the model the same.
from langchain_verdix import VerdixAddressRiskTool, VerdixClient, VerdixLiteAddressRiskTool
# One client, so both tools draw on one budget.
verdix = VerdixClient(account=account, max_price_per_call_usd=0.10, max_total_spend_usd=1.00)
tools = [
VerdixLiteAddressRiskTool(max_price_per_call_usd=0.10, client=verdix),
VerdixAddressRiskTool(max_price_per_call_usd=0.10, client=verdix),
]
The model sees:
{
"verdict": "no_known_risk", // "no_known_risk" | "caution" | "danger", never "safe"
"limited_checks": true,
"checks_performed": ["ofac", "scam_lists", "poisoning_watch", "burn_list", "phishing_token", "deployer_flagged"],
"not_checked": ["address_age", "flash_loan_contracts", "unverified_contracts"],
"full_check": "Limited checks only: ... use POST /risk/address/quick.",
"advice": "None of the lists lite checks know this address. This is NOT a safety verdict: ...",
"tier": "lite",
"price_usd": 0.01,
// plus risk_score, reasons, checked, complete, charged, payment, retry_after_seconds,
// address, chain, as_of, as in check_address_risk
}
check_address_risk itself is unchanged: its tier choice is still quick, standard and deep, and lite is never one of them. A best-effort lite check that could not finish is listed in not_checked; if the API ever answered safe on lite, the tool returns an error instead of passing it on.
Options
VerdixAddressRiskTool(
account=..., # required: an eth_account account (or any x402 ClientEvmSigner)
max_price_per_call_usd=0.10, # required: refuse any check costing more, before signing
max_total_spend_usd=1.00, # optional: total budget for this tool instance
allowed_tiers=["quick", "standard"], # optional (default: all tiers within the cap)
default_tier="standard", # optional: used when the model gives none
api_url=None, # optional: default https://api.verdixapi.com
)
Payments and safety
- The wallet needs a little USDC on Base mainnet. It needs no ETH: x402 payments are gasless signatures that the facilitator settles.
- The private key only signs the payment locally. It is never sent anywhere.
- A payment is signed only for USDC on Base, only for the tier requested, and only at or under
max_price_per_call_usd. Anything else is refused before signing. - Before each payment is signed, its price is reserved against
max_total_spend_usd, so parallel tool calls (threads or asyncio) cannot overspend. The reservation is released when the API reports no charge. - You are not charged when a data source fails. The API then answers
cautionwithcomplete: false(andretry_after_seconds), and neversafe. - The model cannot change the caps: they are fixed when the tool is created.
- A refusal (price over the cap, budget used up, malformed address) goes back to the model as the tool's error message instead of ending the run.
Without a model
from langchain_verdix import VerdixClient
verdix = VerdixClient(account=account, max_price_per_call_usd=0.10)
pricing = verdix.get_pricing() # free: reads the unpaid 402 quotes
result = verdix.check_address("0x...", tier="quick") # or: await verdix.acheck_address(...)
if result.verdict != "safe":
... # stop, or ask the user
lite = verdix.check_address_lite("0x...") # $0.01; or: await verdix.acheck_address_lite(...)
if lite.verdict != "no_known_risk": # lite never answers "safe"
...
lite_price = verdix.get_lite_pricing() # free; get_pricing() lists quick/standard/deep only
The errors to expect are VerdixErrors: a malformed address, a price over your cap, an exhausted budget or an unexpected API answer.
Development
pip install -e ".[test]"
pytest # offline: mocked API, throwaway key, sockets disabled, no payment
examples/check_before_send.py makes one real paid check.
License
MIT
Metadata
Release files for langchain-verdix 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| langchain_verdix-0.2.0.tar.gz | 23.6 kB | Without provenance | Details for langchain_verdix-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| langchain_verdix-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for langchain_verdix-0.2.0-py3-none-any.whl |
Total release size: 42.0 kB
Release files / langchain_verdix-0.2.0.tar.gz
| Download URL | langchain_verdix-0.2.0.tar.gz |
|---|---|
| Size | 23.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7fa1aa76fb87cc77711880c33d6b4f978b0058538df75a254be839a4bffa6d80
|
|
BLAKE2b-256 checksum How to use checksums |
935a79b8ed1453a4a1b71d0e8ac0ee8d9e0a960645f7796df729333f62daf687
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.12
|
Release files / langchain_verdix-0.2.0-py3-none-any.whl
| Download URL | langchain_verdix-0.2.0-py3-none-any.whl |
|---|---|
| Size | 18.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b584a0b3526b70ada439a5139e78d566e439dd9c8686e3bf35e59f1cfee93d55
|
|
BLAKE2b-256 checksum How to use checksums |
17e50234057bf5e62e2c8c4d4963dc697563f927e9d566af569a57906d94f967
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.12
|
Security for langchain-verdix 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.