Skip to content

Safety suite

Three in-chat tools that sit on top of the same Etherscan v2 + model infrastructure as the contract auditor: a token rug checker, a wallet reputation card, and an approval revoker.

All three are free, need no auth, and produce a public shareable artifact.

Token safety (RugCheck)

Intent: TOKEN_SAFETY · Action: token_safety

Scans an ERC-20 for the patterns that precede a rug: concentrated holdings, mint authority, blacklists, punitive taxes, honeypot mechanics.

Pipeline

Locker detection recognises PinkLock, Unicrypt and Team.Finance addresses (hardcoded), so LP tokens sitting in a legitimate locker are not misread as whale concentration.

Output shape

FieldMeaning
score0–100
verdictsafe | caution | risky | dangerous
findings[]Human-readable issues
checks[]ownership_renounced, mintable, blacklist, tax, proxy, honeypot
recommendationsWhat a user should do

Caching

A scan less than 6 hours old for the same (address, chainId) is reused, skipping the model call entirely. This is the single largest cost control in the safety suite, and it is why Autopilot's safety gate can afford to check every token it buys.

Routes

All public, no auth:

EndpointReturns
GET /v1/safety/token/:address?chainId=…Scan JSON
GET /v1/safety/scan/:idScan JSON by scanId
GET /v1/safety/token/:address/badge.svg?chainId=…Embeddable SVG badge

Surfaces

SurfaceFile
Chat carddefi-dex/src/component/defi/swapAI/actions/TokenSafetyResult.tsx
Public page/token/:addresssrc/pages/audit/TokenSafety.tsx
Embed view/token/:address/embedsrc/pages/audit/TokenSafetyEmbed.tsx

The public page shows score, verdict, the checks grid, a top-holders bar, full findings, and — the growth mechanic — an embed snippet a project can copy onto its own token site. The embed view has a transparent background and is fully self-contained.

The embed badge cannot be iframed from defi.blazpay.com

netlify.toml sets X-Frame-Options: SAMEORIGIN for /* with no override, so third-party embedding fails today. netlify.ai.toml fixes it for /token/*/embed via CSP frame-ancestors * — which means the badge only works once the artifact routes live on ai.blazpay.com. See domains.

Used by Autopilot

The same scanner is the safety gate in autopilotCron. safety.minTokenScore in an Autopilot policy is compared against this score. Trusted majors are skipped; unscored tokens block rather than pass.

Wallet reputation card

Intent: WALLET_CARD · Action: wallet_card

A shareable on-chain profile. Accepts a connected wallet, a 0x address, a Blazpay username, or a .eth name.

Data sources

SourceProvides
portfolioService.getSummary / getPortfolio (Zerion)Balances, chain distribution, top tokens
Etherscan v2 txlist (ascending + descending with offset=1)First and last transaction timestamp per chain
Etherscan v2 eth_getTransactionCount proxyPer-chain transaction counts

Chains covered: Ethereum, BSC, Polygon, Arbitrum, Optimism, Base, Linea, Avalanche.

Labels

Computed heuristically — no LLM involved:

CategoryLabels
SizeWhale, Shark, Fish
TenureOG (5y+), Veteran (3y+), Newcomer
BreadthMulti-chain, Power user, Active
BehaviourDiamond hands, Degen, Stable stacker

Routes

EndpointPurpose
GET /v1/wallet-card/:walletCached card JSON
POST /v1/wallet-card/generateForce a fresh generate

Surfaces

SurfaceFile
Chat cardswapAI/actions/WalletCardResult.tsx
Public page/wallet/:walletsrc/pages/audit/WalletCard.tsx

The public page uses the same "diploma" treatment as the audit certificate, with Refresh, Share, and Download → PNG/PDF. Export lazy-loads html-to-image and jspdf. The exportable region is wrapped in <div ref={cardRef}>, and elements marked data-export-hide="true" are excluded from the render — so the buttons do not appear in the downloaded image.

Approval revoker

Intent: REVOKE_APPROVALS · Action: revoke_approvals

Finds the ERC-20 allowances a wallet has granted, scores their risk, and revokes them one click at a time.

