allet address serves as identity. The first request returns a 402 Payment Required response containing the exact fee and EIP-3009 transfer parameters. The client signs the transfer, retries the request, and receives the signed verdict.
import { createWalletClient, http, parseEther } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';
import { verifyEd25519 } from '@noble/ed25519';
interface PreTradePayload {
chain: 'base' | 'solana';
contract: string;
intended_trade_usd: number;
policy: 'strict' | 'balanced' | 'aggressive';
}
interface PreTradeResponse {
policy_recommendation: 'block' | 'caution' | 'allow';
risk_score: number;
max_suggested_exposure_usd: number;
reason: Array<{ code: string; severity: 'low' | 'medium' | 'high' }>;
scan_id: string;
signature: string;
key_fingerprint: string;
}
export class TradeValidator {
private readonly endpoint = 'https://rugguard.redfleet.fr/v1/pretrade/check';
private readonly wallet: ReturnType<typeof privateKeyToAccount>;
constructor(privateKeyHex: string) {
this.wallet = privateKeyToAccount(`0x${privateKeyHex}`);
}
async evaluate(payload: PreTradePayload): Promise<PreTradeResponse> {
const initialRes = await fetch(this.endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (initialRes.status === 402) {
const paymentParams = await initialRes.json();
await this.settlePayment(paymentParams);
const finalRes = await fetch(this.endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Payment-Settled': 'true'
},
body: JSON.stringify(payload),
});
if (!finalRes.ok) throw new Error('Validation failed after payment');
const data = await finalRes.json();
await this.verifySignature(data);
return data as PreTradeResponse;
}
throw new Error('Unexpected response status');
}
private async settlePayment(params: any) {
const client = createWalletClient({
account: this.wallet,
chain: base,
transport: http(),
});
await client.sendTransaction({
to: params.recipient,
value: parseEther(params.amount),
data: params.eip3009_payload,
});
}
private async verifySignature(data: any) {
const payloadBytes = new TextEncoder().encode(
`${data.scan_id}:${data.risk_score}:${data.policy_recommendation}`
);
const isValid = verifyEd25519(
Buffer.from(data.signature, 'hex'),
payloadBytes,
Buffer.from(data.key_fingerprint, 'hex')
);
if (!isValid) throw new Error('Cryptographic verification failed');
}
}
Step 2: Implement Policy Routing
The verdict should never be treated as a simple boolean. The caution state requires dynamic exposure capping. The router below enforces policy boundaries before delegating to execution modules.
export class ExecutionRouter {
constructor(private validator: TradeValidator) {}
async routeBuySignal(tokenAddress: string, targetUsd: number, chain: 'base' | 'solana') {
const result = await this.validator.evaluate({
chain,
contract: tokenAddress,
intended_trade_usd: targetUsd,
policy: 'balanced',
});
switch (result.policy_recommendation) {
case 'allow':
return this.executeSwap(tokenAddress, targetUsd);
case 'caution':
const cappedAmount = Math.min(targetUsd, result.max_suggested_exposure_usd);
console.warn(`[CAUTION] Reducing exposure from $${targetUsd} to $${cappedAmount}`);
return this.executeSwap(tokenAddress, cappedAmount);
case 'block':
console.error(`[BLOCKED] ${result.reason.map(r => r.code).join(', ')}`);
return { status: 'rejected', scan_id: result.scan_id };
default:
throw new Error('Unknown verdict state');
}
}
private async executeSwap(address: string, amount: number) {
// Delegate to DEX router or aggregator
return { status: 'submitted', token: address, amount };
}
}
Architecture Decisions & Rationale
- Wallet-as-Identity: Eliminates API key rotation, rate-limit management, and centralized billing. The 402 flow ensures exact micro-payments without over-provisioning.
- Ed25519 Signatures: Chosen for fast verification and compact signatures. The
scan_id, risk_score, and policy_recommendation are hashed together to create a non-repudiable audit trail. Auditors can later prove the agent received and respected the warning.
- Deterministic Heuristics Over LLMs: The hot path contains zero probabilistic models. Checks like
LP_NOT_LOCKED, MINT_AUTHORITY_ACTIVE, and bytecode MinHash against known rug patterns execute in milliseconds. This guarantees predictable latency and eliminates hallucination risk.
- Policy-Driven Exposure Capping: Instead of hard-blocking
caution verdicts, the system scales position size. This preserves trading velocity while respecting risk thresholds.
Pitfall Guide
1. Skipping Signature Verification
Explanation: Accepting the API response without cryptographic validation opens the agent to man-in-the-middle attacks or compromised proxy layers. An attacker could forge an allow verdict.
Fix: Always verify the Ed25519 signature against the published public key before routing. Cache the pubkey locally and rotate verification logic if the fingerprint changes.
2. Treating "Caution" as "Allow"
Explanation: Many teams hardcode caution to proceed with full position size, ignoring max_suggested_exposure_usd. This defeats the purpose of the firewall.
Fix: Implement dynamic position scaling. If the API suggests $20 exposure but the model requested $100, cap the trade at $20 and log the discrepancy for model retraining.
3. Misinterpreting Heuristic Recall Metrics
Explanation: Public metrics show HONEYPOT_TAX_HIGH at 0% recall on confirmed rugs. Teams assume the heuristic is broken.
Fix: Understand measurement bias. By the time a token is confirmed rugged, the contract is drained and honeypot tax signals are removed. The service compensates with forward sampling at T+0, T+7, T+14, and T+30. Rely on the composite risk score, not isolated heuristic percentages.
4. Hardcoding Payment Amounts
Explanation: Assuming a fixed $0.01 fee per call breaks when the service introduces tiered pricing or network congestion adjustments.
Fix: Parse the exact fee from the 402 response headers. Validate the EIP-3009 nonce and expiration window before signing. Implement retry logic with exponential backoff for failed settlements.
5. Bypassing Validation for "Whitelisted" Contracts
Explanation: Maintaining a static allowlist of trusted tokens ignores deployer history changes and secondary market manipulations.
Fix: Implement periodic re-scanning. Even allowlisted contracts should be validated before large trades. Track deployer_rug_history and owner_renounced status dynamically.
6. Ignoring Chain-Specific Heuristic Counts
Explanation: Base supports 14 deterministic checks, while Solana SPL tokens only support 5. Applying identical risk thresholds across chains creates false confidence on Solana.
Fix: Adjust policy weights per chain. Require lower risk_score thresholds for Solana trades or mandate additional off-chain due diligence for SPL tokens.
7. Neglecting Cold Start Latency
Explanation: First-time scans take up to 5 seconds. High-frequency agents may timeout or duplicate requests.
Fix: Implement request coalescing and a local LRU cache keyed by contract + chain. Cache responses for 15β30 minutes depending on token age. Use background prefetching for tokens appearing in model context windows.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| High-frequency arbitrage bot | Cache-heavy validation with 15-min TTL | Latency sensitivity outweighs fresh scan needs | ~$0.003 per trade (cache hit) |
| Low-frequency swing trading | Full cold scan per signal | Maximum safety for larger position sizes | ~$0.01 per trade |
| Multi-chain deployment (Base + Solana) | Chain-aware policy routing with adjusted thresholds | Solana has fewer heuristics; requires stricter scoring | ~$0.01 Base / ~$0.01 Solana |
| Institutional compliance | Ed25519 audit logging + manual override queue | Cryptographic proof satisfies regulatory due diligence | ~$0.01 + operational overhead |
Configuration Template
// trade-validator.config.ts
export const ValidatorConfig = {
api: {
endpoint: 'https://rugguard.redfleet.fr/v1/pretrade/check',
pubkeyUrl: 'https://rugguard.redfleet.fr/v1/pubkey',
metricsUrl: 'https://rugguard.redfleet.fr/v1/metrics',
},
wallet: {
privateKeyEnv: 'VALIDATOR_WALLET_KEY',
chain: 'base',
paymentMethod: 'EIP-3009',
},
policies: {
strict: {
maxRiskScore: 30,
allowCaution: false,
exposureCapMultiplier: 0.5,
},
balanced: {
maxRiskScore: 60,
allowCaution: true,
exposureCapMultiplier: 1.0,
},
aggressive: {
maxRiskScore: 85,
allowCaution: true,
exposureCapMultiplier: 1.5,
},
},
cache: {
ttlSeconds: 900,
maxSize: 500,
coldStartTimeoutMs: 5000,
},
chains: {
base: { heuristicCount: 14, defaultPolicy: 'balanced' },
solana: { heuristicCount: 5, defaultPolicy: 'strict' },
},
};
Quick Start Guide
- Provision a payment wallet: Generate an EVM-compatible private key and fund it with a small amount of Base ETH. Export it as
VALIDATOR_WALLET_KEY.
- Install dependencies: Add
viem, @noble/ed25519, and your preferred HTTP client to your agent project.
- Initialize the client: Instantiate
TradeValidator with your private key and call evaluate() with the target contract, chain, and intended trade size.
- Route the verdict: Pass the response to your execution router. Respect
block verdicts, cap caution trades at max_suggested_exposure_usd, and proceed on allow.
- Enable audit logging: Store
scan_id, signature, and key_fingerprint alongside every trade record. Use the verifier CLI or custom script to periodically validate signature integrity against the published pubkey.