Walpulse logo

Integration example: paid sanctions screening inside a client report

A signed, pay-per-call sanctions verdict from nsgoods, embedded and cryptographically verified inside a Walpulse wallet-analysis report. Published with Walpulse's permission.

The pattern

Walpulse runs on-chain wallet analysis. When a report needs a sanctions check, a server-side edge function calls the nsgoods Sanctions Screen endpoint, pays for the single call over the x402 protocol, verifies the response signature, and renders the resulting verdict inside the report. Because the verdict is signed, Walpulse's UI can display SIGNATURE VERIFIED YES — the reader is not trusting Walpulse's word or ours, they are checking a signature.

1 · Edge functionA Supabase edge function (Deno) makes a server-side request for one address.
2 · x402 402 → payThe endpoint answers 402 Payment Required; the client pays the exact amount in USDC and retries.
3 · Verify signatureThe 200 response is a signed verdict; the function recovers the signer with EIP-191 and checks it against the pinned address.
4 · Render verdictThe verified verdict (and, for the four-list endpoint, the per-list breakdown) is shown in the report.

A minimal, generic example (Deno / TypeScript)

The snippet below is a representative minimal example written by nsgoods to illustrate the call, the payment, and — the part that matters — the signature check. It is not Walpulse's code and does not describe their internals; any x402-capable HTTP client works in place of x402Fetch.

import { recoverMessageAddress, hashMessage } from "npm:viem";

// The signer that nsgoods publishes for the sanctions family, pinned in the
// signed proof manifest at https://x402.nsgoods.org/proof/index.json
const PINNED_SIGNER = "0x57fF0F084Cba33e6761503f90eEF0Da9F159350c";

// x402Fetch: any wrapper that, on a 402, pays the quoted USDC amount and retries.
// (Base or Solana rail; the amount and payTo come from the 402 challenge itself.)
async function screen(address: string) {
  const url = `https://sanctions.nsgoods.org/screen?address=${address}&chain=ethereum`;
  const res = await x402Fetch(url);            // handles 402 -> pay -> 200
  const body = await res.json();

  // Verify the signature: strip the two signature fields, canonicalise the rest
  // exactly as the signer did (sorted keys, compact separators), then EIP-191 recover.
  const { signature, signed_by, ...rest } = body;
  const canonical = canonicalJson(rest);        // JSON.stringify with sorted keys, no spaces
  const recovered = await recoverMessageAddress({ message: canonical, signature });

  if (recovered.toLowerCase() !== PINNED_SIGNER.toLowerCase()) {
    throw new Error("signature verification failed");   // never render an unverified verdict
  }
  return body;                                  // verdict is now trustworthy: render it
}

// Deterministic canonical JSON: object keys sorted recursively, compact separators.
function canonicalJson(v: unknown): string {
  if (Array.isArray(v)) return "[" + v.map(canonicalJson).join(",") + "]";
  if (v && typeof v === "object")
    return "{" + Object.keys(v as object).sort()
      .map(k => JSON.stringify(k) + ":" + canonicalJson((v as any)[k])).join(",") + "}";
  return JSON.stringify(v);
}

Two endpoints: pick the coverage the report needs

nsgoods exposes two sanctions endpoints. They share the same signer, the same signature scheme, and the same dual-rail payment (USDC on Base or Solana). They differ only in list coverage and price, so a report can route per call.

EndpointCoveragePrice / callWhen to use
/screen OFAC SDN exact-address, all chains $0.005 The default check: is this exact address on the U.S. Treasury SDN list.
/screen-multi Four lists: OFAC · UN · EU · UK (HMT/OFSI), each with its own version date $0.01 When a report needs multi-jurisdiction coverage in one signed call.

What the four-list response looks like

The example below is taken from the public /screen-multi/preview — a fixed demo address, never client data. The headline verdict keeps the exact /screen semantics (deny only on an OFAC SDN match); the additive lists[] block reports each list separately, with matched/clear and that list's version date, all inside the one signed body.

ListResultList version dateSource note
OFAC (US SDN)matched2026-09-04—
UN Consolidatedclear2026-09-07—
EU FSFclear2026-08-05official (public token)
UK OFSI (HMT)clear03/06/2026—

Two principles are worth calling out:

Why the signature matters

Every verdict is signed with an EIP-191 personal-sign over the canonical JSON body. The signer address for the sanctions family is published, not asserted: it is pinned in the signed proof manifest at x402.nsgoods.org/proof/index.json as 0x57fF0F084Cba33e6761503f90eEF0Da9F159350c.

To verify any response independently:

A verdict that does not recover to the pinned signer should never be rendered. That single check is what turns "a vendor said clean" into "a signed statement anyone can verify."

Corpus transparency

Each list is backed by a public, hash-chained log of its versions, so a claim like "checked against the OFAC list of 2026-09-04" can be verified independently rather than taken on trust:

Update 29 September 2026: the UK leg now uses the UK Sanctions List (FCDO). The OFSI Consolidated List closed on 28 January 2026.