Scan

Server-side, in bz-backend/v1/services/approvals.service.js:

  1. Etherscan v2 module=logs getLogs with the Approval topic0 0x8c5be1… and topic1 = pad32(wallet). Paginates up to 5 × 1000 logs.
  2. Reduce to the latest log per (token, spender) pair.
  3. Keep only pairs whose value is > 0.
  4. Cap at the top 25 by allowance size, to bound the metadata fan-out.
  5. Per token: tokeninfo for symbol, name, decimals.
  6. Per spender: getsourcecode for the verified flag and contract name.

Risk score

ConditionPoints
Unlimited allowance+40
Unverified spender contract+25
Very large (but finite) allowance+15

Revocation

There is no backend route — the chat intent returns the scan inline, and revokes are client-side transactions.

swapAI/actions/ApprovalRevoker.tsx:

  • Reads useWallet() for a provider and chainId.
  • If the wallet is on the wrong chain, shows a "Switch to chain" button using wallet.switchChain.
  • Each row's Revoke button builds Contract(token, erc20Abi, signer).approve(spender, 0).
  • Per-row state machine: idle → pending → success/error. The row is removed on success.

The scan infers "active" from the most recent Approval log

It does not query the current on-chain allowance(owner, spender). A freshly spent allowance can therefore still appear in the list. The clean hardening is a multicall over allowance before display — a known, unimplemented improvement.

Public pages

All routed outside MainLayout so they work unauthenticated, historically at audit.blazpay.ai and migrating to ai.blazpay.com:

RoutePage
/token/:addressToken scan
/token/:address/embedIframe-embeddable mini card
/wallet/:walletWallet reputation card
/report/:idAudit report — see contract auditor
/certificate/:idAudit certificate

SEOHead is applied to all of them, which helps Googlebot. Social bots still see the global OG fallback because there is no server-side OG rendering — a known future improvement.

bz-agent wiring

All three intents follow the audit feature's pattern exactly:

PieceFiles
Templatessrc/template/{tokenSafety,walletCard,revokeApprovals}.ts
NodestokenSafetyNode, walletCardNode, revokeApprovalsNode in src/agent/node.ts
GraphaddNode + addEdge(…, 'EXIT') + a case in the entry conditional edges, in src/agent/graph.ts
IntentsAVAILABLE_INTENTS in src/utils/constant.ts

bz-backend dispatch

Three cases in v1/services/agent.service.js:

js
case 'TOKEN_SAFETY':     return await scanToken(...)
case 'WALLET_CARD':      return await generateWalletCard(...)
case 'REVOKE_APPROVALS': return await scanApprovals(...)

Plus three fields on BlazMessage.data (tokenSafety, walletCard, revokeApprovals) and three BLAZ_ACTIONS entries.

Environment variables

VariableUsed by
ETHERSCAN_API_KEYAll three — Etherscan v2 unified multi-chain key
OPENAI_API_KEYToken safety only. Wallet card and approvals use no model
AUDIT_PUBLIC_BASE_URLShare-link prefix, defaults to https://audit.blazpay.ai

One Etherscan key covers every chain

The v2 endpoint is https://api.etherscan.io/v2/api?chainid=…. Supported here: Ethereum, BSC, Polygon, Arbitrum, Optimism, Base, Linea, Avalanche, Fantom.

Known limits

  • Wallet card "oldest hold" defaults to wallet age, not per-token entry time. Correct per-token entry needs an ERC-20 Transfer log index per chain.
  • Wallet card "protocols used" is reserved but unfilled — it needs a curated counterparty address book.
  • ENS resolution is only against the internal username database. Real on-chain resolution is one ENS Universal Resolver eth_call away.
  • Approval scan does not read current allowances. See the warning above.
  • The approval scan caps at 25 entries. A wallet with hundreds of approvals sees only the largest.
  • Token safety depends on verified source. An unverified contract yields a much weaker scan, and the score reflects uncertainty rather than confirmed safety.
  • The embed badge is blocked by X-Frame-Options on defi. See the warning above.

Internal + developer documentation. Usage figures are point-in-time snapshots — re-verify before publishing them.