@piprail/sdk 1.22.1 → 1.24.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 +83 -0
- package/README.md +28 -1
- package/dist/index.cjs +472 -27
- package/dist/index.d.cts +437 -6
- package/dist/index.d.ts +437 -6
- package/dist/index.js +460 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,88 @@ 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.24.0] — 2026-06-14 — multi-chain buying (one buyer, a wallet per chain)
|
|
8
|
+
|
|
9
|
+
### Added — pay a 402 on whichever chain it asks for
|
|
10
|
+
|
|
11
|
+
- **`MultiChainPayer`** — a `PipRailClient` is bound to ONE chain + ONE wallet (an EVM key can't sign a
|
|
12
|
+
Solana tx). `MultiChainPayer.fromWallets({ wallets: { base: { privateKey }, solana: { secretKey }, … }, policy })`
|
|
13
|
+
carries one wallet per chain and exposes a single `fetch`/`get`/`post`/`planPayment`/`canAfford`/`quote`/
|
|
14
|
+
`discover`/`register`/`spent`/`budget`. On a 402 it surveys every funded chain and pays the FIRST chain
|
|
15
|
+
you listed that can actually settle — through each client's own spend policy, `onBeforePay`, retries, and
|
|
16
|
+
replay-protection. No price oracle, no backend, no custody; across coins the order you list the chains is
|
|
17
|
+
the preference (within a chain, the cheapest-gas rail). Also `new MultiChainPayer([...clients])` for full
|
|
18
|
+
control (e.g. custom-EVM viem `Chain`s). `schemes` (incl. the gasless `exact` rail) propagates to every
|
|
19
|
+
chain's client.
|
|
20
|
+
- **`fetchAcross(clients, url, init?)`** — the EXECUTION counterpart to `planAcross`: plan across an array
|
|
21
|
+
of single-chain clients and pay, on its owning client, the rail `planAcross` reports as `best` (the first
|
|
22
|
+
funded chain that can settle). Throws `PaymentDeclinedError` with a merged, per-chain funding hint when
|
|
23
|
+
no chain can settle.
|
|
24
|
+
- **`PayingClient`** — the shared read-+-pay interface `paymentTools` now accepts; both `PipRailClient` and
|
|
25
|
+
`MultiChainPayer` satisfy it, so the agent toolkit (and the MCP) wrap either unchanged.
|
|
26
|
+
- **`piprail_register` agent tool** gains optional `network` + `asset` params, so a multi-chain agent can
|
|
27
|
+
advertise a listing on a specific chain instead of defaulting to the first wallet's chain.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- **Cross-chain `best` selection is now your PREFERENCE order, not raw gas magnitude.** `planAcross` /
|
|
32
|
+
`fetchAcross` no longer compare gas fees across different native coins (base units aren't comparable —
|
|
33
|
+
e.g. EVM wei vs Solana lamports — and there's no oracle), which previously let a small-base-unit coin
|
|
34
|
+
win regardless of real cost. They now pay the FIRST chain you list that can settle (within a chain, the
|
|
35
|
+
cheapest-gas rail still wins) — matching the documented contract. Single-chain `PipRailClient` ranking is
|
|
36
|
+
unchanged.
|
|
37
|
+
- **`planAcross` now propagates a TOTAL outage.** If EVERY client fails to reach the resource it throws
|
|
38
|
+
(like a single client) instead of returning `null` — so `canAfford`/`quote` can't report a false
|
|
39
|
+
"affordable"/"not-gated". A single chain being down still just drops that chain.
|
|
40
|
+
- **Clearer multi-chain decline message.** When no funded chain can settle, `planAcross`'s `fundingHint`
|
|
41
|
+
(and the `PaymentDeclinedError` `fetchAcross` throws) now names EVERY funded chain's own blocker — "top up
|
|
42
|
+
X USDC on base · add ~Y POL gas on polygon" — instead of only the first. Chains the 402 never offered are
|
|
43
|
+
dropped as noise when another chain is close; if none of your chains are offered, it says where the 402
|
|
44
|
+
IS payable. Per-rail `blockers`/`warnings` stay machine-readable for agents that branch programmatically.
|
|
45
|
+
|
|
46
|
+
Single-chain `PipRailClient` behaviour is byte-identical. Examples: `examples/multi-chain` (routing + a
|
|
47
|
+
live gasless-`exact` BNB Permit2 settlement through `MultiChainPayer`).
|
|
48
|
+
|
|
49
|
+
## [1.23.0] — 2026-06-14 — self-describing endpoints + discovery reach
|
|
50
|
+
|
|
51
|
+
### Added — self-describing, more discoverable endpoints (discoverability plan: Phases 1, 2, 4, 5)
|
|
52
|
+
|
|
53
|
+
- **Self-describing 402s, ON by default.** Every challenge a gate builds now carries an
|
|
54
|
+
`extensions.piprail` block — identity, per-rail how-to-pay, `npm i @piprail/sdk` + a paste-ready
|
|
55
|
+
snippet, the MCP command, and docs + discovery pointers — so any human, AI agent, or crawler that
|
|
56
|
+
lands on a gated endpoint (even the `onchain-proof` scheme a stock x402 client can't pay) knows what
|
|
57
|
+
it is and how to pay it. **Purely-additive** metadata in the spec-opaque `extensions` bag, and it
|
|
58
|
+
rides in the response **body** only — the base64 `payment-required` **header stays slim** (just
|
|
59
|
+
`accepts[]` + a small `bazaar`/rejection block), so the header, pay path, `accepts[]`, and status are
|
|
60
|
+
byte-identical (a rejection's `{code,detail}` are deep-merged as siblings and still win). Opt out with
|
|
61
|
+
`requirePayment({ selfDescribe: false })`. New exports: `buildSelfDescription`, `describeChallenge`, `BRAND`.
|
|
62
|
+
- **Human landing page + HTTP pointers.** `gate.landingPage(challenge)` / `renderLandingPage()` render a
|
|
63
|
+
tiny, HTML-escaped 402 page for a browser (serve it on `Accept: text/html`; agents/crawlers keep the
|
|
64
|
+
JSON 402). It leads with **how to pay** and a clear **caution** that a manual send to the address won't
|
|
65
|
+
unlock the resource (pay via an x402 client — no custody, no manual-payment desk). `discoveryHeaders()`
|
|
66
|
+
+ `POWERED_BY` emit a `Link` (rel `service-desc` / `x402-discovery`) + `x-powered-by` header bag to
|
|
67
|
+
spread on every response. The SDK serves nothing — the merchant does.
|
|
68
|
+
- **Facilitator-coverage map.** `KNOWN_FACILITATORS` (+ `knownFacilitatorsFor` /
|
|
69
|
+
`firstKeylessFacilitator`) — shipped data of which keyless facilitators settle `exact` on which
|
|
70
|
+
networks (seeded: PayAI on Base + Solana, dated-verified). `facilitatorCoverage(url)` /
|
|
71
|
+
`parseFacilitatorSupported(body)` read a facilitator's live `GET /supported` (best-effort, never throw).
|
|
72
|
+
- **Agent guide** gained a "landing cold — read the self-description" section (surfaced over the MCP).
|
|
73
|
+
|
|
74
|
+
All of the above is additive + opt-in; the zero-config pay path is byte-identical to before. New pure
|
|
75
|
+
modules (`selfdescribe.ts`, `landing.ts`, `facilitators.ts`) join the viem-free protocol-layer grep.
|
|
76
|
+
|
|
77
|
+
### Removed
|
|
78
|
+
- **`base-sepolia` (84532) testnet entry** from `EXACT_NETWORK_SLUGS` and the Permit2 proxy chain-id set
|
|
79
|
+
— mainnets only, no testnet presets (it was dead reference data; nothing settles on a testnet).
|
|
80
|
+
|
|
81
|
+
### Fixed
|
|
82
|
+
- **`x-powered-by` is now ASCII** (`PipRail x402 | https://piprail.com`) — the previous non-ASCII middot
|
|
83
|
+
mangled to `·` over Node's latin1 header transport. `GENERATOR` keeps its `·` (JSON body only).
|
|
84
|
+
- **`renderLandingPage` never throws** on a rail missing `amount`/`asset` (matches the module's
|
|
85
|
+
never-throw contract).
|
|
86
|
+
- **`fetchFacilitatorFeePayer` matches the network normalized** (CAIP-2 *or* slug) so a slug-reporting
|
|
87
|
+
facilitator's Solana exact fee-payer is found instead of silently dropped.
|
|
88
|
+
|
|
7
89
|
## [1.22.1] — 2026-06-13 — docs: community links + integrations signposting
|
|
8
90
|
|
|
9
91
|
Docs-only patch — **no code change** (the SDK is byte-identical to 1.22.0). Refreshes the README:
|
|
@@ -982,6 +1064,7 @@ straight into your wallet. The API is small and self-contained.
|
|
|
982
1064
|
to your wallet; PipRail never holds funds.
|
|
983
1065
|
- `viem ^2.21` is a peer dependency. Node 20+ or a modern browser.
|
|
984
1066
|
|
|
1067
|
+
[1.24.0]: https://www.npmjs.com/package/@piprail/sdk
|
|
985
1068
|
[1.15.1]: https://www.npmjs.com/package/@piprail/sdk
|
|
986
1069
|
[1.15.0]: https://www.npmjs.com/package/@piprail/sdk
|
|
987
1070
|
[1.14.0]: https://www.npmjs.com/package/@piprail/sdk
|
package/README.md
CHANGED
|
@@ -36,6 +36,33 @@ const client = new PipRailClient({ chain: 'base', wallet: { privateKey: process.
|
|
|
36
36
|
const res = await client.fetch('https://api.example.com/report') // hits the 402, pays it, retries with proof
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
## Pay across chains — one buyer, a wallet per chain
|
|
40
|
+
|
|
41
|
+
A client is bound to one chain (an EVM key can't sign a Solana tx). To pay a 402
|
|
42
|
+
on **whatever chain it asks for**, give a `MultiChainPayer` one wallet per chain —
|
|
43
|
+
it surveys every chain you hold and pays the **first one you listed** that can settle
|
|
44
|
+
(your preference; within a chain, the cheapest-gas rail — there's no oracle to compare
|
|
45
|
+
gas across coins):
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { MultiChainPayer } from '@piprail/sdk'
|
|
49
|
+
|
|
50
|
+
const payer = MultiChainPayer.fromWallets({
|
|
51
|
+
wallets: {
|
|
52
|
+
base: { privateKey: process.env.EVM_KEY }, // one EVM key works on every EVM chain
|
|
53
|
+
solana: { secretKey: process.env.SOLANA_KEY },
|
|
54
|
+
xrpl: { seed: process.env.XRPL_SEED },
|
|
55
|
+
},
|
|
56
|
+
policy: { maxAmount: '1.00', maxTotal: '10.00', tokens: ['USDC', 'USDT'] }, // one budget, every chain
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
await payer.planPayment(url) // read-only: every chain ranked, payable-first in your listed order
|
|
60
|
+
const res = await payer.get(url) // pays on the first chain that can settle — same spend policy, no manual routing
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Built on `planAcross` / `fetchAcross` (the same composable primitives, for when you
|
|
64
|
+
already hold an array of clients). See [`examples/multi-chain`](../examples/multi-chain).
|
|
65
|
+
|
|
39
66
|
The same app can **take** payments and **make** them. → [Making payments](https://docs.piprail.com/making-payments/piprail-client/)
|
|
40
67
|
|
|
41
68
|
---
|
|
@@ -46,7 +73,7 @@ The same app can **take** payments and **make** them. → [Making payments](http
|
|
|
46
73
|
|---|---|
|
|
47
74
|
| **[Getting started](https://docs.piprail.com/getting-started/introduction/)** | Install · quickstart · how it works |
|
|
48
75
|
| **[Accepting payments](https://docs.piprail.com/accepting-payments/require-payment-and-gate/)** | `requirePayment` · `createPaymentGate` · the `exact` rail |
|
|
49
|
-
| **[Making payments](https://docs.piprail.com/making-payments/piprail-client/)** | `PipRailClient` · `quote` · `estimateCost` · `planPayment` · auto-route |
|
|
76
|
+
| **[Making payments](https://docs.piprail.com/making-payments/piprail-client/)** | `PipRailClient` · `quote` · `estimateCost` · `planPayment` · auto-route · `MultiChainPayer` |
|
|
50
77
|
| **[Spend controls](https://docs.piprail.com/spend-controls/payment-policy/)** | Budgets · time envelope · the spend ledger |
|
|
51
78
|
| **[Agent toolkit](https://docs.piprail.com/agent-toolkit/payment-tools/)** | `paymentTools` · the agent guide · NL renderers |
|
|
52
79
|
| **[Discovery](https://docs.piprail.com/discovery/discover-and-register/)** | Find & be found on the open x402 indexes ($0, no backend) |
|