SendCheck — EVM address verification (x402)

org.pennyforge.api由 api.pennyforge.org 發布官方發布者v1.0.0更新於 Oct 24, 2026

Pre-send EVM address checks: free pre-check, $0.01 check, $0.05 deep scan. x402: payment is auth.

已驗證Streamable HTTP可網頁執行金融安全與監控

概覽

AI 產生的概覽

遠端 MCP 伺服器,在付款前檢查 EVM 位址:免費 EIP-55 校驗和驗證,以及以 x402 付費的錢包類型、活動與餘額查核。

可以做什麼
  • 免費驗證 EVM 位址的 EIP-55 校驗和,找出打錯字、漏字元與大小寫遭竄改的情況。
  • 付費查核單一鏈上的一個位址:是錢包還是合約、轉出活動、原生幣與 USDC/USDT 餘額,並給出分級結論、原因與下一步建議。
  • 付費深度掃描涵蓋 Base、Ethereum、Arbitrum、Optimism 與 Polygon 五條鏈,包含錯誤網路偵測與代幣不符標記。
  • 付費採兩階段:第一次呼叫回傳確切的 x402 付款挑戰,第二次帶付款資訊呼叫才取得結果。
適用情境
適合在助理或代理即將轉出 USDC 或其他 EVM 資產前,先確認目標位址格式正確,並以少許費用判斷它是否是正確鏈上可信的收款方。免費檢查足以發現打錯字;金額較大的轉帳更適合使用付費查核。
執行需求
遠端 streamable HTTP 端點;未宣告需要帳號、API 金鑰或環境變數。免費驗證無需任何設定。付費查核透過 Base 上的 x402(EIP-3009)以 USDC 計價,每次 0.01 美元與 0.05 美元,因此用戶端需要已儲值的錢包與 x402 付款支援。需要能連線至該服務主機的網路。
安裝前請注意
付費工具會實際花錢:單鏈查核 0.01 美元、深度掃描 0.05 美元,皆以 Base 上的 USDC 支付;兩階段流程代表必須先回應付款挑戰才能取得結果。校驗和通過只表示位址格式正確,不代表收款方正確。結論僅供參考,且來自個人第三方服務,簽署轉帳前應只把它當作一項訊號。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 SendCheck — EVM address verification (x402),將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Web executable,透過 Streamable HTTP。 遠端服務在工作區中設定後即可從網頁執行環境執行。

交給代理程式安裝

把這段提示詞貼上到 Claude Code、Codex、Cursor 等代理程式,或 SourceWeft 對話中。代理程式會讀取該伺服器的安裝指南,先向你展示發布者、端點和登入方式,經你同意後再把它新增到你的客戶端。金鑰不會寫進任何命令。

閱讀 https://sourceweft.com/mcp/org.pennyforge.api%2Fsendcheck/install.md,並按其中說明把這個 MCP 伺服器接入我的客戶端。修改任何設定之前,先告訴我它的發布者、端點和登入方式,等我確認。不要把 API 金鑰寫進命令或我會提交的檔案;問我金鑰應從哪裡取得。

檢視代理程式所依據的安裝指南

其他 MCP 客戶端

把它新增到你客戶端的 mcpServers 設定中。

{
  "mcpServers": {
    "sendcheck-evm-address-verification-x402": {
      "type": "http",
      "url": "https://api.pennyforge.org/mcp"
    }
  }
}

README

sendcheck-verify

[Listed on mcpservers.org]

Verify before you pay. A zero-dependency client for the free GET /validate pre-check on the SendCheck x402 API — the 10-line guard that stops your agent from signing a payment to a mistyped, mismatched, or tampered settlement address.

When an x402 client pays a service, it pays the settlement address the service told it about. A single wrong character (a case-flip, a dropped digit, a substituted address) and the USDC goes somewhere else forever. /validate is the free, no-key, no-account pre-check: is this address even a well-formed, correctly-cased EVM address before you put real money on it?

Since v0.2.0 the package does one more thing: it verifies the service's signed service card — an ES256 JWS over the payTo and the per-route maxPrice that the worker re-signs at every deploy — so a client that pinned the public key once can confirm the challenge's payTo cryptographically, before paying. That is the reference implementation of the drift-proof check (the "signed service card" from the address-drift article).

Why this exists

SendCheck's paid endpoints cost $0.01 (one chain) or $0.05 (all five chains) via x402 — USDC on Base, no account, no API key. But most "is this address garbage?" checks don't need chain data: the EIP-55 checksum alone catches the most common, most expensive failures (typos, dropped characters, tampered casing). So /validate is free, and this package makes calling it one line.

The signed service card (v0.2.0)

