SDK Docs
Add real-time threat intelligence to any Solana wallet or dApp in under five minutes. Fully typed, cache-first, zero dependencies beyond the Solana web3 library.
npm install @walour/sdkInstall
Works with any Node 18+ runtime. TypeScript types are bundled.
npm install @walour/sdk
pnpm add @walour/sdk
yarn add @walour/sdk
bun add @walour/sdkSet these environment variables before calling any SDK function:
ANTHROPIC_API_KEY=... # for decodeTransaction()
HELIUS_API_KEY=... # primary RPC + token checks
SUPABASE_URL=... # threat corpus database
SUPABASE_SERVICE_KEY=... # service role key
UPSTASH_REDIS_REST_URL=... # cache layer
UPSTASH_REDIS_REST_TOKEN=...Quick Start
All SDK functions are stateless exports. Import only what you need.
import { checkDomain, checkTokenRisk, decodeTransaction } from '@walour/sdk'
// Check a domain before the user signs a transaction from it
const domain = await checkDomain('suspicious-site.xyz')
if (domain.level === 'RED') {
showWarning(domain.reason)
}
// Check a token mint for rug risk
const token = await checkTokenRisk(mintAddress)
if (token.level === 'RED') {
console.warn(token.reasons.join(', '))
}
// Stream a human-readable explanation of the transaction
for await (const chunk of decodeTransaction(versionedTx)) {
appendToUI(chunk)
}checkTokenRisk(mint)
Score a token mint against 8 parallel risk checks. Returns a risk level, a 0-100 score, and a list of reasons for any failures.
import { checkTokenRisk } from '@walour/sdk'
const result = await checkTokenRisk(mintAddress: string)
interface TokenRiskResult {
level: 'GREEN' | 'AMBER' | 'RED'
score: number // 0-100, higher = more risk
reasons: string[] // human-readable flag descriptions
checks: Record<string, {
passed: boolean
weight: number
detail: string
}>
}Checks performed
| Check | Weight | Flags when |
|---|---|---|
| Mint authority active | 15 | Creator can still mint unlimited supply |
| Freeze authority active | 15 | Creator can freeze holder accounts |
| Holder concentration | 8-15 | Top wallet holds more than 30% of supply |
| LP lock (Raydium) | 10 | Liquidity pool is unlocked or missing |
| Supply anomaly | 10 | 0 decimals with over 1B supply |
| Token age | 8-15 | Created less than 24h ago |
| GoPlus honeypot | 20 | GoPlus flags as honeypot or blacklisted |
| Walour corpus hit | 30 | Address in threat registry |
checkDomain(hostname)
Check a domain against the Walour threat corpus. Falls back to GoPlus if the address is not in the local corpus.
import { checkDomain } from '@walour/sdk'
const result = await checkDomain('malicious-dapp.xyz')
interface DomainRiskResult {
level: 'GREEN' | 'AMBER' | 'RED'
reason: string
confidence: number // 0-1
source?: string // 'corpus' | 'goplus'
}lookupAddress(pubkey)
Look up any Solana address or domain in the threat corpus. Checks Redis, then Supabase, then the on-chain registry PDA in order.
import { lookupAddress } from '@walour/sdk'
const threat = await lookupAddress(address: string)
// Returns null if address is clean
interface ThreatReport {
address: string
type: 'drainer' | 'rug' | 'phishing_domain' | 'malicious_token'
source: 'chainabuse' | 'scam_sniffer' | 'community' | 'twitter'
confidence: number // 0-1
evidence_url?: string
first_seen: string // ISO 8601
last_updated: string // ISO 8601
}null for clean addresses.decodeTransaction(tx)
Stream a plain-English explanation of what a transaction does before the user signs it. Powered by Claude Sonnet 4.6. Detects red flags synchronously and streams the AI explanation in parallel.
import { decodeTransaction } from '@walour/sdk'
import { VersionedTransaction } from '@solana/web3.js'
// Accepts a VersionedTransaction object
for await (const chunk of decodeTransaction(tx: VersionedTransaction)) {
process.stdout.write(chunk)
}
// In React:
const [explanation, setExplanation] = useState('')
for await (const chunk of decodeTransaction(tx)) {
setExplanation(prev => prev + chunk)
}submitPrivateReportCloak()
Submit an anonymous threat report using a Cloak UTXO shielded pool with Groth16 proofs. The caller's identity is never linked to the report on-chain.
import { submitPrivateReportCloak } from '@walour/sdk'
const result = await submitPrivateReportCloak(
address: string, // Solana address or domain
label: 'drainer' | 'rug' | 'phishing_domain' | 'malicious_token',
confidence: number, // 0-1
options: {
connection: Connection,
payer: Keypair,
depositLamports?: number // optional deposit for ZK proof
}
)
interface PrivateReportCloakResult {
txSignature: string
viewingKey: string // base64 Groth16 proof
}Caching
Every SDK function is cache-first. On a miss the SDK fetches all sources in parallel, writes to Upstash Redis, then returns. Subsequent calls within the TTL window skip all network round-trips.
| Function | TTL | Warm latency |
|---|---|---|
| checkTokenRisk() | 60s | under 100ms |
| lookupAddress() | 300s | under 100ms |
| checkDomain() | 3600s | under 100ms |
| decodeTransaction() | 86400s | under 100ms |
Bypass cache
Pass skipCache: true in the options argument to any function to force a fresh fetch.
Cache invalidation
When a report is submitted via submitPrivateReportCloak(), the cache entry for that address is evicted immediately so the next lookup reflects the new signal.
Circuit Breakers
Every external provider is wrapped in a circuit breaker. If a provider fails 3 times within 60 seconds, its circuit opens and calls are routed to the next provider in the fallback chain automatically.
RPC fallback chain
| Priority | Provider | Notes |
|---|---|---|
| 1 | Helius | Primary, enhanced APIs, ALT resolution |
| 2 | Triton | High-availability fallback |
| 3 | Solana public RPC | Last resort, rate-limited |
Inspect circuit state
import { getRpcEndpoints } from '@walour/sdk'
// Check which providers are currently healthy
const endpoints = getRpcEndpoints()
// Returns RpcEndpoint[] ordered by priority with circuit state
// Circuit states:
// CLOSED = healthy, accepting requests
// OPEN = tripped, routing to next provider
// HALF_OPEN = probing recovery with a single test request