Skip to content

Release v1.0.0#74

Open
roger-gan wants to merge 95 commits into
mainfrom
release-v1.0.0
Open

Release v1.0.0#74
roger-gan wants to merge 95 commits into
mainfrom
release-v1.0.0

Conversation

@roger-gan

Copy link
Copy Markdown
Contributor

Description

Tests

Checklist

  • I have formatted and linted my code
  • All new and existing tests pass
  • My commits are signed (required for merge) -- you may need to rebase if you initially pushed unsigned commits

app and others added 30 commits June 15, 2026 17:25
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>
roger-gan and others added 30 commits June 24, 2026 16:51
… 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant