tion demonstrates a production-ready pattern using TypeScript.
Architecture Decisions
- Service Encapsulation: Trust evaluation is isolated in a dedicated class to prevent payment logic from becoming coupled with risk assessment. This allows independent scaling, caching strategies, and fallback mechanisms.
- Tier-Based Routing: Raw scores (0β100) are mapped to discrete risk tiers. This simplifies decision logic and prevents threshold drift. Tiers align with operational actions:
TRUSTED (proceed), MODERATE (proceed with logging), CAUTION (require secondary validation), HIGH_RISK (abort).
- Caching Strategy: Results are cached for 1 hour per domain. This reduces redundant WHOIS queries, prevents rate limiting, and lowers cumulative costs. Cache invalidation is handled automatically by the API, but local TTL enforcement prevents stale data from persisting beyond operational windows.
- Cost-Proportional Gating: Risk tolerance scales with transaction value. High-value transfers require stricter thresholds, while micro-transactions can tolerate moderate risk. This prevents over-engineering validation for low-stakes operations.
Implementation
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
import { createHash } from "crypto";
interface TrustEvaluationResult {
domain: string;
score: number;
tier: "TRUSTED" | "MODERATE" | "CAUTION" | "HIGH_RISK";
breakdown: {
domainAge: number;
tld: number;
dnsPresence: number;
registrar: number;
};
metadata: {
checkedAt: string;
cached: boolean;
apiVersion: string;
};
}
interface TransactionGateConfig {
agentPrivateKey: string;
trustEndpoint: string;
cacheTTLSeconds: number;
thresholds: {
highRiskAbort: number;
cautionEscalate: number;
moderateLog: number;
};
}
class TransactionGatekeeper {
private paymentClient: ReturnType<typeof wrapFetchWithPayment>;
private cache: Map<string, { result: TrustEvaluationResult; expiresAt: number }>;
private config: TransactionGateConfig;
constructor(config: TransactionGateConfig) {
this.config = config;
this.cache = new Map();
const signer = privateKeyToAccount(config.agentPrivateKey as `0x${string}`);
const client = new x402Client();
registerExactEvmScheme(client, { signer });
this.paymentClient = wrapFetchWithPayment(fetch, client);
}
private generateCacheKey(domain: string): string {
return createHash("sha256").update(domain).digest("hex");
}
private getCachedResult(domain: string): TrustEvaluationResult | null {
const key = this.generateCacheKey(domain);
const entry = this.cache.get(key);
if (entry && Date.now() < entry.expiresAt) {
return entry.result;
}
this.cache.delete(key);
return null;
}
private setCacheResult(domain: string, result: TrustEvaluationResult): void {
const key = this.generateCacheKey(domain);
this.cache.set(key, {
result,
expiresAt: Date.now() + this.config.cacheTTLSeconds * 1000,
});
}
async evaluateDomainRisk(targetUrl: string): Promise<TrustEvaluationResult> {
const domain = new URL(targetUrl).hostname;
const cached = this.getCachedResult(domain);
if (cached) return cached;
const response = await this.paymentClient(
`${this.config.trustEndpoint}?domain=${encodeURIComponent(domain)}`
);
if (!response.ok) {
throw new Error(`Trust evaluation failed: ${response.status} ${response.statusText}`);
}
const payload: TrustEvaluationResult = await response.json();
this.setCacheResult(domain, payload);
return payload;
}
async gateTransaction(
targetUrl: string,
transactionValueUSDC: number
): Promise<{ action: "proceed" | "escalate" | "abort"; reason: string }> {
const evaluation = await this.evaluateDomainRisk(targetUrl);
const { score, tier } = evaluation;
// Cost-proportional threshold adjustment
const dynamicThreshold = transactionValueUSDC > 1.0 ? 70 : 50;
if (score <= this.config.thresholds.highRiskAbort || tier === "HIGH_RISK") {
return { action: "abort", reason: `Domain blocked: ${tier} (score: ${score})` };
}
if (score < dynamicThreshold) {
return { action: "escalate", reason: `Score below dynamic threshold: ${score} < ${dynamicThreshold}` };
}
if (tier === "CAUTION") {
return { action: "escalate", reason: `Caution tier triggered: ${score}` };
}
return { action: "proceed", reason: `Validation passed: ${tier} (${score})` };
}
}
Rationale
The TransactionGatekeeper class separates concerns cleanly. Payment client initialization happens once during construction, preventing repeated cryptographic setup. Cache management uses SHA-256 hashing to normalize domain variations (e.g., www.example.com vs example.com). The gateTransaction method applies dynamic thresholds based on transaction value, ensuring that risk tolerance aligns with financial exposure. All decisions return structured actions (proceed, escalate, abort) with explicit reasons, enabling downstream logging and audit compliance.
Pitfall Guide
1. Treating Trust Scores as Absolute Verdicts
Explanation: The scoring model evaluates structural signals, not content or behavioral intent. A domain with a 90/100 score can still host malicious payloads if compromised after initial validation.
Fix: Combine trust scoring with content validation, signature verification, and transaction monitoring. Use the score as a pre-filter, not a final authorization.
2. Ignoring WHOIS Privacy Redaction
Explanation: GDPR and registrar privacy policies frequently redact registration dates. When age data is unavailable, the API defaults to 0 points, artificially lowering the score for legitimate privacy-conscious operators.
Fix: Implement fallback logic that weights DNS presence and registrar reputation higher when age data is missing. Log redaction events separately to avoid penalizing compliant domains.
3. Hard-Coding Static Thresholds
Explanation: Fixed score cutoffs fail to account for transaction context. A 45/100 score might be acceptable for a $0.01 API call but catastrophic for a $500 data purchase.
Fix: Use cost-proportional gating. Scale minimum acceptable scores based on transaction value, agent risk profile, and historical success rates.
4. Bypassing Cache Invalidation
Explanation: Relying solely on API-side caching without local TTL enforcement can lead to stale evaluations. Domains can change registrars, DNS records, or ownership within hours.
Fix: Enforce local cache expiration matching the API's 1-hour window. Implement background refresh for high-value domains to ensure evaluations remain current without blocking transaction flows.
5. Mixing Testnet and Mainnet Configurations
Explanation: Deploying testnet x402 facilitator URLs or Base Sepolia RPC endpoints in production causes silent payment failures or fund loss. Environment variable leakage is a common deployment error.
Fix: Use strict environment validation at startup. Fail fast if NETWORK or FACILITATOR_URL mismatch expected production values. Implement configuration guards that reject testnet parameters in production builds.
6. Over-Reliance on TLD Reputation
Explanation: While .xyz and .tk correlate with higher fraud rates, legitimate services increasingly adopt alternative TLDs. Filtering solely on TLD creates false positives.
Fix: Treat TLD as one weighted signal among four. Require corroborating evidence (DNS records, registrar legitimacy, age) before escalating or aborting based on TLD alone.
7. Unhandled x402 Payment Failures
Explanation: Trust evaluation requires payment. If the agent wallet lacks sufficient USDC, or the facilitator is unreachable, the evaluation fails silently or throws unhandled exceptions.
Fix: Wrap payment calls in retry logic with exponential backoff. Implement wallet balance checks before evaluation. Define fallback behavior (e.g., queue evaluation, alert operator) when payment infrastructure is degraded.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Micro-transactions (<$0.10) | MODERATE threshold with logging | Low financial exposure justifies faster execution | Negligible (0.003 USDC/query) |
| High-value transfers (>$100) | TRUSTED threshold + secondary validation | Maximizes fraud prevention before significant fund commitment | Moderate (additional validation steps) |
| Internal agent-to-agent calls | Bypass trust scoring | Controlled environment eliminates external domain risk | Zero |
| Sandbox/Testing | Use Base Sepolia testnet + faucet | Prevents mainnet fund consumption during development | Zero (testnet USDC) |
| New startup onboarding | CAUTION tier with manual review | Low age score doesn't indicate fraud; requires contextual verification | Low (delayed execution) |
Configuration Template
# x402 Protocol Configuration
AGENT_PRIVATE_KEY=0x_your_production_private_key
NETWORK=eip155:8453
FACILITATOR_URL=https://x402.org/facilitator
# Trust Evaluation Service
TRUST_ENDPOINT=https://trustsource.cc/trustscore
CACHE_TTL_SECONDS=3600
# Risk Thresholds
HIGH_RISK_ABORT_THRESHOLD=24
CAUTION_ESCALATE_THRESHOLD=49
MODERATE_LOG_THRESHOLD=74
# Transaction Context
DEFAULT_MIN_SCORE=50
HIGH_VALUE_THRESHOLD_USDC=1.0
HIGH_VALUE_MIN_SCORE=70
Quick Start Guide
- Install Dependencies: Run
npm install @x402/fetch @x402/evm/exact/client viem to add protocol and cryptographic packages.
- Configure Environment: Populate the
.env template with your agent's private key, network settings, and threshold values. Ensure NETWORK matches your target chain.
- Initialize Gatekeeper: Instantiate
TransactionGatekeeper with your configuration. Verify wallet balance and facilitator connectivity before proceeding.
- Execute Validation: Call
gateTransaction(targetUrl, amountUSDC) before any payment. Handle the returned action (proceed, escalate, abort) in your transaction router.
- Monitor & Iterate: Log evaluation outcomes, track cache hit rates, and adjust thresholds based on operational data. Review HIGH_RISK triggers weekly to refine risk parameters.