← Sol Node Labs

Add a fail-closed honeypot check to a Base trading bot

A successful quote or simulation does not establish that a token is safe to trade. Before signing, a bot should combine sellability, taxes, privileged controls, verified source, holder concentration, liquidity, and evidence availability—and block when critical evidence is missing.

Operating rule: risk screening may block a trade, but it should never be the component that signs or broadcasts one.

1. Test a contract against current evidence

The public endpoint supports Base and Ethereum and returns JSON to API clients. The trial is limited to three checks per IP per hour.

curl --get 'https://solnodelabs.com/free/token-risk' \
  --data-urlencode 'chain=base' \
  --data-urlencode 'address=0x4200000000000000000000000000000000000006' \
  -H 'Accept: application/json'

2. Add a deterministic Node.js gate

const BLOCKING_FLAGS = new Set([
  "honeypot_simulation", "cannot_sell", "cannot_buy",
  "owner_balance_control", "buy_tax_extreme", "sell_tax_extreme"
]);

export async function tokenPreflight(address) {
  if (!/^0x[a-fA-F0-9]{40}$/.test(address)) {
    return { execute: false, reason: "invalid_token_address" };
  }

  const url = new URL("https://solnodelabs.com/free/token-risk");
  url.searchParams.set("chain", "base");
  url.searchParams.set("address", address);

  let response;
  try {
    response = await fetch(url, {
      headers: { accept: "application/json" },
      signal: AbortSignal.timeout(10_000)
    });
  } catch {
    return { execute: false, reason: "risk_provider_unavailable" };
  }

  if (!response.ok) {
    return { execute: false, reason: `risk_http_${response.status}` };
  }

  const evidence = await response.json();
  const missingCriticalEvidence = [
    "tokenMetadata", "contractMetadata", "marketData", "goPlusSecurity"
  ].some(key => evidence.dataCompleteness?.[key] !== true);

  if (missingCriticalEvidence) {
    return { execute: false, reason: "risk_evidence_incomplete", evidence };
  }

  const blocking = evidence.flags?.filter(flag =>
    flag.severity === "critical" || BLOCKING_FLAGS.has(flag.code)
  ) ?? [];

  if (blocking.length) {
    return { execute: false, reason: "blocking_token_risk", blocking, evidence };
  }

  if (evidence.riskScore >= 40) {
    return { execute: false, reason: "risk_threshold_exceeded", evidence };
  }

  return { execute: true, evidence };
}

3. Enforce the gate before signing

const preflight = await tokenPreflight(candidateToken);

if (!preflight.execute) {
  auditLog.write({ token: candidateToken, ...preflight });
  return { status: "blocked", reason: preflight.reason };
}

// Position limits, slippage checks, approval limits, and transaction
// simulation remain separate controls after this point.
return buildUnsignedSwap(candidateToken);

4. Preserve the decision evidence

Store the normalized chain and address, analysis time, policy version, completeness fields, flags, score, and final action. Do not cache a low-risk result indefinitely: ownership, taxes, liquidity, and market conditions can change.

5. Production considerations

Need this fitted to an existing bot?

The $49 USDC pilot adds one fail-closed preflight, evidence logging, and focused failure-path tests to a supplied public repository or minimal reproduction. Scope is agreed before payment.

Create a private pilot ticket