MCP Server

Stablecoin Scanner

com.achivx/stablescan
Crypto & Web3 Security Public & reachable MCP 2025-11-25

What this MCP does

Analyzes stablecoin addresses across multiple chains for sanctions, freezes, transaction exposure, approvals, risk, and wallet-graph relationships.

get_address_approvals
Open ERC-20/Permit2 allowances for an address (Class-F decoded facts) with a per-row approval-risk band (shadow heuristic): unlimited to a flagged/unknown spender is the #1 drain vector. A known-service spender (DEX router / Permit2) is normal. Expired Permit2 sub-allowances are 'expired'. The band is informational, not a risk-tier contribution, until it clears the precision gate. No auth.
Input schema
{'type': 'object', 'required': ['address'], 'properties': {'chain': {'type': ['integer', 'string'], 'description': 'Chain, in any of four spellings: the id (8453 Base â\x80\x94 default for 0x addresses; a Tâ\x80¦ address defaults to Tron and a base58 one to Solana, 1 Ethereum, 10 Optimism, 42161 Arbitrum, 56 BNB Chain, 728126428 Tron, -1 Solana), the same id as a string, the network slug (base, ethereum, optimism, arbitrum, bnb-chain, tron, solana) or its CAIP-2 id (eip155:1, solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp). Unknown spellings are refused, and so is a network this scanner knows but does not index â\x80\x94 by name, never with an empty clean verdict. The answer is about ONE chain: chain_id says which, chain_source says how it was chosen (explicit|default|address_form) and chains_with_data lists where else this address has data â\x80\x94 read it before concluding, then ask again with chain set'}, 'address': {'type': 'string', 'description': 'the address to check (0x hex for EVM, or base58 for Solana)'}}, 'additionalProperties': False}
get_address_compliance
Per-address Class-F compliance facts (M4) on ONE chain, plus the OFAC SDN flag. freeze[]: one row per enabled freeze-capable stablecoin — status frozen|seized|clear|unknown; 'unknown' = no issuer act names the address and the pair's historical freeze-log backfill is not complete (freeze_log_backfilled=false), so absence-of-evidence is NOT published as 'not frozen' (P4-18); coverage=true = this chain's issuer acts are in the indexed log at all (a stablecoin registered after indexing began, e.g. USD1 on Ethereum/Tron, also needs its own history swept). frozen_usd6 = the token balance last read on a frozen/seized address at frozen_asof_block, face value with 6 decimals — locked now, not at the act (a blacklisted address can still receive); absent = not read, never zero. not_freezable[] = the enabled stablecoins here whose issuer has no per-address freeze ({token: symbol, token_address: contract address — the two fields a freeze[] row spells the same way — variant, reason: no_freeze_function = the contract has no freeze function, no_freeze_authority = a Solana mint has no freeze authority}), e.g. the Binance-Peg USDT/USDC wrappers on BNB Chain: the issuer cannot freeze them on this chain, enforcement happens off-chain (bridge, custodian, exchange); every enabled stablecoin is in freeze[] or here, never both. frozen_elsewhere[] = the same 20 address bytes under a standing freeze/seize on ANOTHER indexed chain (EVM and Tron share them; Solana never matches), the cross-chain signal get_address_risk does not score: the issuer acted there — it does not freeze the balance here, and the same bytes are the same owner only for a plain key account; empty = no act elsewhere in what is indexed, not clean everywhere. freeze_proposals[] = acts proposed on this chain's issuer owner multisig naming the address (USDT on Ethereum and Tron): executed ones only, pending too only if the operator sets FREEZE_PROPOSALS_PUBLIC=true, failed/revoked/superseded/stale never; balance_at_submission, balance_at_execution and moved_out are raw token minor units (decimal strings); empty = no published proposal, not 'nothing pending'. Decoded on-chain issuer acts with provenance; no heuristics; not an identity/AML/legal determination. No auth.
Input schema
{'type': 'object', 'required': ['address'], 'properties': {'chain': {'type': ['integer', 'string'], 'description': 'Chain, in any of four spellings: the id (8453 Base â\x80\x94 default for 0x addresses; a Tâ\x80¦ address defaults to Tron and a base58 one to Solana, 1 Ethereum, 10 Optimism, 42161 Arbitrum, 56 BNB Chain, 728126428 Tron, -1 Solana), the same id as a string, the network slug (base, ethereum, optimism, arbitrum, bnb-chain, tron, solana) or its CAIP-2 id (eip155:1, solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp). Unknown spellings are refused, and so is a network this scanner knows but does not index â\x80\x94 by name, never with an empty clean verdict. The answer is about ONE chain: chain_id says which, chain_source says how it was chosen (explicit|default|address_form) and chains_with_data lists where else this address has data â\x80\x94 read it before concluding, then ask again with chain set'}, 'address': {'type': 'string', 'description': 'the address to check (0x hex for EVM, or base58 for Solana)'}}, 'additionalProperties': False}
get_address_exposure
The §8 taint-exposure verdict for an address: a bounded backward BFS over the value graph to the nearest OFAC-sanctioned or hack/drainer/mixer root, returning a class (sanctioned|direct|indirect|negligible|unknown|none), nearest-hop, and a value-proportional haircut fraction. 'unknown' = the assessment could not be completed (OFAC list unloaded, frontier truncated, or short history) — never a false 'none'. Heuristic risk signal, not an AML/legal determination. No auth.
Input schema
{'type': 'object', 'required': ['address'], 'properties': {'chain': {'type': ['integer', 'string'], 'description': 'Chain, in any of four spellings: the id (8453 Base â\x80\x94 default for 0x addresses; a Tâ\x80¦ address defaults to Tron and a base58 one to Solana, 1 Ethereum, 10 Optimism, 42161 Arbitrum, 56 BNB Chain, 728126428 Tron, -1 Solana), the same id as a string, the network slug (base, ethereum, optimism, arbitrum, bnb-chain, tron, solana) or its CAIP-2 id (eip155:1, solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp). Unknown spellings are refused, and so is a network this scanner knows but does not index â\x80\x94 by name, never with an empty clean verdict. The answer is about ONE chain: chain_id says which, chain_source says how it was chosen (explicit|default|address_form) and chains_with_data lists where else this address has data â\x80\x94 read it before concluding, then ask again with chain set'}, 'address': {'type': 'string', 'description': 'the address to check (0x hex for EVM, or base58 for Solana)'}}, 'additionalProperties': False}
get_address_risk
The unified risk verdict for an address (§9 R2): a single tier (unknown|none|low|medium|high|severe) aggregating decoded FACTS (OFAC listing, issuer freeze/seize, exposure to a sanctioned/hack root) and ATTRIBUTED labels (with provenance, capped at medium alone; a lone-source accusation contributes no tier). tier 'unknown' means the assessment could NOT be completed (incomplete coverage) — NOT 'checked clean'. The freeze input is this chain's per-token status alone: frozen/seized argues for high, and an 'unknown' status or a pair whose freeze history is not backfilled turns a would-be 'none' into 'unknown'. NOT scored: the same address frozen on another chain (frozen_elsewhere), issuer freeze proposals and frozen balances — read them with get_address_compliance. Not an identity/AML/legal determination. No auth.
Input schema
{'type': 'object', 'required': ['address'], 'properties': {'chain': {'type': ['integer', 'string'], 'description': 'Chain, in any of four spellings: the id (8453 Base â\x80\x94 default for 0x addresses; a Tâ\x80¦ address defaults to Tron and a base58 one to Solana, 1 Ethereum, 10 Optimism, 42161 Arbitrum, 56 BNB Chain, 728126428 Tron, -1 Solana), the same id as a string, the network slug (base, ethereum, optimism, arbitrum, bnb-chain, tron, solana) or its CAIP-2 id (eip155:1, solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp). Unknown spellings are refused, and so is a network this scanner knows but does not index â\x80\x94 by name, never with an empty clean verdict. The answer is about ONE chain: chain_id says which, chain_source says how it was chosen (explicit|default|address_form) and chains_with_data lists where else this address has data â\x80\x94 read it before concluding, then ask again with chain set'}, 'address': {'type': 'string', 'description': 'the address to check (0x hex for EVM, or base58 for Solana)'}}, 'additionalProperties': False}
get_recent_transactions
Recent stablecoin transfers on ONE chain (newest first), filterable by recipient/payer, cursor-paginated (next_cursor). Answer: items[], each row tx_hash, log_index, block_number, block_time (RFC 3339), payer, recipient, amount {amount (minor units, string), decimals, token (symbol)}, token (the contract address), finality, and usd_amount where priced. The chain is the one asked for — rows do not repeat it. Generic on-chain Transfer facts. No auth.
Input schema
{'type': 'object', 'properties': {'chain': {'type': ['integer', 'string'], 'description': 'Chain of the feed, in any of four spellings: the id (8453 Base â\x80\x94 default, 1 Ethereum, 10 Optimism, 42161 Arbitrum, 56 BNB Chain, 728126428 Tron, -1 Solana), the same id as a string, the network slug (base, ethereum, optimism, arbitrum, bnb-chain, tron, solana) or its CAIP-2 id (eip155:1, solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp). Unknown spellings are refused, and so is a network this scanner knows but does not index. Without chain, a Tâ\x80¦ or base58 payer/recipient filter selects Tron or Solana; otherwise Base. The rows do not repeat the chain: it is the one asked for'}, 'limit': {'type': ['null', 'integer'], 'maximum': 200, 'minimum': 1, 'description': 'Max rows, 1..200 (default 25). A value outside that range is refused, as REST refuses it'}, 'payer': {'type': 'string', 'description': 'Filter by payer address (0x+40hex)'}, 'cursor': {'type': 'string', 'description': 'Pagination cursor from a previous response'}, 'recipient': {'type': 'string', 'description': 'Filter by recipient address (0x+40hex)'}}, 'additionalProperties': False}
get_sanctions_status
OFAC SDN sanction-list coverage: how many EVM + Solana + Tron addresses are ingested (free public 0xB10C list, US-Gov public-domain data) and when it was last refreshed. Heuristic exposure signal — not legal advice; verify against the official SDN list. No auth.
Input schema
{'type': 'object', 'additionalProperties': False}
get_sync_status
Data freshness for one chain: the indexer's head, safe and finalized blocks, its lag in blocks and freshness_seconds, so the agent knows how recent the data behind an answer is. No auth.
Input schema
{'type': 'object', 'properties': {'chain': {'type': ['integer', 'string'], 'description': 'Chain, in any of four spellings: the id (8453 Base â\x80\x94 default, 1 Ethereum, 10 Optimism, 42161 Arbitrum, 56 BNB Chain, 728126428 Tron, -1 Solana), the same id as a string, the network slug (base, ethereum, optimism, arbitrum, bnb-chain, tron, solana) or its CAIP-2 id (eip155:1, solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp). Unknown spellings are refused, and so is a network this scanner knows but does not index. chain_id in the answer says which chain it is about'}}, 'additionalProperties': False}
get_wallet_graph_walk
Bounded walk over the indexed stablecoin settlement flow graph from a wallet: payer<->recipient hops, value-weighted score, entity-aware node keys, attributed-node stop option, and sanctioned-node markers. This is an explainability primitive, not a detector verdict, identity/control assertion, AML decision, or legal advice. Subjects over the degree gate (~50k edge-rows) return hop 1 only with frontier_truncated=true. No auth.
Input schema
{'type': 'object', 'required': ['address', 'token'], 'properties': {'hops': {'type': ['null', 'integer'], 'description': 'Max graph hops, 0..4 (default 2)'}, 'chain': {'type': ['integer', 'string'], 'description': 'Chain, in any of four spellings: the id (8453 Base â\x80\x94 default for 0x addresses; a Tâ\x80¦ address defaults to Tron and a base58 one to Solana, 1 Ethereum, 10 Optimism, 42161 Arbitrum, 56 BNB Chain, 728126428 Tron, -1 Solana), the same id as a string, the network slug (base, ethereum, optimism, arbitrum, bnb-chain, tron, solana) or its CAIP-2 id (eip155:1, solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp). Unknown spellings are refused, and so is a network this scanner knows but does not index â\x80\x94 by name, never with an empty clean verdict. The answer is about ONE chain: chain_id says which, chain_source says how it was chosen (explicit|default|address_form) and chains_with_data lists where else this address has data â\x80\x94 read it before concluding, then ask again with chain set'}, 'token': {'type': 'string', 'description': 'Token symbol â\x80\x94 REQUIRED, the server does not guess it (e.g. USDT). The asset hub page for a stablecoin names its own token.'}, 'window': {'type': 'string', 'description': 'Time window: 1h|24h|7d|30d|90d|1y|all (default all)'}, 'address': {'type': 'string', 'description': 'Wallet address (EVM 0x-hex or Solana base58)'}, 'frontier_cap': {'type': 'integer', 'description': 'Per-hop frontier cap, 1..500 (default 500)'}, 'stop_at_attributed': {'type': ['null', 'boolean'], 'description': 'Stop expanding known services/facilitators (default true)'}, 'stop_on_sanctioned': {'type': ['null', 'boolean'], 'description': 'Stop once a sanctioned node is reached (default false)'}}, 'additionalProperties': False}
Added
get_wallet_graph_walk
Oct. 1, 2026, 2:40 a.m.
Added
get_sync_status
Oct. 1, 2026, 2:40 a.m.
Added
get_sanctions_status
Oct. 1, 2026, 2:40 a.m.
Added
get_recent_transactions
Oct. 1, 2026, 2:40 a.m.
Added
get_address_risk
Oct. 1, 2026, 2:40 a.m.
Added
get_address_exposure
Oct. 1, 2026, 2:40 a.m.
Added
get_address_compliance
Oct. 1, 2026, 2:40 a.m.
Added
get_address_approvals
Oct. 1, 2026, 2:40 a.m.