Release v1.0.0#74
Open
roger-gan wants to merge 95 commits into
Open
Conversation
feat(ts): add typescript_new monorepo
…for v1.0.0-beta.0 publish
- Rename all 17 publishable packages @x402/* -> @bankofai/x402-* (names, deps,
workspace: refs, source imports, README badges)
- Add "publishConfig": { "access": "public" } to publishable packages
- Mark legacy/* packages (x402, x402-fetch, ...) as private (not published)
- Unify all publishable package versions to 1.0.0-beta.0
- Regenerate pnpm-lock.yaml to match the real 25-project workspace
(previous lockfile was the typescript/ workspace's, copied in by mistake)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…esolutions The lockfile committed in a6c71ca was regenerated from scratch and drifted some transitive deps below what the source code requires — notably resolving @noble/curves to 1.9.1 in @bankofai/x402-extensions, whose secp256k1/p256 type lacks the `.Point` member used in offer-receipt/did.ts (TS2339, DTS build fails). Replace it with the upstream monorepo's lockfile (renamed @x402/* -> @bankofai/x402-*), which pins @noble/curves to 1.9.7 and matches the exact resolutions the source was built against. `pnpm install --lockfile-only` reports "Already up to date" — no drift. extensions/package.json is unchanged (^1.9.0). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ckfile The upstream lockfile used as the base in 71a0e9d predated site/package.json's dependency on @bankofai/x402-hedera (workspace:*), so `pnpm install --frozen-lockfile` failed with ERR_PNPM_OUTDATED_LOCKFILE. Add the workspace link entry to the site importer. Verified against all 25 workspace projects: every package.json dependency is now present in the lockfile importers, and @noble/curves stays pinned at 1.9.7. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
….gitignore The root .gitignore has the standard Python `lib/` build-output rule (unanchored), which also matched typescript_new/site/lib/. When typescript_new was imported (9c172bb) this excluded the only TS source under any lib/ dir — site/lib/animations.tsx — so `pnpm build` failed with 5 "Can't resolve '@/lib/animations'" errors in the site app. Add a .gitignore negation for typescript_new/site/lib/ and restore the file from upstream. Verified it exports textStagger, fadeInUp, AnimatedGrid, AnimatedCard (the symbols the site components import). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Trim typescript_new to an EVM + TRON SDK: - Delete mechanism packages: aptos, avm, hedera, stellar, svm (keep evm, tron) - Delete @bankofai/x402-paywall (browser payment UI; server-side, EVM/SVM/AVM only, no TRON) — it is an optional peer of the http middlewares, which fall back to static HTML when it is absent, so no middleware source changes needed - Delete site/ (private x402 demo Next.js app; not published) Consumer cleanup: - Remove the optional @bankofai/x402-paywall peer from express/hono/fastify/next - Drop build:paywall-deps script; narrow test:integration to core/evm/tron - Remove site from pnpm-workspace.yaml and the site#build turbo task - Update fetch JSDoc example (svm -> tron) and README package tables - Regenerate pnpm-lock.yaml: 18 importers, consistent with all package.json, @noble/curves stays 1.9.7 Publishable packages: 17 -> 11 (core, extensions, mcp, axios, express, fastify, fetch, hono, next, evm, tron). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…keys createClientTronSigner / createFacilitatorTronSigner take a wallet (AgentWallet / FacilitatorAgentWallet), not a raw private key. The README examples passed PK directly and dropped the required await on the async createClientTronSigner. Rewrite the Signers section to build wallets that keep the key out of the SDK, matching the verified test helpers.
…_new These were present upstream but not carried into typescript_new at import, so `pnpm format` / `format:check` (which run `prettier -c .prettierrc`) had no config to read. Restore both from upstream. Build/publish are unaffected; this only re-enables code formatting. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New overlay `@bankofai/x402-evm/facilitator/agent-wallet` exposes `createFacilitatorEvmSigner(publicClient, wallet, options?)` — the EVM counterpart of `createFacilitatorTronSigner`. It builds, wallet-signs, and broadcasts settlement txs internally via viem + a key-custody wallet, so facilitators no longer hand-wire readContract/writeContract/sendTransaction/ waitForTransactionReceipt/getCode/verifyTypedData (sdk-issues #1). - Broadcast normalizes the signed-tx hex with strip-then-prefix, tolerating agent-wallet dropping the `0x` (sdk-issues #2); survives an upstream fix. - `FacilitatorEvmPublicClient` is a `Pick<viem PublicClient, ...>` so a real client is assignable without a cast under strictFunctionTypes; the loose/ strict impedance is narrowed once inside the factory (sdk-issues #5). - Pin the tsup DTS heap (NODE_OPTIONS=--max-old-space-size=8192) to stop the worker OOM that silently dropped esm .d.mts (sdk-issues #6). - Additive-only: upstream signer.ts / index.ts untouched, exported via a new subpath so future upstream merges stay conflict-free. - Unit tests cover build/sign/broadcast, 0x tolerance, gas override, defaultGas, dataSuffix, and read pass-throughs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client-side counterpart of createFacilitatorEvmSigner, symmetric with TRON's createClientTronSigner. Adapts an @bankofai/agent-wallet wallet to a ClientEvmSigner: resolves the address eagerly, re-adds the 0x prefix that agent-wallet strips from signatures (signature analog of sdk-issues #2), and wires readContract for permit2 enrichment — so client code never touches a raw key. - Additive overlay at @bankofai/x402-evm/client/agent-wallet; upstream signer.ts / index.ts untouched, exported via a new subpath. - Unit tests: eager address resolution, 0x tolerance, readContract wiring, optional public client. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
examples_new/typescript: a runnable client -> server -> facilitator loop for
@bankofai/x402-*, scoped to EVM + TRON.
- Each example self-contained; chain setup isolated in chains/{evm,tron}.ts and
self-skips a chain whose wallet/address is absent (EVM-only / TRON-only / both).
- Wallet custody via @bankofai/agent-wallet (resolveWallet); examples never read
a raw key. Uses create{Client,Facilitator}EvmSigner and the TRON equivalents.
- facilitator/basic (:4022, agent-wallet), servers/express (:4021, keyless),
clients/fetch.
- Workspace links the in-repo SDK packages via pnpm-workspace.yaml.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
TRON's mainstream tokens (USDT/USDD) lack ERC-3009, so they settle via permit2, which needs a one-time on-chain approve(Permit2, MaxUint256) from the token owner. The Python client does this automatically; the TS client did not, leaving the approve to the app. Mirror the Python behavior: - AgentWallet gains an optional signTransaction; ClientTronSigner gains an optional ensureAllowance (defaults exposed via createClientTronSigner's allowanceMode option). - createPermit2Payload calls ensureAllowance with amount+fee before signing. - ensurePermit2Allowance reads allowance(owner, Permit2) and, when short, broadcasts approve(Permit2, MaxUint256), waiting for receipt SUCCESS. Deliberate divergences from Python: spender is the canonical Permit2 (not PaymentPermit), allowance-read errors surface rather than being swallowed, and a clear error is thrown early when an approve is needed but the wallet cannot sign transactions. The facilitator build/sign/broadcast and receipt poll are factored into shared helpers and reused unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The fetch client's TRON adapter now exposes signTransaction, letting the signer auto-broadcast the one-time Permit2 approve that USDT/USDD need on first use — parity with the Python client. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
toClientAgentWallet now exposes signTransaction, so the Nile e2e client signer can auto-broadcast the one-time Permit2 approve. Verified on-chain: resetting the payer's allowance to 0 and running the first payment auto- approves (allowance → MaxUint256) before signing, then settles. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ensureAllowance waited on getTransactionInfo, which only populates after the block solidifies (~19 confirmations, ~57s on Nile) — making the first permit2 payment block ~60s. The ERC-20 allowance is live as soon as the approve is packed (~3s), so wait on getTransaction's contractRet instead. Adds pollTransactionPacked (getTransaction → ret[0].contractRet, 1.5s interval, 60s cap) and uses it for the approve; revert is still caught via contractRet !== "SUCCESS". The facilitator settlement path keeps the solidified getTransactionInfo wait (parity with Python and the legacy TS client). Verified on Nile: first-payment createPaymentPayload dropped from ~60.7s to ~1.1s, allowance 0 → MaxUint256, verify + settle succeed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Plain ERC-20 tokens (no ERC-3009 / no EIP-2612, e.g. BSC USDC) settle via Permit2 and need a one-time approve(Permit2). The erc20ApprovalGasSponsoring extension has the client sign that approve offline and the facilitator broadcast it bundled with settle — but the agent-wallet bridge factories didn't expose the needed capabilities. - createClientEvmSigner: forward signTransaction (re-adding the 0x agent-wallet strips), so the client can sign the approve tx. getTransactionCount / estimateFeesPerGas already flow from the viem publicClient. - createFacilitatorEvmSigner: add sendTransactions + GasSponsoringFacilitatorEvmSigner so the signer satisfies the extension's Erc20ApprovalGasSponsoringSigner (pre-signed tx -> sendRawTransaction; call intent -> wallet-signed). Both are additive to the BankofAI overlay files; upstream signer.ts/index.ts and the exact mechanism flow are untouched. Tests added for both paths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Parity with the demo's SufficientBalancePolicy: a beforePaymentCreation hook checks the payer can cover amount + fee before signing, aborting with a clear error instead of a confusing on-chain settle failure. The new SDK's payment selection is synchronous, so the async on-chain balance read runs as this hook; it guards the chosen requirement (TRON only — EVM has no client checkBalance). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Aligns examples_new with the BankofAI network/token support docs.
- TRON: advertise USDT and USDD on tron:nile (both permit2; client auto-approve).
- EVM: switch to BSC testnet (eip155:97), advertising DHLU (ERC-3009 -> exact
eip3009, gasless) and USDC (plain BEP-20 -> exact permit2 + gas-sponsored
approve via the erc20ApprovalGasSponsoring extension). Token capabilities and
the DHLU EIP-712 domain ("DA HULU","1") verified on-chain.
- Refactor the EVM client/server/facilitator modules to per-network tables
(EVM_NETWORKS / EVM_TOKENS): each network gets its own publicClient + signer
registered under its exact CAIP-2 id (removes the misleading eip155:* wildcard
that backed every chain with a single publicClient). Adding a chain is one
table entry; no SDK changes (explicit-asset prices, not DEFAULT_STABLECOINS).
- Pre-list BSC mainnet (USDC/USDT/EPS, all permit2 — verified) and TRON mainnet
as commented-ready entries; document the support matrix + "adding a chain/token"
in the READMEs. Wire @bankofai/x402-extensions; fix stale EVM_PRIVATE_KEY ->
AGENT_WALLET_PRIVATE_KEY in env templates.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the three committed per-app .env-local (empty tracked files — a footgun for accidentally committing a filled-in key) with one shared root .env-local: all three apps now load ../../.env-local, which is gitignored. - Add .gitignore (.env-local, .env, node_modules, dist). - .env-local.example trimmed to essentials; one AGENT_WALLET_PRIVATE_KEY serves client + facilitator on both chains. Drop TRON_PRIVATE_KEY entirely (agent-wallet's env provider is single-key; the network only selects the signer type). - Ports fixed in code (server 4021, facilitator 4022) — no PORT in env. - READMEs updated to `cp .env-local.example .env-local`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First real `tsc` run (after `pnpm install` populated node_modules) surfaced
both refactor fallout and pre-existing latent bugs:
- Network type: Object.keys/entries widen network ids to `string`; cast to the
CAIP-2 `${string}:${string}` so register()/accepts accept them.
- tryResolveWallet now returns a wallet typed with signTypedData (resolveWallet
is typed as the base Wallet; signTypedData lives on Eip712Capable).
- facilitator settle hook: ctx.paymentRequirements -> ctx.requirements.
- TRON_NETWORK annotated as `${string}:${string}` (was widening to string).
- express async middleware cast to RequestHandler.
All three apps (server/client/facilitator) now pass `pnpm typecheck`. Also
commit the examples pnpm-lock.yaml for reproducible installs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The beforePaymentCreation hook (added in 316dfae) only guarded the *chosen* requirement and aborted if unaffordable — it couldn't re-select an affordable token the way the demo's SufficientBalancePolicy does (new SDK selection is synchronous, so async balance filtering can't run before selection). Net value was just a slightly earlier/clearer error, and it could be counterproductive in multi-token scenarios (abort on the first option instead of paying with the affordable one). Remove it; an unaffordable payment is rejected at the facilitator anyway, and token choice is better handled by a selector. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The facilitator's waitForTransactionReceipt polled getTransactionInfo, which only populates after the block solidifies (~19 confirmations, ~57s on Nile) — so every settle blocked ~57s. Switch it to pollTransactionPacked (the same getTransaction.contractRet path the client approve already uses): the settle result is known as soon as the tx is packed (~3s), and TRON does not reorg packed blocks in practice. Removes the now-unused pollTransactionReceipt and TronTxInfo. Client + facilitator now both confirm at packed speed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pollTransactionPacked read getTransaction.ret[0].contractRet in the sub-second window after broadcast, where TRON returns an unreliable preconfirm result that can read REVERT for a tx that ultimately succeeds. That false REVERT made facilitator settle return success:false, so the resource server re-challenged and the client got a 402 after the payment had already landed on-chain. Delay before the first read to skip the preconfirm window, and require two consecutive identical contractRet reads before trusting the result. Shared by client ensureAllowance and facilitator waitForTransactionReceipt, so the approve-confirmation path is fixed too. Verified live on tron:nile (USDT + USDD). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
createClientEvmSigner detached wallet.signTransaction into a local const, so calling it lost its `this`; agent-wallet's LocalSigner.signTransaction reads this._impl and threw "Cannot read properties of undefined (reading '_impl')". This broke the ERC-20 approval gas-sponsoring path (BSC USDC permit2). signTypedData was unaffected because it is invoked as wallet.signTypedData(...). Bind to the wallet. Verified live on BSC testnet (eip155:97): USDC permit2 + gas-sponsored approve now signs and settles. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tryResolveWallet passed the short family name ("evm"/"tron") to @bankofai/agent-wallet's resolveWallet, which requires a CAIP-2 id (must start with eip155: or tron:). "evm" was rejected, so the facilitator and client silently skipped EVM and registered TRON only; "tron" worked only by accident. Map the family to a representative CAIP-2 id — address derivation is chain-id-independent within a family.
Verified live: facilitator now registers eip155:97 and BSC testnet payments (DHLU eip3009, USDC permit2) succeed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…lient resolveDeadline now honors a caller-requested deadline via extensions.paymentPermitContext.meta.validBefore (matching the Python client's source), falls back to the max window otherwise, rejects values below the network min, and clamps over-window values down with an [x402]-prefixed console.warn (per docs/solutions.md #2 "warn when clamping", styled after core). Removes the previously dead minDelta path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ensurePermit2Allowance now coerces the Permit2 allowance read with BigInt() before comparing against the required amount — tronweb contract reads can surface a string/number, which misbehaves or throws against a bigint; this matches readGasFreeBalance's coercion. buildSignAndBroadcast now fails fast when a successful broadcast returns no txid, instead of returning "" and letting the caller poll an empty hash until timeout. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… error
A wallet returning a string that starts with "{" but is not valid JSON would
throw a raw SyntaxError out of toSignedTransaction. Wrap the parse and throw an
Error that names the contract violation ("signTransaction returned a malformed
JSON string: ...") so the failure is diagnosable rather than opaque.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…S clients Add a dual-chain (BSC testnet + TRON Nile) MCP server/client example and wire it into the workspace (pnpm-workspace + dev:mcp-* scripts). Make the dual-chain clients (fetch, upto, batch-settlement, mcp) drive which chains/tokens to pay — and in what order — from a new PAY_TARGETS env var. Each target is "<network>[@<token>]" (network prefix match; token by symbol or asset address); each is paid exactly once via a mutable payment-requirements selector, so EVM and TRON (and per-token, e.g. USDT vs USDD) each settle once. The mcp client switches from createx402MCPClient to a manual x402Client + wrapMCPClientWithPayment so the selector can be injected. Unset PAY_TARGETS keeps the prior behavior (each configured chain once). Document PAY_TARGETS in the .env*.example files, noting "@" (not "#") because dotenv treats "#" as a comment. Verified end-to-end on testnet: fetch, mcp, and upto pay EVM + TRON each once; batch-settlement's dual-chain payment path settles on both chains. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the manual test/integrations nile suite (gasfree, permit2, exact, upto, batch-settlement, policy-selection) and its helpers/README/.env; these on-chain integration paths are now exercised via the runnable examples_new clients instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… ./exact aggregate The root barrel re-exported building blocks that nothing outside the package consumes (internal code reaches them via relative imports): drop the low-level signer adapters (toClientTronSigner / toFacilitatorTronSigner — the public surface is the create* factories), buildAssetExtra, and the fee-computation verbs (resolveBaseFee / isTokenAllowed / buildFeeInfo / validateFee / readFeeFromExtra). The symbols stay exported from their own modules for internal + test use. Also expose the `./exact` aggregate (ExactTronScheme), which had an orphaned src/exact/index.ts that was neither in the tsup entry list nor the package.json exports map — now symmetric with upto/gasfree/ batch-settlement. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…re barrel attachFacilitatorLogging is an implementation detail of createFacilitator; drop it from the @bankofai/x402-core public barrel (still module-exported for createFacilitator + unit tests, marked @internal). Also remove the unused barrel exports getLogger (dead — zero references repo-wide; function deleted) and resetLogger (test-only; reached via direct module import). Public logging surface kept: log, setLogger, noopLogger, consoleLogger, Logger, createFacilitator. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A locally-run resource server embeds `paymentPayload.resource.url = http://localhost:<port>`. Some edge WAFs in front of a hosted facilitator (observed: AWS managed rule `EC2MetaDataSSRF_BODY`) flag that loopback URL in the verify/settle request body as an SSRF probe and reject the POST with 403, surfacing to the client as a failed payment. The facilitator never reads `resource` on the verify/settle path and the signature does not cover it, so dropping it is a functional no-op. Add `ResourceStrippingFacilitatorClient`, an opt-in FacilitatorClient decorator that removes `paymentPayload.resource` before verify/settle. Wired into all four server lines (express/exact, gasfree, upto, batch-settlement) behind `STRIP_RESOURCE_URL` (default off; wire payload untouched when off). Stripping happens at the verify/settle boundary, so it is scheme-agnostic. No SDK/core changes — example overlay only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ctEvmScheme `registerExactEvmScheme` (client + facilitator) registered the v2 ExactEvmScheme and then also auto-registered ExactEvmSchemeV1 for every legacy v1 network, which is what made a facilitator advertise the full Coinbase-style v1 network list (base, polygon, sei, …) in /supported. The current SDK no longer wants to bring up x402 v1. Drop the `registerV1(...)` block from both helpers. The v1 classes (`ExactEvmSchemeV1`, `NETWORKS`) and their export subpaths (`@bankofai/x402-evm/v1`, `@bankofai/x402-evm/exact/v1/*`) are kept, so this is not a breaking API change — callers that still need v1 can register it explicitly. Reversible by re-adding the block. All 709 evm unit tests pass (v1 suites included). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an optional FACILITATOR_API_KEY env var to all five examples_new TypeScript resource servers (express, gasfree, upto, batch-settlement, mcp). When set, it's sent as the X-API-KEY header on every facilitator call (verify/settle/supported) via createAuthHeaders; unset keeps the anonymous behavior unchanged. Hosted facilitators use it to select a rate-limit tier. Documented in the .env-*.example files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
viem's default BSC testnet node (data-seed-prebsc-*.bnbchain.org:8545) is frequently unreachable. The permit2 path (e.g. USDC) reads the chain to sign the gas-sponsored approve and fails when the default RPC is down, while ERC-3009 tokens (DHLU) sign offline and are unaffected. Wire an optional EVM_RPC_URL env var through createClientEvmSigner so the client can point at a reliable endpoint. Unset falls back to the viem default (unchanged behavior). Documented in .env-exact.example. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The resource-server middleware treats a structured facilitator failure
(verify isValid=false / settle success=false) as a normal 402 and
returns it without logging — only unexpected exceptions are logged. So a
rejected payment looks silent on the server while the client just sees
402 {}, hiding the real invalidReason/errorReason.
Add attachPaymentLogging() that registers observers on the resource
server's existing hook surface (onAfterVerify/onVerifyFailure/
onBeforeSettle/onAfterSettle/onSettleFailure), mirroring the SDK's own
attachFacilitatorLogging instead of wrapping the facilitator client.
Wired unconditionally in the express example (no env gate).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…resource server Mirror the facilitator's logging overlay on the resource-server side and share one log format between them. - Add paymentLogFormat.ts: single source of truth for the verify/settle message strings, levels, and field shapes. - Add resourceServerLogging.ts: attachResourceServerLogging() (hooks on the existing x402ResourceServer surface) + createResourceServer() convenience, the symmetric counterpart of createFacilitator(). - Refactor facilitatorLogging.ts onto the shared format (behavior and emitted messages unchanged; existing tests stay green). - Export createResourceServer from the core barrel. The two roles differ in which hook carries an invalid verify (facilitator onVerifyFailure vs resource-server onAfterVerify with isValid=false), but emit an identical "x402: verify failed" line. Adds unit tests covering both roles' paths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the manual new x402ResourceServer + bespoke logging-hooks wiring with the SDK's createResourceServer, which pre-attaches the unified verify/settle logging. Drops the example-local loggingHooks.ts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GasFree's feeTo is the relayer's service provider: verify rejects any address not in the relayer's registered provider list with gasfree_fee_to_mismatch. The facilitator's getExtra defaulted feeTo to its own wallet (signer.getAddresses()[0]), which is not a provider — so the client signed serviceProvider = facilitator wallet and every gasfree payment failed verification (observed against the hosted facilitator, whose advertised feeTo was not in /config/provider/all). getExtra now advertises feeTo only when explicitly configured to a real provider; otherwise it omits feeTo and the client picks one from the relayer's provider list. This mirrors the Python facilitator's fee_quote, which selects a provider rather than the facilitator address. The exact scheme is unchanged — there the facilitator IS the fee recipient. Adds gasfree-feeto.test.ts covering the omit-by-default behavior and the explicit-provider passthrough. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…examples Bring the remaining four example servers in line with express: swap the plain new x402ResourceServer for createResourceServer, which pre-attaches the unified verify/settle logging. Without it, a successful or rejected payment was silent on these servers (the middleware only logs unexpected exceptions), so a 402 gave no clue why. mcp previously imported x402ResourceServer from @bankofai/x402-mcp (a re-export of the core class), so createResourceServer from core is type-compatible with createPaymentWrapper. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the fetch/exact client: the upto and batch-settlement EVM clients read the chain (allowance, channel state via channels(bytes32)) over viem's default BSC testnet node, which is frequently unreachable and fails the run with "fetch failed". Pass an optional EVM_RPC_URL through createClientEvmSigner so the EVM path can target a reliable endpoint; unset falls back to the viem default (unchanged). Documented in the .env-upto/.env-batch examples. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The batch-settlement refund probe picked the first accept matching the scheme, ignoring network. On a route that advertises batch-settlement on multiple networks (e.g. EVM + TRON), this handed an eip155 requirement to the TRON refund, which threw "Unsupported network format: eip155:97" in computeChannelId → getTronChainId. Filter the probe to the package's own chain family (tron:* / eip155:*). The EVM file is upstream-derived; the same latent bug exists upstream (refund takes no network and picks accepts[0]), but only surfaces once a single route mixes VM families — which upstream never does. The eip155:* filter is correct for the EVM package and worth contributing upstream. Adds multi-network refund probe tests for both packages (a leading foreign-family accept must be skipped). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reorganize the four .env-*.example files into fixed SHARED / CLIENT / SERVER / FACILITATOR sections, matching which process actually reads each var (verified against process.env usage in clients/, servers/, facilitator/). Notably the server never signs — it only holds payout addresses and calls the facilitator. Content unchanged; only regrouped with clarifying comments. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Directory reshape:
- Move previous-generation projects (python, typescript, examples, e2e,
integration, scripts, specs) + old root manifests + assets into legacy/,
which is reference-only and slated for removal.
- Promote typescript_new/ -> typescript/ and examples_new/ -> examples/,
rewriting workspace/lockfile links and doc references off the _new suffix.
CI:
- Drop the 5 legacy-only workflows (check_python, check_typescript, check_e2e,
nightly_testnet, publish_pypi); only audit-pr.yml remains. .github and
.claude carry no legacy/ references.
.claude/rules realigned to the new TypeScript-only SDK:
- Remove python and exact_permit rules (no python; permit2 is now an `exact`
assetTransferMethod, not a separate scheme).
- Rewrite index, common, typescript, networks, exact, exact_gasfree for the
new package layout (core/mechanisms/{evm,tron}/extensions/http/mcp), upstream
fork+overlay model, signer factories, and vitest unit/integration testing.
- Add upto, batch-settlement, auth-capture scheme sheets.
- Stop citing specs/ (old content); rules now reference the package source.
Functional refs preserved (legacy/scripts/upload_pkgs.sh; e2e harness is
self-relative). No new CI added for the new SDK yet.
The /x402:create-scenario wizard scaffolded declarative python e2e scenarios (e2e/scenarios/ + integration/run.py), a harness that now lives in legacy/ and has no equivalent in the vitest-based new SDK. Remove it and repoint the scheme-author agent's follow-ups (vitest tests + register) and the root CLAUDE.md wizard list. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Its purpose was drafting a specs/<scheme>.md spec document — the spec-first model that no longer applies in the new SDK (specs are legacy; a new scheme is classes + rule sheet + vitest tests). Drop the agent and its mentions in root CLAUDE.md and the rules index. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rewrite version/installation/quick-start/schemes/development for the 1.0.0-beta.0 granular @bankofai/x402-* packages: drop the Python SDK and the umbrella package, replace old class names with the real new API (x402Client + wrapFetchWithPayment, createClient<Chain>Signer, Exact<Chain>Scheme, createResourceServer, x402Facilitator), list the current schemes (exact eip3009/permit2, upto, batch-settlement, auth-capture, exact_gasfree), and point at examples/typescript instead of the removed bsc-testnet-smoke / specs paths. Architecture, flow, networks, security, and license sections preserved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the Python development section and the python/x402 + typescript/packages/x402 layout; document the pnpm/turbo monorepo (build/test/lint, vitest unit vs integration), the upstream fork+overlay rule for core/mechanisms/evm, the ESM/no-any/BigInt coding standards, and the legacy/ note. Generic sections (reporting, PRs, security, license) preserved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
TRON is the primary chain — show the full TRON (Nile) client snippet first (createClientTronSigner with apiKey, register under tron:*), and demote EVM (BSC) to the symmetric "swap the imports" variant. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove the Demo Repository header link and the "Try it out" demo line. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add USDT (0x337610d2..., 18 dec, plain BEP-20) alongside DHLU and USDC in the express server EVM_TOKENS table. Uses the same permit2 + gas- sponsored approve path as USDC. SDK code unchanged. Update .env-exact.example PAY_TARGETS comment to list USDT.
TOKEN_ADDRESSES was a flat global map, so the USDT symbol collided between BSC testnet and TRON Nile. Index by chain family (eip155/tron) so eip155:97@USDT resolves to the BSC testnet USDT contract while tron:nile@USDT still resolves to the TRON Nile USDT contract.
The previous address 0x337610d2...428 has no bytecode on eip155:97 (verified via eth_getCode), causing facilitator verify to fail with asset_not_deployed_contract. The correct BSC testnet USDT is 0x7ef95a0FEE0Dd31b22626fA2e10Ee6A223F8a684 (symbol USDT, name Tether USD, 18 decimals, plain BEP-20 → permit2 path).
The Tether USD deployment 0x7ef95a0F...8a684 had no test balance available. Switch to 0x337610d2...4ddd (symbol USDT, name 'USDT Token', 18 dec) which is the BSC testnet USDT that test funds were transferred to (2 USDT confirmed via tx 0xfe49955c...). Both are valid USDT deployments on eip155:97; this one has faucet/test-transfer liquidity.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Tests
Checklist