@piprail/sdk 2.15.0 → 2.16.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +260 -0
  2. package/README.md +2 -2
  3. package/dist/{algorand-CSEUTWNW.js → algorand-25FMBCT3.js} +13 -4
  4. package/dist/{algorand-I2O24RSM.cjs → algorand-Q3TUQLPK.cjs} +49 -40
  5. package/dist/{aptos-J7QGCWNW.js → aptos-SBV6SGDP.js} +3 -3
  6. package/dist/{aptos-GA5AEYYL.cjs → aptos-VBJONBFY.cjs} +36 -36
  7. package/dist/chunk-2CX7XRZK.js +649 -0
  8. package/dist/chunk-C52H5TYB.js +474 -0
  9. package/dist/chunk-OXEFPLZA.cjs +474 -0
  10. package/dist/{chunk-3FR22M3O.cjs → chunk-QONQSZHJ.cjs} +2 -2
  11. package/dist/{chunk-3ZWM3DKM.js → chunk-QU25LSVS.js} +2 -2
  12. package/dist/chunk-V2IJ5HUW.cjs +649 -0
  13. package/dist/index.cjs +507 -1347
  14. package/dist/index.d.cts +39 -6
  15. package/dist/index.d.ts +39 -6
  16. package/dist/index.js +192 -1032
  17. package/dist/{ledger-uFtXlIHY.d.cts → ledger-Crc1bZox.d.cts} +164 -10
  18. package/dist/{ledger-uFtXlIHY.d.ts → ledger-Crc1bZox.d.ts} +164 -10
  19. package/dist/{near-NDQLGI4U.cjs → near-5LTTDU6G.cjs} +27 -27
  20. package/dist/{near-YA4Y4IGC.js → near-OLKCMTBI.js} +1 -1
  21. package/dist/node.d.cts +2 -2
  22. package/dist/node.d.ts +2 -2
  23. package/dist/{solana-OU6IUMQM.cjs → solana-AI2G7V33.cjs} +73 -52
  24. package/dist/{solana-MB6G3OA5.js → solana-HTKDRTD3.js} +32 -11
  25. package/dist/{stellar-RSNVOIKL.cjs → stellar-E2KWEV2E.cjs} +20 -20
  26. package/dist/{stellar-WDWSYOED.js → stellar-EUFZLX6J.js} +1 -1
  27. package/dist/{sui-GVSFNNJX.js → sui-KCIITCYH.js} +6 -2
  28. package/dist/{sui-MVC46BFJ.cjs → sui-Q3NJOJZS.cjs} +21 -17
  29. package/dist/{ton-MYOI3U3H.cjs → ton-J7TQWRN4.cjs} +16 -16
  30. package/dist/{ton-EJMGM3YI.js → ton-WH2JVQOO.js} +1 -1
  31. package/dist/{tron-VAPVMGJK.js → tron-FXBXDNEY.js} +1 -1
  32. package/dist/{tron-42X5N4GQ.cjs → tron-HIPMOX7S.cjs} +25 -25
  33. package/dist/{xrpl-Q2UWPCTJ.js → xrpl-RUOB37QH.js} +283 -5
  34. package/dist/{xrpl-MGUBUNSZ.cjs → xrpl-SVVS445B.cjs} +312 -34
  35. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,264 @@ 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