Pre-payment address checks have a hole: the "expected payTo" a client compares against usually comes from the same discovery doc it just fetched — so a compromised service can point you at its own new address, and for a service that rotates its address often, every remembered address is stale before the check finishes.

SendCheck closes that hole by signing its payment details. The worker signs the settlement address and the per-route max prices with a stable ES256 (EC P-256) key at every deploy, and serves the signed block { jws, key } in three places:

<origin>/.well-known/x402   →  top-level "attestation"<origin>/openapi.json       →  info["x-attestation"]the 402 challenge itself    →  extensions["x-sendcheck-attestation"]

SendCheck signs its payment details with a stable key at every deploy, so any client that has pinned that key once can cryptographically confirm — before paying — that the address in the payment challenge is one we signed; first contact is still trust-on-first-use.

What the signature buys: key continuity for returning, stateful clients — a compromised worker/DNS/CDN cannot silently redirect your payments, and a rotated key is a loud event you can flag. What it does not buy (we under-claim on purpose): first-visit authenticity (that is TOFU), protection for ephemeral agents with no pin, or a price below the signed maxPrice.

The signed payload also carries a per-route maxPrice, so verifyChallenge catches a challenge that quietly raises the price above what the key signed.

The drift-proof wiring

js
import { checkService, verifyChallenge, ATTESTATION_EXTENSION } from "@pennyforgeorg/sendcheck-verify";
const origin = "https://api.pennyforge.org";
// 1) Once: pin the public key (first contact = trust-on-first-use).const first = await checkService(origin);          // { ok, keyId, key: JWK, payTo, … }// persist { kid: first.keyId, x: first.key.x, y: first.key.y } somewhere durable
// 2) Every call: confirm the card still verifies and the key has not rotated.const svc = await checkService(origin, { pinnedKey: storedPin });if (svc.keyChanged) { /* loud: the key rotated — inspect before trusting */ }if (!svc.ok) throw new Error("service card not valid: " + svc.attestation.reason);
// 3) At payment time: bind the 402 CHALLENGE to the signed card.//    (challenge = decoded base64 PAYMENT-REQUIRED header, or the 402 JSON body)const verdict = await verifyChallenge(challenge, svc.block, { origin });if (!verdict.valid) throw new Error("challenge not signed: " + verdict.reason);// …now sign the EIP-3009 payment to challenge.accepts[0].payTo.

verifyChallenge checks the challenge's payTo against the signed one and the challenge price against the signed maxPrice for the challenged route. Failure reasons: signature_invalid, challenge_payto_mismatch, price_exceeded, route_not_signed, expired, iat_future, service_mismatch, kid_mismatch, malformed.

Install

bash
# GitHub npm registry (v0.2.0 published 2026-09-08)npm install @pennyforgeorg/sendcheck-verify --registry https://npm.pkg.github.com

GitHub's npm registry asks for any valid GitHub token even on public reads — put //npm.pkg.github.com/:_authToken=<your GH token> in your .npmrc (classic PAT or fine-grained with Packages:Read works). No token handy? Install from the public repo:

bash
npm install github:pennyforgeorg/sendcheck-verify

Requires Node 18+ (uses global fetch). Zero dependencies. MIT.

Quickstart

js
import { verifyBeforePay } from "@pennyforgeorg/sendcheck-verify";
// Throws SendCheckVerifyError unless the address passes the free pre-check.await verifyBeforePay("0x9504A5939AB5be2B2B1F8beA7D7ebeCcd96c485D");
// …then sign your x402 payment.

CommonJS:

js
const { verifyBeforePay } = require("@pennyforgeorg/sendcheck-verify");

Typical wiring inside an x402 client:

js
import { verifyBeforePay } from "@pennyforgeorg/sendcheck-verify";import { wrapFetchWithPayment } from "x402-fetch"; // or your own x402 stack
const pay = wrapFetchWithPayment(fetch, { wallet });
async function call(serviceUrl, address, body) {  await verifyBeforePay(address);          // free — catch bad addresses here  const res = await pay(serviceUrl, {      // paid only when the address is clean    method: "POST",    headers: { "content-type": "application/json" },    body: JSON.stringify(body),  });  return res.json();}

If you already normalize addresses on your side and just want the result object (no throw):

js
const r = await verifyBeforePay(addr, { throwOnError: false });// { valid, status: "valid" | "plain" | "mismatch" | "format" | "network" | "http",//   message, normalized, checked }

API

verifyBeforePay(address, opts?)

  • address — EVM address, 0x + 40 hex.
  • opts.baseUrl — override the API host (default: the live worker).
  • opts.fetchFn — inject a fetch implementation (tests / proxies).
  • opts.throwOnError — default true; pass false to always get the result back.

Behavior notes:

  • A string that isn't even 0x+40-hex fails the local format check and throws (or returns checked: false) without sending a request — you never pay a round trip for garbage input.
  • status: "plain" = valid address, all-lowercase or all-uppercase (no case information). That's fine to pay; the response includes the normalized EIP-55 form.
  • status: "mismatch" = the casing does not match the address hash — usually a typo or tampering. This is the one to treat as hard-fail.

looksLikeAddress(addr) — offline format check, no network.

verifyAttestation(attestation, opts?)

Verify a signed block { jws, key } (ES256 JWS over a canonical-JSON payload). Pure + async (WebCrypto); no network. Returns { valid, reason?, payload, keyId, signedAt, expiresAt, warning? }. opts: origin, expectedPayTo, challengePayTo, route, challengeMaxAmount (atomic USDC string), now (unix seconds, for tests). Time windows: exp hard-reject with a 60s clock-skew grace; iat may be up to 60s in the future; an iat older than 30 days is valid but returns warning: "stale". reason codes: malformed, header_mismatch, kid_mismatch, signature_invalid, service_mismatch, payto_mismatch, challenge_payto_mismatch, route_not_signed, price_exceeded, expired, iat_future.

checkService(origin, opts?)

Fetch <origin>/.well-known/x402, verify its signed card against the doc's own advertised payTo, and report whether a pinned key changed. Returns { ok, origin, payTo, keyId, signedAt, expiresAt, warning?, attestation, keyChanged, endpoints: [{resource, amount}] }. opts.fetchFn (inject fetch), opts.pinnedKey ({kid} or {kid,x,y}), opts.throwOnError. keyChanged is the loud rotation signal.

verifyChallenge(challenge, attestation, opts?)

Bind a 402 challenge to a signed card. challenge is the decoded 402 declaration ({ resource: { url }, accepts: [{ payTo, maxAmountRequired }] }) — decode the base64 PAYMENT-REQUIRED header with JSON.parse(Buffer.from(hdr, "base64").toString("utf8")). The route is derived from resource.url. Returns the verifyAttestation result.

ATTESTATION_EXTENSION — "x-sendcheck-attestation", the extension name

the signed block rides in under inside the 402 challenge's extensions map.

canonicalJson(value) — RFC-8785-style canonical JSON (recursively sorted

keys) — exported for anyone re-implementing the signer.

SendCheckVerifyError — thrown on non-valid results; err.result carries

the full result object.

The endpoint, in plain terms

GET https://api.pennyforge.org/validate?address=0x…→ 200 { "valid": true, "status": "valid", "message": "Valid EIP-55 checksum.",        "normalized": "0x…" }

Free. No key. No account. JSON in, JSON out. The paid siblings — POST /check ($0.01, one chain: wallet-vs-contract, activity, balances, verdict) and POST /deep ($0.05, all five chains + wrong-network detection) — use x402 (USDC on Base). Machine-readable docs: llms.txt and openapi.json on the same host.

Who runs it

SendCheck is a one-person studio (Pennyforge). The worker runs on Cloudflare Workers; the address engine is MIT and public here. If the free endpoint ever moves, opts.baseUrl is the only thing you change.

License

MIT — see LICENSE.

來源:README.md,提交 53eb096

工具

3

validate_address

FREE pre-send check of an EVM address: EIP-55 checksum verification (catches typos, dropped characters, clipboard malware) + a note that a passing checksum is a spell-check, not payee verification. Run this before paying for check_address.

輸入參數address必填

check_address

Paid pre-send verification of one EVM address on one chain ($0.01 USDC via x402, EIP-3009 on Base): checksum, wallet-vs-contract, outgoing activity, native/USDC/USDT balances, leveled verdict with reasons and next action. TWO-PHASE: call without x_payment first to get the exact payment challenge (payTo, amount, asset), sign the USDC transfer, then call again with x_payment = the resulting X-Payment header to get the report. No account, no API key — the payment is the authentication.

輸入參數chainaddress必填x_payment

deep_scan

Paid deep scan: the full verification across ALL FIVE chains (Base, Ethereum, Arbitrum, Optimism, Polygon) incl. wrong-network detection and USDC/USDT token-mismatch flags ($0.05 USDC via x402, EIP-3009 on Base). TWO-PHASE like check_address: first call returns the payment challenge; second call with x_payment returns the report.

輸入參數address必填x_payment

版本歷史

1
  1. v1.0.0最新Sep 16, 2026

評分

已滿足適用檢查項的 100%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通必需25 分
  • 已宣告認證方式必需10 分
  • 已列出工具必需15 分
  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分