@piprail/sdk 1.15.1 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,121 @@ All notable changes to `@piprail/sdk` are documented here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
5
5
  versions follow [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [1.18.0] — 2026-06-11 — gasless `exact` on 7 more chains + a Permit2-proxy guard
8
+
9
+ A minor, fully additive release — defaults byte-identical (`exact` stays opt-in), no new dependency,
10
+ the lazy-chunk invariant holds.
11
+
12
+ ### Added — gasless EIP-3009 `exact` on 7 more EVM chains
13
+ - **Sonic, Linea, Celo, Unichain, World Chain, Sei, HyperEVM** added to `EXACT_NETWORK_SLUGS`. Each
14
+ ships a **native Circle USDC** whose `transferWithAuthorization` (EIP-3009) was **verified on-chain**
15
+ (`authorizationState` present), so the buyer pays **gasless, with no approval and no proxy**. This
16
+ roughly **doubles** PipRail's gasless-EVM footprint (7 → 14 mainnet chains). Live-proven on **HyperEVM
17
+ mainnet** (a standard `exact` client signed EIP-3009, the gate self-settled — tx `0xe31f92ee…`).
18
+ - (The rail was never gated per-chain in the driver — it's capability-detected at runtime via
19
+ `exactDomain`. `EXACT_NETWORK_SLUGS` is the public `chainIdForExactNetwork` helper + the advertised
20
+ list; it had simply drifted behind the real capability. It's now accurate.)
21
+
22
+ ### Hardened — never advertise an unsettleable Permit2 rail
23
+ - The `exact` rail's **Permit2** fallback (for non-EIP-3009 tokens) now checks the **x402ExactPermit2Proxy
24
+ is deployed** on the chain before advertising it. On a chain with neither an EIP-3009 token nor the
25
+ proxy (e.g. Mantle/Scroll/Kaia for their tokens), the gate offers `onchain-proof` only instead of a
26
+ Permit2 rail it could never settle; a forced `method: 'permit2'` there is a clear config error.
27
+ - New driver-contract method `exactPermit2Supported?()` (EVM driver implements it from the verified
28
+ proxy-chain set). New public exports: **`PERMIT2_PROXY_CHAIN_IDS`**, **`isPermit2ProxyChain`**.
29
+
30
+ ### Docs
31
+ - The chain-specific "Permit2 & BNB Chain" page was consolidated into a comprehensive **Gasless
32
+ payments** page (what gasless means · `onchain-proof` vs `exact` · EIP-3009 vs Permit2 · a clear
33
+ per-chain/-token coverage table). Old URL redirects.
34
+
35
+ ### Tests
36
+ - +5 (the 7 new slugs, `isPermit2ProxyChain`, and the proxy-guard: auto-drop, mixed-gate, forced-throw).
37
+
38
+ ## [1.17.0] — 2026-06-11 — `onPaid` hardening: enriched, isolated, durable receipts
39
+
40
+ A minor, fully additive release — defaults byte-identical (fire-and-forget stays the default,
41
+ the wire `X402Receipt` is unchanged), the protocol layer stays viem-free, and the EVM bundle
42
+ pulls in **no new dependency** (`deliverReceipt` is global `fetch` + Web Crypto).
43
+
44
+ ### Fixed — an `async onPaid` could crash the process
45
+ - The gate's `onPaid` isolation only caught **synchronous** throws; an `async` handler that
46
+ rejected (the common case — a DB/queue/webhook write) escaped as an `unhandledRejection` that
47
+ could crash the process. `fireOnPaid` now isolates a **rejected promise as well as a sync
48
+ throw**, routing both to the new `onPaidError` seam. The "a hook can never break the request"
49
+ guarantee is now true for async handlers too.
50
+
51
+ ### Added — the enriched `PaidReceipt` (what `onPaid` now receives)
52
+ - `onPaid` (and the new `onPaidError`) receive a `PaidReceipt`: every `X402Receipt` field **plus**
53
+ `decimals`, `symbol`, `amountFormatted` (formatted from the *settled* amount), and a stable
54
+ `idempotencyKey` (= the settled tx id) — so a receipt handler never needs a second lookup. The
55
+ wire receipt (`result.receipt`, the response header) stays the lean settlement record.
56
+
57
+ ### Added — receipt-hook options on `requirePayment` / `createPaymentGate`
58
+ - `onPaid?: (receipt: PaidReceipt) => void | Promise<void>` — now explicitly **sync or async**.
59
+ - `onPaidError?: (err, receipt) => void` — observe a failing hook instead of swallowing it
60
+ silently (its own throws are isolated too).
61
+ - `awaitOnPaid?: boolean` (default `false`) — await the hook before serving the resource, so
62
+ "receipt recorded" is guaranteed on the happy path. A rejection is still isolated; it never
63
+ turns a settled payment into a 402.
64
+
65
+ ### Added — `deliverReceipt()`, a reliable self-hosted webhook primitive
66
+ - `deliverReceipt(receipt, { url, secret, retries, timeoutMs, backoff, headers, onAttempt })`
67
+ POSTs a `PaidReceipt` to **your own** endpoint with retries + exponential backoff, an
68
+ **HMAC-SHA256** signature (`piprail-signature: sha256=…`), and an `idempotency-key` header. It
69
+ **never throws** (failure → `{ delivered: false, … }`), retries `408`/`429`/`5xx`/transport
70
+ errors, and stops on a permanent `4xx`. Isomorphic (global `fetch` + Web Crypto), zero new deps.
71
+ PipRail hosts nothing — the URL is yours. New exports: `deliverReceipt`, `DeliverReceiptOptions`,
72
+ `DeliverAttempt`, `DeliverResult`, and the `PaidReceipt` type.
73
+
74
+ ### Delivery contract (documented)
75
+ - `onPaid` is **at-least-once**: exactly once per proof on a single in-memory replay store, but
76
+ across instances sharing a custom `isUsed`/`markUsed` store a race can deliver twice — **dedupe
77
+ on `idempotencyKey`**. Covered on the Receipts & onPaid docs page with queue + webhook patterns.
78
+
79
+ ### Tests
80
+ - +22: `test/server-onpaid.test.ts` (enrichment, sync+async isolation, no-`unhandledRejection`,
81
+ `awaitOnPaid` ordering, fire-once-per-settlement) and `test/receipts.test.ts` (retries, backoff,
82
+ permanent-vs-retryable status, HMAC signature verification, idempotency + header precedence,
83
+ timeout/abort, never-throws).
84
+
85
+ ## [1.16.0] — 2026-06-11 — x402 `exact` Permit2 method: BNB Chain is a first-class exact rail
86
+
87
+ A minor, fully additive feature — defaults byte-identical (`exact` stays opt-in), the protocol
88
+ layer stays viem-free, and the EVM bundle pulls in **no new dependency** (Permit2 is EIP-712
89
+ signing + one `approve` on the existing `viem` peer; the lazy-chunk invariant still holds).
90
+
91
+ ### Added — the `permit2` asset-transfer method of the x402 `exact` scheme (EVM)
92
+ - The `exact` scheme now settles tokens **without** EIP-3009 — most importantly **Binance-Peg
93
+ USDC/USDT on BNB Chain** (no native Circle USDC exists on BNB). Per the x402 spec
94
+ (`specs/schemes/exact/scheme_exact_evm.md`): the buyer signs a Permit2 `PermitWitnessTransferFrom`
95
+ whose `spender` is the canonical **x402ExactPermit2Proxy** (`0x402085…20001`) and whose
96
+ `witness.to` binds the recipient; the merchant/relayer self-settles via the proxy's `settle`.
97
+ - **Buyer** — `PipRailClient({ schemes: ['exact'] })` auto-detects `extra.assetTransferMethod` and
98
+ signs EIP-3009 or Permit2 accordingly. The one-time `approve(Permit2)` is done lazily on first use
99
+ (the only on-chain action the buyer takes; gas-free thereafter); `estimateCost` notes it.
100
+ - **Seller** — `requirePayment({ exact: { settle: 'self', relayer } })` **auto-selects** the method:
101
+ EIP-3009 when the token supports it, else Permit2 (any ERC-20). New `exact.method?: 'eip3009' |
102
+ 'permit2' | 'auto'` (default `'auto'`) pins it. The advertised rail carries
103
+ `extra.assetTransferMethod`; Permit2 replay uses the Permit2 nonce bitmap.
104
+ - New public exports: `PERMIT2_ADDRESS`, `X402_EXACT_PERMIT2_PROXY`, `PERMIT2_WITNESS_TYPES`, and the
105
+ wire types `Permit2Authorization` / `Permit2PaymentPayload` / `ExactPaymentPayloadAny`.
106
+ `ParsedExactPayment` is now a discriminated union on `method` (`'eip3009' | 'permit2'`). BNB slugs
107
+ added to `EXACT_NETWORK_SLUGS`.
108
+ - **FDUSD + USD1 are now default BNB tokens.** Both **are EIP-3009** (unlike Binance-Peg USDC/USDT),
109
+ so the `exact` rail uses the **gasless `transferWithAuthorization` path — no Permit2 approve**.
110
+ They hardcode their EIP-712 domain version (`"1"`) without a `version()` function, so
111
+ `readExactDomain` now **derives the version from the on-chain `DOMAIN_SEPARATOR`** (generalizes to
112
+ any `version()`-less EIP-3009 token). 18-decimal; verified on-chain.
113
+
114
+ ### Verified
115
+ - **Live-proven on BNB mainnet** (real USDC, both rails — 402 → pay → 200, balance moved, replay
116
+ rejected): onchain-proof tx `0x4bf044b554e5d1390b5c0fb225bad7501c4fa1e3538005aed144ad153d30eb14`;
117
+ exact/Permit2 self-settle tx `0x6e3ecc3f3230d6e1627db5c233a102dd1878e46bab676302a84f78f30be61589`.
118
+ - **FDUSD + USD1 live-proven on BNB mainnet** via the gasless EIP-3009 `exact` rail (domain version
119
+ derived on-chain, replay rejected): FDUSD tx `0xfaec2e82a294790322a24db65458abbe4913a493e81dd66accfcf7a8be5dbfda`;
120
+ USD1 tx `0x10e68722375943a183edd749b67acf05a75baa98680a31b06af804d56a160c28`.
121
+
7
122
  ## [1.15.1] — 2026-06-10 — docs consolidation: the README is now a signpost to docs.piprail.com
8
123
 
9
124
  Docs-only. No code, no API, no behaviour change — `dist` is byte-identical.