+ ## [2.16.0] — 2026-09-06 — the XRP Ledger `exact` buyer, and paying the 91% of the x402 web we could not
8
+
9
+ ### Added — the XRP Ledger `exact` buyer (stage 04, first family)
10
+
11
+ - **A PipRail agent can now pay a standard x402 `exact` rail on the XRP Ledger** — 1,732 live rails,
12
+ and as far as we can tell the only open-source buyer on that chain. `drivers/xrpl/exact.ts`
13
+ implements `scheme_exact_xrpl.md` in both directions: the buyer signs a complete `Payment` and
14
+ broadcasts nothing; the gate decodes the blob, re-derives every checked field from its own trusted
15
+ accept, submits, and waits for a validated `tesSUCCESS`.
16
+
17
+ XRPL is the family where **the payer pays the fee** (it is a field inside the signed transaction),
18
+ so there is no sponsor, no co-signature, and no fee-drain guard. The scheme replaces those with a
19
+ list of MUST-NOTs the settler enforces — no `Memos`, no `Paths`, no `tfPartialPayment`, no
20
+ `Delegate`, never `Amount` and `DeliverMax` together — each of which is a way a hostile blob could
21
+ underdeliver, and each of which has a test.
22
+
23
+ The challenge binds through `InvoiceID = SHA-256(extra.invoiceId)`, **not** a memo: the scheme has
24
+ settlers reject `Memos`, which is exactly what PipRail's `onchain-proof` XRPL path uses for its
25
+ nonce. The two paths deliberately share no code.
26
+
27
+ **`extra.areFeesSponsored` is required by the scheme and absent from 100% of live rails**, so it is
28
+ read as *false unless present*. Requiring it would have rejected the entire deployed XRPL x402 web
29
+ — the third instance of that bug class this release cycle, and the reason it now has a test of its
30
+ own.
31
+
32
+ **Native XRP only, deliberately.** The two XRPL asset forms use different wire amount conventions
33
+ (native is an integer *drops* string, an issued currency is a *decimal* `value` — both verified
34
+ against live merchant challenges). The SDK prices and spend-caps in base units, so reading an
35
+ IOU's `"12"` as base units would understate a 12-RLUSD payment by 10^15 and slip it under any
36
+ policy cap while the buyer signed the real amount. Issued currencies are therefore dropped at
37
+ gather rather than mispriced; the RLUSD half waits until that decimal path is threaded through
38
+ quoting and the policy.
39
+
40
+ - **`ResolvedNetwork.exactPayableAsset?(asset)`** — a new optional driver hook. The buyer's gather
41
+ used to hard-code "any recognised token is exact-payable, the native coin never is", which was
42
+ true until XRPL, where it is wrong in *both* directions. A driver that declares the hook is
43
+ authoritative for all of its assets; every other family omits it and behaves exactly as before.
44
+
45
+ - **Three conformance fixes found by pointing the buyer at real merchants**, each of which made a
46
+ live XRPL rail unpayable on its own:
47
+ - `describeAsset` and `recipientReady` only knew the native coin as `'native'`. The XRPL x402 web
48
+ writes it `'XRP'` — all 863 native rails do. The first miss dropped every rail at gather ("no
49
+ compatible accept" on a chain we fully support); the second then demanded a **trustline** for a
50
+ coin that cannot have one. Both now go through one `isXrpNative()` predicate.
51
+ - `LastLedgerSequence` was a fixed 20-ledger window. The settler checks it is "within policy
52
+ window", and the merchant's policy is what `maxTimeoutSeconds` announces — so a rail offering
53
+ 60s was getting an ~80s blob, one that outlives the merchant's own quote. Now derived, clamped.
54
+ - `Flags` was hard-set to `0`. `tfPartialPayment` is a different bit, so zero was never needed to
55
+ exclude it — but it made our transactions the only ones on the ledger without
56
+ `tfFullyCanonicalSig`. Now set, matching what every other XRPL client sends.
57
+ - `extra.sourceTag` is mirrored into the transaction's `SourceTag`. Not in the scheme's `extra`
58
+ table, but present on **1,728 of the 1,732** live rails — it is how deployed vendors correlate a
59
+ payment back to their own quote.
60
+ - Live-proven on mainnet: settle tx
61
+ `F673064D2E0D982481844A769E829FD8B486EB9FB7B014F866C92F1C49770505` — 0.01 XRP through our own
62
+ gate on the `exact` rail, payer debited 0.010012 XRP (the amount **plus its own 12-drop fee**),
63
+ merchant credited 0.010000, and a genuine replay of the same signed blob refused with a 402.
64
+ - Buyer-side errors are mapped per `ERRORS.md` §9: an unactivated payer account (XRPL reports it as
65
+ `actNotFound`, but on that ledger "does not exist" means "unfunded") becomes
66
+ `InsufficientFundsError` with the base-reserve fix in the message; an `xrpl.js` validation failure
67
+ at signing becomes `UnsupportedSchemeError` carrying the library's reason; any other RPC failure is
68
+ rethrown unchanged rather than disguised as an affordability problem. All three are tested.
69
+ - **Not proven, and it is NOT our bug: a paid round-trip against a THIRD-PARTY XRPL merchant.**
70
+ Two live vendors refuse our payload with `invalid_payload` — from **their own pre-check**, before
71
+ their facilitator ever sees it (proven by routing a v1-shaped body past that pre-check, which then
72
+ returned the facilitator's own schema error: it wants exactly the `{x402Version: 2, accepted,
73
+ payload}` envelope we already send). The decisive test: **the same client, envelope and header pay
74
+ one of those same gateways successfully on its Base rail** (0.001 USDC, HTTP 200). So our envelope
75
+ is correct and the fault is XRPL-specific on their side. Our transaction also matches, field for
76
+ field, a payment that merchant has accepted from another client. Working conclusion: the deployed
77
+ XRPL dialect has drifted from `scheme_exact_xrpl.md` — worth raising upstream.
78
+ **No funds were lost across ~10 refused attempts** — every vendor rejects before settling, and the
79
+ ledger confirms no payment ever left the wallet.
80
+
81
+
82
+ ### Fixed — the buyer required a field the spec makes optional, and so could not pay 91% of the x402 web
83
+
84
+ - **`schemes: ['exact']` clients now pay the standard x402 web: 26.3% → 98.8% of the resources
85
+ listed on the CDP Bazaar** (15,686 resources / 40,388 `exact` rails, measured 2026-09-06 by
86
+ `npm run x402:coverage`; the figures live in `scripts/x402-corpus/coverage.json`, which owns
87
+ them). Rail-level coverage went 9.2% → 88.9%.
88
+
89
+ The buyer's gather predicate required `extra.assetTransferMethod` on every `exact` rail. The
90
+ ratified scheme says the opposite — `scheme_exact_evm.md`: *"If no `assetTransferMethod` is
91
+ specified in `PaymentRequired.extra`, clients should default to `"eip3009"`"* — and the SVM,
92
+ Algorand, Aptos, NEAR and Hedera schemes never define the key at all (their required extra is
93
+ `feePayer`). Only **9%** of live rails carry it; **79%** carry just the EIP-712 domain. So a
94
+ PipRail agent silently refused every Solana rail, every Arbitrum rail and 85% of Base rails —
95
+ including textbook canonical-USDC EIP-3009 rails it was perfectly able to sign.
96
+
97
+ This was never a pay-path bug: the EVM driver already defaulted to EIP-3009 and re-derives the
98
+ token's EIP-712 domain on-chain. Only the gather refused. A rail that NAMES a method we don't
99
+ implement (the spec's `erc7710`) is still skipped rather than signed blind.
100
+
101
+ Found by probing seven real third-party 402s and paying none of them, while 1,636 unit tests were
102
+ green. The tests agreed with their own hand-built fixtures; nothing checked the fixtures against
103
+ the world. That gap is now closed by `sdk/test/x402-corpus.test.ts` — 62 REAL challenges captured
104
+ from the live index, replayed through the real client.
105
+
106
+ ### Fixed — the Solana buyer required `extra.decimals`, which the SVM scheme never defines
107
+
108
+ - **99.3% of live Solana rails planned as payable and then threw at signing time.**
109
+ `payExactSolana` required `extra.decimals` for the TransferChecked and raised
110
+ `UnsupportedSchemeError` without it — but `scheme_exact_svm.md` doesn't define `decimals` at all
111
+ (its one required extra is `feePayer`), and only **41 of the 5,760** live Solana rails carry it.
112
+ The same "we require a field the spec makes optional" mistake as `assetTransferMethod`, one layer
113
+ deeper, and worse because it broke the plan-vs-pay contract: the rail gathered, `planPayment`
114
+ said `payable`, and the payment then failed.
115
+
116
+ The decimals now come **from the mint on-chain**, which is also the safer source — a server that
117
+ understated them could otherwise have the buyer sign a transfer 10^n larger than the quote, and
118
+ TransferChecked would accept it. A rail that states decimals disagreeing with the mint is refused;
119
+ `extra.decimals` survives only as a fallback if the mint read fails.
120
+
121
+ Found by paying a real Solana merchant. No unit test could have caught it — every fixture in the
122
+ suite carried `extra.decimals`, because our own gate always emits it.
123
+
124
+ ### Live-proven on mainnet (2026-09-06)
125
+
126
+ Real payments to real third-party merchants, one per wire shape this release unlocks:
127
+
128
+ | shape | merchant | proof |
129
+ |---|---|---|
130
+ | v2 · Base · domain-only (no marker) | `api.onesource.io/api/chain/block-number` | 200, 0.001 USDC, `0x44e9fed18bf9…` |
131
+ | v2 · Base · slug network | `coil.trade/api/crypto/gate` | 200, 0.001 USDC, `0xc38e6da9cbc2…` |
132
+ | **v1** · Base · `X-PAYMENT` wire | `x402factory.ai/base/coinprice` | 200, 0.001 USDC, `eip3009-nonce:0xb6c321fa…` |
133
+ | v2 · Solana · `feePayer`, no marker | `hypernatt.com/api/m2m/liq-radar` | 200, 0.001 USDC, `solana-nonce:3g3Wj5G6G7D…` |
134
+
135
+ Every row is a real body from a real stranger's endpoint, re-run against the FINAL build of this
136
+ release (an earlier run on the same day proved the same four shapes).
137
+
138
+ Own-gate regression, also on mainnet and also re-run on the final build: Base USDC + EURC
139
+ round-tripped gasless via EIP-3009 — payer ETH delta **0**, merchant received, **replay rejected**
140
+ (settle `0x85dfcc2b480f…` / `0x41fa1bdf5ea1…`); Algorand settled live with the enriched receipt and
141
+ a rejected replay (`TBRSSLNUJGCUS6…`).
142
+
143
+ ### Added — x402 **v1** challenges on the buyer path
144
+
145
+ - **A v1 server can now be quoted and paid.** PipRail's stated posture has always been "emit strict
146
+ v2, accept liberal v1 + v2", and the gate honoured it — but the buyer's `isValidChallenge`
147
+ hard-required `x402Version === 2`, so every v1 server was an `InvalidEnvelopeError`, and no v1
148
+ header emitter existed to answer one with. 251 of the 15,686 catalogued resources are v1.
149
+ `normalizeV1Challenge` lifts a v1 body (`maxAmountRequired`, slug network, per-accept `resource`
150
+ string) into the standard shape, and `buildV1PaymentHeader` frames the answer on the v1 wire.
151
+ Probed live: a v1 server ignores `PAYMENT-SIGNATURE` and reads only `X-PAYMENT`, so the network
152
+ slug is echoed back exactly as the server sent it (v1 verifiers string-compare it).
153
+ The slug is remembered on a synthetic `wireNetwork` field, which is **stripped from every
154
+ outgoing `accepted` echo** — that block is documented as the merchant's rail returned verbatim,
155
+ and facilitators compare (some hash) it, so a key the merchant never published must not appear
156
+ in it.
157
+
158
+ ### Added — the Algorand CAIP-2 id the spec actually uses
159
+
160
+ - `scheme_exact_algo.md` gives Algorand mainnet as the base64 genesis hash **truncated to 32 chars**
161
+ (`algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73k`), and the deployed web follows it — 874 live rails on
162
+ the spec form vs 589 on the padded 44-char id PipRail binds. Both forms are now accepted on parse.
163
+ What PipRail *emits* is unchanged (switching that would break deployed 2.x Algorand buyers; it is
164
+ queued for the next major).
165
+
166
+ - **The GATE now matches an echoed network id through the same canonicalisation.** It compared
167
+ `accepted.network` to its own rail with `===`, so a conformant buyer that canonicalised the id
168
+ before echoing it — the Algorand spec form, or TON's superseded `ton:-239` — had a perfectly
169
+ valid payment refused. All three selector sites (`onchain-proof`, `exact`, `upto`) now compare
170
+ through `normalizeNetwork`. This can only widen which of the gate's OWN rails is selected: payTo,
171
+ amount and asset are re-derived from the gate's spec either way, so a wrong network still cannot
172
+ redirect a payment. The Algorand driver's `supports()` accepts both spellings directly too,
173
+ mirroring TON.
174
+
175
+ ### Added — coverage is now measured, not asserted
176
+
177
+ - **`npm run x402:coverage`** (`scripts/x402-corpus/audit.mjs`) pages the live CDP Bazaar, classifies
178
+ every rail against the SDK's own exported predicates, and writes `scripts/x402-corpus/coverage.json`.
179
+ Every coverage figure in this repo cites that file. `--live` runs a real read-only `quote()` against
180
+ live third-party 402s (8/9 quotable, the 9th a merchant whose `upto` rail omits the spec-mandated
181
+ `facilitatorAddress`); `--fixture` regenerates the regression corpus.
182
+ - `PayOption.method` reports the transfer method the buyer would actually sign, appending
183
+ `' (default)'` when the rail named none — so the inference is visible instead of implicit.
184
+ - New exports: `exactTransferMethod`, `isSettleableExactMethod`, `KNOWN_EXACT_TRANSFER_METHODS`,
185
+ `DEFAULT_EXACT_TRANSFER_METHOD`, `normalizeV1Challenge`, `buildV1PaymentHeader`.
186
+
187
+ ### Changed — types now admit what the wire allows
188
+
189
+ - `X402ExactAcceptEntry.extra` and `extra.assetTransferMethod` are **optional**. A conformant rail may
190
+ ship `extra: {}` or omit the block entirely, and the type said otherwise, which is how a
191
+ bare-`extra` deref came to look like a real hazard. Every read on the buyer path optional-chains.
192
+ Defaults are unchanged: `schemes` still defaults to `['onchain-proof']`, so a zero-config client
193
+ behaves byte-identically.
194
+
195
+ ## [2.15.1] — 2026-08-28 — a dead Sui RPC, an RPC failure blamed on the token, and fuzz hardening
196
+
197
+ ### Fixed — Sui's shipped default RPC was dead (whole-chain outage)
198
+
199
+ - **`chain: 'sui'` with no `rpcUrl` was 100% non-functional.** The preset shipped
200
+ `https://fullnode.mainnet.sui.io:443`, and Mysten has since **turned JSON-RPC off** on the
201
+ official public fullnodes — *every* method now answers `-32601 Method not found. JSON-RPC on
202
+ public fullnodes has been deprecated`, pointing at gRPC/GraphQL. Since `pay`, `verify`,
203
+ `balanceOf` and `recipientReady` all read through `SuiJsonRpcClient`, nothing on Sui worked
204
+ unless the caller passed their own endpoint. The default is now
205
+ `https://sui-rpc.publicnode.com`, verified for `suix_getBalance`, `suix_getCoinMetadata`,
206
+ `suix_getOwnedObjects` and `sui_getTransactionBlock`.
207
+ No unit test could catch this — they all mock the reader. Caught by the live wallet sweep,
208
+ which reads each driver's `defaultRpc` and does a real balance read; that sweep is now a
209
+ stage of the SDK audit so a dead default surfaces immediately.
210
+
211
+ ### Fixed — the gate blamed the token when the RPC was at fault
212
+
213
+ - **A transient RPC failure was reported as "this token isn't EIP-3009".** `readExactDomain`
214
+ fails closed to `null` on two very different causes — the token genuinely isn't EIP-3009, or
215
+ the on-chain read failed (unreachable, rate-limited, or an HTML error page from a proxy) —
216
+ and the gate asserted the first. On Base USDC, which *is* EIP-3009, an unreachable RPC
217
+ produced `this token isn't EIP-3009 (auto-selected Permit2)`: the merchant's gasless rail
218
+ silently downgraded to `onchain-proof` (buyers start paying gas) and they were told to change
219
+ the one thing that was correct. The buyer side already retried once and named both causes;
220
+ the gate now does the same — a single 300 ms retry on the domain read, and a skip reason that
221
+ states both possibilities and how to tell them apart. Behaviour on a healthy RPC is unchanged.
222
+
223
+
224
+ ### Fixed — hardening found by a new adversarial fuzz sweep
225
+
226
+ `test/fuzz-payment-paths.test.ts` generates thousands of hostile inputs per run against the
227
+ paths that decide "was I actually paid?", seeded so a failure reproduces exactly
228
+ (`FUZZ_SEED=<n>`). It found three things, all fixed here. No API changed; every existing
229
+ call keeps its behaviour.
230
+
231
+ - **A spent proof could be re-redeemed by padding its reference with whitespace.** The
232
+ gate's single-use key was case-folded (EVM hashes are case-insensitive hex) but not
233
+ trimmed, so ` 0xabc…`, `0xabc… ` and `\t0xabc…\n` each hashed to a *different* key than
234
+ `0xabc…` and unlocked the resource a second time for one payment. The ref is now
235
+ canonicalised at the boundary, before the claim and before the driver sees it, so the
236
+ built-in store **and** a caller's custom `isUsed`/`markUsed` both receive one identical
237
+ value. Reaching this on a live EVM gate needed an RPC that accepts a padded hash — a real
238
+ one rejects it — so this was defence-in-depth rather than a known live exploit; the point
239
+ is that at-most-once no longer depends on how strict somebody else's RPC happens to be.
240
+ Six regression cases in `test/server-replay.test.ts`.
241
+ - **`parseUnits` threw a bare `Error`** on the single most likely amount mistake there is —
242
+ pricing at more decimal places than the token has — while its two other rejection branches
243
+ threw `InvalidConfigError`. A caller branching on `err instanceof PipRailError`
244
+ (ERRORS.md §1) missed exactly that case. Now typed, as is the malformed-amount branch of
245
+ `floorUnits`.
246
+ - **The header builders threw a raw `TypeError`** when handed a value `JSON.stringify`
247
+ cannot serialise — a `BigInt` most obviously, and amounts are `bigint` everywhere *inside*
248
+ the SDK, so passing one out is a natural slip. `buildSignatureHeader`,
249
+ `buildExactSignatureHeader` and their siblings now throw `InvalidConfigError` with a
250
+ message that names the fix.
251
+
252
+ ### Changed — facilitator registry hygiene
253
+
254
+ - **Two dead facilitators removed** (see 2.14.2): the docs, the website data and a live
255
+ example probe were still advertising them after the registry was corrected. A new
256
+ cross-surface guard (`test/facilitators-surface.test.ts`) now holds all of them to
257
+ `KNOWN_FACILITATORS` — a dead host may appear only inside an explanation of its removal,
258
+ the docs' copy-paste URL list is set-compared against the registry, and the website's
259
+ generated data must match the registry's networks **and URL order** (that order is what
260
+ `firstKeylessFacilitator` returns).
261
+ - All **25** settlement hashes recorded in the registry's notes were re-verified on-chain on
262
+ 2026-08-28 — 25/25 present and successful, 0 refuted — by
263
+ `.claude/skills/facilitator-probe/scripts/verify-tx.mjs`.
264
+
7
265
  ## [2.15.0] — 2026-06-24 — merchant on-ramp: presets, adapters, `gate.selfTest()`
8
266
 
9
267
  ### Added — merchant on-ramp (presets, adapters, self-test)
@@ -1852,6 +2110,8 @@ straight into your wallet. The API is small and self-contained.
1852
2110
  to your wallet; PipRail never holds funds.
1853
2111
  - `viem ^2.21` is a peer dependency. Node 20+ or a modern browser.
1854
2112
 
2113
+ [2.16.0]: https://www.npmjs.com/package/@piprail/sdk
2114
+ [2.15.1]: https://www.npmjs.com/package/@piprail/sdk
1855
2115
  [2.15.0]: https://www.npmjs.com/package/@piprail/sdk
1856
2116
  [2.14.0]: https://www.npmjs.com/package/@piprail/sdk
1857
2117
  [2.13.1]: https://www.npmjs.com/package/@piprail/sdk
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Accept and make [x402](https://x402.org) crypto payments — on any EVM chain plus Solana, TON, Tron, NEAR, Sui, Aptos, Algorand, Stellar & the XRP Ledger — in a couple of lines.**
4
4
 
5
- No middleman, no database, no fee, no account. Payments settle **straight into your wallet**, verified locally against your own RPC. Gate a route to make it paid-only; point an agent at a paid URL and it pays itself.
5
+ No middleman, no database, no fee, no account, no API key. Payments settle **straight into your wallet**, verified locally against your own RPC. Gate a route to make it paid-only; point an agent at a paid URL and it pays itself.
6
6
 
7
7
  ```bash
8
8
  npm install @piprail/sdk viem
@@ -26,7 +26,7 @@ app.get('/report',
26
26
 
27
27
  That route now costs **0.05 USDC on Base**, paid straight to your wallet. One parameter picks the chain. Add `onPaid` / `onFailed` to be notified the moment a payment settles or is rejected — both carry the same reason, and the buyer's client is notified too. → [Accepting payments](https://docs.piprail.com/accepting-payments/require-payment-and-gate/)
28
28
 
29
- > **Don't want to wire it by hand?** `npm create piprail@latest` scaffolds a complete, mainnet-ready, deployable merchant (node / Cloudflare / Vercel) — paste a public address, no key, no account. → [Scaffold a merchant](https://docs.piprail.com/getting-started/scaffolder/)
29
+ > **Don't want to wire it by hand?** `npm create @piprail` scaffolds a complete, mainnet-ready, deployable merchant (node / Cloudflare / Vercel) — paste a public address, no key, no account. → [Scaffold a merchant](https://docs.piprail.com/getting-started/scaffolder/)
30
30
 
31
31
  ## Let an agent pay for it
32
32
 
@@ -1,3 +1,6 @@
1
+ import {
2
+ ALGORAND_SPEC_CAIP2
3
+ } from "./chunk-2CX7XRZK.js";
1
4
  import {
2
5
  ConfirmationTimeoutError,
3
6
  InsufficientFundsError,
@@ -10,7 +13,7 @@ import {
10
13
  nativeCost,
11
14
  rejectForeignToken,
12
15
  toInsufficientFundsError
13
- } from "./chunk-3ZWM3DKM.js";
16
+ } from "./chunk-QU25LSVS.js";
14
17
 
15
18
  // src/drivers/algorand/index.ts
16
19
  import algosdk3 from "algosdk";
@@ -110,7 +113,7 @@ async function payExactAlgorand(input) {
110
113
  "Algorand exact is ASA-only (an asset transfer); native ALGO is not exact-payable. Pay via onchain-proof."
111
114
  );
112
115
  }
113
- const feePayer = accept.extra.feePayer;
116
+ const feePayer = accept.extra?.feePayer;
114
117
  if (!feePayer) {
115
118
  throw new UnsupportedSchemeError("Algorand exact rail must advertise extra.feePayer (the fee sponsor address).");
116
119
  }
@@ -171,7 +174,7 @@ function decodeElement(bytes) {
171
174
  }
172
175
  async function verifyAndSettleExactAlgorand(input) {
173
176
  const { client, feePayerSk, feePayerAddr, payload, accept } = input;
174
- const railFeePayer = accept.extra.feePayer;
177
+ const railFeePayer = accept.extra?.feePayer;
175
178
  if (!railFeePayer) throw new SettlementError("Algorand exact: rail is missing extra.feePayer.");
176
179
  if (railFeePayer !== feePayerAddr) {
177
180
  throw new SettlementError("Algorand exact: the gate relayer address does not match the rail extra.feePayer \u2014 misconfigured rail.");
@@ -463,7 +466,13 @@ function makeAlgorandNetwork(preset, algodUrl) {
463
466
  return {
464
467
  family: "algorand",
465
468
  network,
466
- supports: (n) => n === network,
469
+ // Accept our padded 44-char genesis-hash id AND the spec's 32-char truncated form
470
+ // (`scheme_exact_algo.md`'s Network Identifiers table — the form 874 of the 1,463 live
471
+ // Algorand rails use), so a caller that bypasses `normalizeNetwork` and hands the raw
472
+ // spec id to the driver still matches. Mirrors TON's legacy-id tolerance; the
473
+ // normalizeNetwork alias remains the canonical seam. We still EMIT the padded form —
474
+ // changing that would break every deployed PipRail 2.x buyer, so it waits for a major.
475
+ supports: (n) => n === network || n === ALGORAND_SPEC_CAIP2,
467
476
  resolveToken(token) {
468
477
  if (token === "native") {
469
478
  return { asset: "native", decimals: ALGO_DECIMALS, symbol: ALGO_SYMBOL };
@@ -1,5 +1,6 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
+ var _chunkV2IJ5HUWcjs = require('./chunk-V2IJ5HUW.cjs');
3
4
 
4
5
 
5
6
 
@@ -10,7 +11,9 @@
10
11
 
11
12
 
12
13
 
13
- var _chunk3FR22M3Ocjs = require('./chunk-3FR22M3O.cjs');
14
+
15
+
16
+ var _chunkQONQSZHJcjs = require('./chunk-QONQSZHJ.cjs');
14
17
 
15
18
  // src/drivers/algorand/index.ts
16
19
  var _algosdk = require('algosdk'); var _algosdk2 = _interopRequireDefault(_algosdk);
@@ -58,13 +61,13 @@ async function payAlgorand(params) {
58
61
  } catch (err) {
59
62
  const mapped = mapAlgorandError(err, accept.payTo);
60
63
  if (mapped) throw mapped;
61
- throw _nullishCoalesce(_chunk3FR22M3Ocjs.toInsufficientFundsError.call(void 0, err), () => ( err));
64
+ throw _nullishCoalesce(_chunkQONQSZHJcjs.toInsufficientFundsError.call(void 0, err), () => ( err));
62
65
  }
63
66
  }
64
67
  function mapAlgorandError(err, payTo) {
65
68
  const m = err instanceof Error ? err.message : String(err);
66
69
  if (/must optin/i.test(m) || /missing from/i.test(m) && m.includes(payTo)) {
67
- return new (0, _chunk3FR22M3Ocjs.RecipientNotReadyError)(
70
+ return new (0, _chunkQONQSZHJcjs.RecipientNotReadyError)(
68
71
  `Algorand recipient ${payTo} hasn't opted into this asset \u2014 it must opt in (a 0-amount asset transfer to itself) before it can receive. (Algorand: ${firstLine(m)})`,
69
72
  { cause: err }
70
73
  );
@@ -72,7 +75,7 @@ function mapAlgorandError(err, payTo) {
72
75
  if (/overspend|below min|min(imum)? balance|tried to spend|balance \d+ below|asset \d+ missing from|insufficient|underflow/i.test(
73
76
  m
74
77
  )) {
75
- return new (0, _chunk3FR22M3Ocjs.InsufficientFundsError)(
78
+ return new (0, _chunkQONQSZHJcjs.InsufficientFundsError)(
76
79
  `Algorand payment failed: the sender can't cover it \u2014 token balance, ALGO for fees, the 0.1-ALGO minimum balance, or a missing asset opt-in on the sender. (Algorand: ${firstLine(m)})`,
77
80
  { cause: err }
78
81
  );
@@ -99,33 +102,33 @@ function isSet(addr) {
99
102
  function randomNote() {
100
103
  const g = globalThis.crypto;
101
104
  if (!_optionalChain([g, 'optionalAccess', _ => _.getRandomValues])) {
102
- throw new (0, _chunk3FR22M3Ocjs.UnsupportedSchemeError)("Algorand exact: no Web Crypto CSPRNG available to generate a unique note.");
105
+ throw new (0, _chunkQONQSZHJcjs.UnsupportedSchemeError)("Algorand exact: no Web Crypto CSPRNG available to generate a unique note.");
103
106
  }
104
107
  return g.getRandomValues(new Uint8Array(32));
105
108
  }
106
109
  async function payExactAlgorand(input) {
107
110
  const { suggestedParams, sk, sender, accept } = input;
108
111
  if (accept.asset === "native") {
109
- throw new (0, _chunk3FR22M3Ocjs.UnsupportedSchemeError)(
112
+ throw new (0, _chunkQONQSZHJcjs.UnsupportedSchemeError)(
110
113
  "Algorand exact is ASA-only (an asset transfer); native ALGO is not exact-payable. Pay via onchain-proof."
111
114
  );
112
115
  }
113
- const feePayer = accept.extra.feePayer;
116
+ const feePayer = _optionalChain([accept, 'access', _2 => _2.extra, 'optionalAccess', _3 => _3.feePayer]);
114
117
  if (!feePayer) {
115
- throw new (0, _chunk3FR22M3Ocjs.UnsupportedSchemeError)("Algorand exact rail must advertise extra.feePayer (the fee sponsor address).");
118
+ throw new (0, _chunkQONQSZHJcjs.UnsupportedSchemeError)("Algorand exact rail must advertise extra.feePayer (the fee sponsor address).");
116
119
  }
117
120
  const assetId = parseAlgorandAssetId(accept.asset);
118
121
  if (assetId === null) {
119
- throw new (0, _chunk3FR22M3Ocjs.UnsupportedSchemeError)(`Algorand exact rail asset "${accept.asset}" must be a numeric ASA id.`);
122
+ throw new (0, _chunkQONQSZHJcjs.UnsupportedSchemeError)(`Algorand exact rail asset "${accept.asset}" must be a numeric ASA id.`);
120
123
  }
121
124
  if (!_algosdk2.default.isValidAddress(feePayer)) {
122
- throw new (0, _chunk3FR22M3Ocjs.UnsupportedSchemeError)(`Algorand exact: extra.feePayer "${feePayer}" is not a valid Algorand address.`);
125
+ throw new (0, _chunkQONQSZHJcjs.UnsupportedSchemeError)(`Algorand exact: extra.feePayer "${feePayer}" is not a valid Algorand address.`);
123
126
  }
124
127
  if (!_algosdk2.default.isValidAddress(accept.payTo)) {
125
- throw new (0, _chunk3FR22M3Ocjs.UnsupportedSchemeError)(`Algorand exact: payTo "${accept.payTo}" is not a valid Algorand address.`);
128
+ throw new (0, _chunkQONQSZHJcjs.UnsupportedSchemeError)(`Algorand exact: payTo "${accept.payTo}" is not a valid Algorand address.`);
126
129
  }
127
130
  if (feePayer === sender) {
128
- throw new (0, _chunk3FR22M3Ocjs.UnsupportedSchemeError)(
131
+ throw new (0, _chunkQONQSZHJcjs.UnsupportedSchemeError)(
129
132
  "Algorand exact: the fee payer must differ from the payer \u2014 the buyer must pay ZERO ALGO (the sponsor pays the pooled fee)."
130
133
  );
131
134
  }
@@ -171,14 +174,14 @@ function decodeElement(bytes) {
171
174
  }
172
175
  async function verifyAndSettleExactAlgorand(input) {
173
176
  const { client, feePayerSk, feePayerAddr, payload, accept } = input;
174
- const railFeePayer = accept.extra.feePayer;
175
- if (!railFeePayer) throw new (0, _chunk3FR22M3Ocjs.SettlementError)("Algorand exact: rail is missing extra.feePayer.");
177
+ const railFeePayer = _optionalChain([accept, 'access', _4 => _4.extra, 'optionalAccess', _5 => _5.feePayer]);
178
+ if (!railFeePayer) throw new (0, _chunkQONQSZHJcjs.SettlementError)("Algorand exact: rail is missing extra.feePayer.");
176
179
  if (railFeePayer !== feePayerAddr) {
177
- throw new (0, _chunk3FR22M3Ocjs.SettlementError)("Algorand exact: the gate relayer address does not match the rail extra.feePayer \u2014 misconfigured rail.");
180
+ throw new (0, _chunkQONQSZHJcjs.SettlementError)("Algorand exact: the gate relayer address does not match the rail extra.feePayer \u2014 misconfigured rail.");
178
181
  }
179
182
  const assetId = parseAlgorandAssetId(accept.asset);
180
- if (assetId === null) throw new (0, _chunk3FR22M3Ocjs.SettlementError)(`Algorand exact: rail asset "${accept.asset}" is not a numeric ASA id.`);
181
- if (!_algosdk2.default.isValidAddress(accept.payTo)) throw new (0, _chunk3FR22M3Ocjs.SettlementError)(`Algorand exact: rail payTo "${accept.payTo}" is invalid.`);
183
+ if (assetId === null) throw new (0, _chunkQONQSZHJcjs.SettlementError)(`Algorand exact: rail asset "${accept.asset}" is not a numeric ASA id.`);
184
+ if (!_algosdk2.default.isValidAddress(accept.payTo)) throw new (0, _chunkQONQSZHJcjs.SettlementError)(`Algorand exact: rail payTo "${accept.payTo}" is invalid.`);
182
185
  const { paymentIndex, paymentGroup } = payload;
183
186
  if (paymentGroup.length !== GROUP_SIZE) {
184
187
  return fail("signature_invalid", `Algorand exact expects a ${GROUP_SIZE}-transaction group (payment + fee), got ${paymentGroup.length}.`);
@@ -206,7 +209,7 @@ async function verifyAndSettleExactAlgorand(input) {
206
209
  return fail("transfer_not_found", `Transfer asset id ${at.assetIndex} \u2260 rail asset ${assetId}.`);
207
210
  }
208
211
  if (!addrEq(at.receiver, accept.payTo)) {
209
- return fail("wrong_recipient", `Transfer pays ${_optionalChain([at, 'access', _2 => _2.receiver, 'optionalAccess', _3 => _3.toString, 'call', _4 => _4()])}, not payTo ${accept.payTo}.`);
212
+ return fail("wrong_recipient", `Transfer pays ${_optionalChain([at, 'access', _6 => _6.receiver, 'optionalAccess', _7 => _7.toString, 'call', _8 => _8()])}, not payTo ${accept.payTo}.`);
210
213
  }
211
214
  if (BigInt(at.amount) < BigInt(accept.amount)) {
212
215
  return fail("amount_too_low", `Transfer pays ${at.amount} of the ASA, required ${accept.amount}.`);
@@ -221,7 +224,7 @@ async function verifyAndSettleExactAlgorand(input) {
221
224
  return fail("signature_invalid", "The fee transaction must be a payment (pay) transaction.");
222
225
  }
223
226
  if (!addrEq(fee.sender, railFeePayer)) {
224
- return fail("signature_invalid", `The fee transaction sender ${_optionalChain([fee, 'access', _5 => _5.sender, 'optionalAccess', _6 => _6.toString, 'call', _7 => _7()])} is not the rail fee payer ${railFeePayer}.`);
227
+ return fail("signature_invalid", `The fee transaction sender ${_optionalChain([fee, 'access', _9 => _9.sender, 'optionalAccess', _10 => _10.toString, 'call', _11 => _11()])} is not the rail fee payer ${railFeePayer}.`);
225
228
  }
226
229
  if (BigInt(fee.payment.amount) !== 0n) {
227
230
  return fail("signature_invalid", "The fee transaction must move 0 ALGO (it only pools the group fee).");
@@ -244,7 +247,7 @@ async function verifyAndSettleExactAlgorand(input) {
244
247
  try {
245
248
  feeSigned = fee.signTxn(feePayerSk);
246
249
  } catch (err) {
247
- throw new (0, _chunk3FR22M3Ocjs.SettlementError)(`Algorand exact settle: the gate could not sign the fee transaction (${shorten(err instanceof Error ? err.message : String(err))}).`, { cause: err });
250
+ throw new (0, _chunkQONQSZHJcjs.SettlementError)(`Algorand exact settle: the gate could not sign the fee transaction (${shorten(err instanceof Error ? err.message : String(err))}).`, { cause: err });
248
251
  }
249
252
  const signedGroup = elements.map((el, i) => i === paymentIndex ? payElement.raw : feeSigned);
250
253
  try {
@@ -266,7 +269,7 @@ async function verifyAndSettleExactAlgorand(input) {
266
269
  if (/signature|sig|auth|malformed/i.test(m)) return fail("signature_invalid", `Settle rejected \u2014 bad signature: ${shorten(m)}.`);
267
270
  if (/already in ledger|already committed|duplicate/i.test(m)) return fail("tx_already_used", `This payment was already settled: ${shorten(m)}.`);
268
271
  if (/\bround\b|expired|lifetime|stale|validity/i.test(m)) return fail("payment_expired", `Settle rejected \u2014 validity window expired: ${shorten(m)}.`);
269
- throw new (0, _chunk3FR22M3Ocjs.SettlementError)(
272
+ throw new (0, _chunkQONQSZHJcjs.SettlementError)(
270
273
  `Algorand exact settle: the group failed to submit (${shorten(m)}). The buyer's signed transaction is still valid \u2014 fund/fix the fee payer (${railFeePayer}) and the buyer can re-present it.`,
271
274
  { cause: err }
272
275
  );
@@ -275,10 +278,10 @@ async function verifyAndSettleExactAlgorand(input) {
275
278
  try {
276
279
  round = await client.waitForConfirmation(buyerTxid);
277
280
  } catch (err) {
278
- throw new (0, _chunk3FR22M3Ocjs.SettlementError)(`Algorand exact settle: submitted ${buyerTxid} but confirmation read failed (${shorten(err instanceof Error ? err.message : String(err))}). It likely landed \u2014 re-verify before re-presenting; do NOT re-pay.`, { cause: err });
281
+ throw new (0, _chunkQONQSZHJcjs.SettlementError)(`Algorand exact settle: submitted ${buyerTxid} but confirmation read failed (${shorten(err instanceof Error ? err.message : String(err))}). It likely landed \u2014 re-verify before re-presenting; do NOT re-pay.`, { cause: err });
279
282
  }
280
283
  if (round === null) {
281
- throw new (0, _chunk3FR22M3Ocjs.SettlementError)(`Algorand exact settle: submitted ${buyerTxid} but it did not confirm in time. It likely landed \u2014 re-verify before re-presenting; do NOT re-pay.`);
284
+ throw new (0, _chunkQONQSZHJcjs.SettlementError)(`Algorand exact settle: submitted ${buyerTxid} but it did not confirm in time. It likely landed \u2014 re-verify before re-presenting; do NOT re-pay.`);
282
285
  }
283
286
  return {
284
287
  ok: true,
@@ -374,13 +377,13 @@ function rpcFailed(nonce) {
374
377
 
375
378
  function assertAlgorandWallet(wallet, network) {
376
379
  if (typeof wallet !== "object" || wallet === null) {
377
- throw new (0, _chunk3FR22M3Ocjs.WrongFamilyError)(
380
+ throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
378
381
  `chain ${network} is Algorand; wallet must be { key } (25-word mnemonic) or { account }.`
379
382
  );
380
383
  }
381
- _chunk3FR22M3Ocjs.assertNoLegacyWalletKey.call(void 0, wallet, "Algorand");
384
+ _chunkQONQSZHJcjs.assertNoLegacyWalletKey.call(void 0, wallet, "Algorand");
382
385
  if (!("key" in wallet) && !("account" in wallet)) {
383
- throw new (0, _chunk3FR22M3Ocjs.WrongFamilyError)(
386
+ throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
384
387
  `chain ${network} is Algorand; wallet must be { key } (25-word mnemonic) or { account }.`
385
388
  );
386
389
  }
@@ -395,13 +398,13 @@ function resolveAlgorandWallet(config) {
395
398
  const { addr, sk } = _algosdk2.default.mnemonicToSecretKey(config.key);
396
399
  return { addr: addr.toString(), sk };
397
400
  } catch (cause) {
398
- throw new (0, _chunk3FR22M3Ocjs.WrongFamilyError)(
401
+ throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
399
402
  "Algorand wallet { key } is not a valid 25-word Algorand mnemonic.",
400
403
  { cause }
401
404
  );
402
405
  }
403
406
  }
404
- throw new (0, _chunk3FR22M3Ocjs.WrongFamilyError)("Algorand wallet needs { key } (25-word mnemonic) or { account }.");
407
+ throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)("Algorand wallet needs { key } (25-word mnemonic) or { account }.");
405
408
  }
406
409
 
407
410
  // src/drivers/algorand/index.ts
@@ -447,12 +450,12 @@ function makeAlgorandNetwork(preset, algodUrl) {
447
450
  const settleClient = {
448
451
  async simulate(signedGroup) {
449
452
  const resp = await algod.simulateRawTransactions(signedGroup).do();
450
- const fm = _optionalChain([resp, 'optionalAccess', _8 => _8.txnGroups, 'optionalAccess', _9 => _9[0], 'optionalAccess', _10 => _10.failureMessage]);
453
+ const fm = _optionalChain([resp, 'optionalAccess', _12 => _12.txnGroups, 'optionalAccess', _13 => _13[0], 'optionalAccess', _14 => _14.failureMessage]);
451
454
  return { failureMessage: fm && fm.length > 0 ? fm : null };
452
455
  },
453
456
  async submit(signedGroup) {
454
457
  const res = await algod.sendRawTransaction(signedGroup).do();
455
- return _nullishCoalesce(_optionalChain([res, 'optionalAccess', _11 => _11.txid]), () => ( ""));
458
+ return _nullishCoalesce(_optionalChain([res, 'optionalAccess', _15 => _15.txid]), () => ( ""));
456
459
  },
457
460
  async waitForConfirmation(txid) {
458
461
  const info = await _algosdk2.default.waitForConfirmation(algod, txid, 10);
@@ -463,7 +466,13 @@ function makeAlgorandNetwork(preset, algodUrl) {
463
466
  return {
464
467
  family: "algorand",
465
468
  network,
466
- supports: (n) => n === network,
469
+ // Accept our padded 44-char genesis-hash id AND the spec's 32-char truncated form
470
+ // (`scheme_exact_algo.md`'s Network Identifiers table — the form 874 of the 1,463 live
471
+ // Algorand rails use), so a caller that bypasses `normalizeNetwork` and hands the raw
472
+ // spec id to the driver still matches. Mirrors TON's legacy-id tolerance; the
473
+ // normalizeNetwork alias remains the canonical seam. We still EMIT the padded form —
474
+ // changing that would break every deployed PipRail 2.x buyer, so it waits for a major.
475
+ supports: (n) => n === network || n === _chunkV2IJ5HUWcjs.ALGORAND_SPEC_CAIP2,
467
476
  resolveToken(token) {
468
477
  if (token === "native") {
469
478
  return { asset: "native", decimals: ALGO_DECIMALS, symbol: ALGO_SYMBOL };
@@ -472,16 +481,16 @@ function makeAlgorandNetwork(preset, algodUrl) {
472
481
  const info = preset.tokens[token.toUpperCase()];
473
482
  if (!info) {
474
483
  const known = Object.keys(preset.tokens).join(", ") || "(none built in)";
475
- throw new (0, _chunk3FR22M3Ocjs.UnknownTokenError)(
484
+ throw new (0, _chunkQONQSZHJcjs.UnknownTokenError)(
476
485
  `token "${token}" isn't built in for Algorand (known: ${known}). Pass { assetId, decimals } for a custom ASA, or use 'native'.`
477
486
  );
478
487
  }
479
488
  return { asset: algorandAssetId(info.assetId), decimals: info.decimals, symbol: info.symbol };
480
489
  }
481
- _chunk3FR22M3Ocjs.rejectForeignToken.call(void 0, token, "algorand", network);
490
+ _chunkQONQSZHJcjs.rejectForeignToken.call(void 0, token, "algorand", network);
482
491
  const t = token;
483
492
  if (typeof t.assetId !== "number" || typeof t.decimals !== "number") {
484
- throw new (0, _chunk3FR22M3Ocjs.WrongFamilyError)(
493
+ throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
485
494
  `chain ${network} is Algorand; a custom token must be { assetId, decimals }.`
486
495
  );
487
496
  }
@@ -502,12 +511,12 @@ function makeAlgorandNetwork(preset, algodUrl) {
502
511
  },
503
512
  assertValidPayTo(payTo) {
504
513
  if (payTo.startsWith("0x")) {
505
- throw new (0, _chunk3FR22M3Ocjs.WrongFamilyError)(
514
+ throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
506
515
  `chain ${network} is Algorand, but payTo "${payTo}" looks like an EVM address.`
507
516
  );
508
517
  }
509
518
  if (!_algosdk2.default.isValidAddress(payTo)) {
510
- throw new (0, _chunk3FR22M3Ocjs.WrongFamilyError)(
519
+ throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
511
520
  `chain ${network} is Algorand, but payTo "${payTo}" is not a valid Algorand address.`
512
521
  );
513
522
  }
@@ -524,14 +533,14 @@ function makeAlgorandNetwork(preset, algodUrl) {
524
533
  const info = await _algosdk2.default.waitForConfirmation(algod, ref, 10);
525
534
  return { height: String(_nullishCoalesce(info.confirmedRound, () => ( 0))) };
526
535
  } catch (err) {
527
- throw new (0, _chunk3FR22M3Ocjs.ConfirmationTimeoutError)(`Algorand tx ${ref} did not confirm in time.`, {
536
+ throw new (0, _chunkQONQSZHJcjs.ConfirmationTimeoutError)(`Algorand tx ${ref} did not confirm in time.`, {
528
537
  cause: err
529
538
  });
530
539
  }
531
540
  },
532
541
  async estimateCost(accept) {
533
542
  if (accept.scheme === "exact") {
534
- return _chunk3FR22M3Ocjs.nativeCost.call(void 0, {
543
+ return _chunkQONQSZHJcjs.nativeCost.call(void 0, {
535
544
  symbol: ALGO_SYMBOL,
536
545
  decimals: ALGO_DECIMALS,
537
546
  fee: 0n,
@@ -539,7 +548,7 @@ function makeAlgorandNetwork(preset, algodUrl) {
539
548
  detail: "gasless \u2014 the fee payer (facilitator/relayer) pools the group fee; the buyer pays 0 ALGO"
540
549
  });
541
550
  }
542
- return _chunk3FR22M3Ocjs.nativeCost.call(void 0, {
551
+ return _chunkQONQSZHJcjs.nativeCost.call(void 0, {
543
552
  symbol: ALGO_SYMBOL,
544
553
  decimals: ALGO_DECIMALS,
545
554
  fee: 1000n,
@@ -575,7 +584,7 @@ function makeAlgorandNetwork(preset, algodUrl) {
575
584
  const optedIn = (_nullishCoalesce(info.assets, () => ( []))).some((a) => Number(a.assetId) === assetId);
576
585
  return optedIn ? { ready: true } : { ready: false, reason: "NOT_OPTED_IN" };
577
586
  } catch (e) {
578
- if (/does not exist|no accounts found|404|account not found/i.test(String(_nullishCoalesce(_optionalChain([e, 'optionalAccess', _12 => _12.message]), () => ( e))))) {
587
+ if (/does not exist|no accounts found|404|account not found/i.test(String(_nullishCoalesce(_optionalChain([e, 'optionalAccess', _16 => _16.message]), () => ( e))))) {
579
588
  return { ready: false, reason: "NOT_OPTED_IN" };
580
589
  }
581
590
  return { ready: "unknown" };