@piprail/sdk 2.2.0 → 2.4.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 +130 -1
- package/dist/algorand-TMA62DN2.cjs +678 -0
- package/dist/algorand-WB6PBJU4.js +678 -0
- package/dist/aptos-JQMZNTMD.cjs +673 -0
- package/dist/aptos-QAAXIUY3.js +673 -0
- package/dist/index.cjs +262 -86
- package/dist/index.d.cts +88 -26
- package/dist/index.d.ts +88 -26
- package/dist/index.js +255 -79
- package/dist/{solana-E4MD6JJ6.js → solana-3FMCWSEE.js} +20 -0
- package/dist/{solana-TLHL2KNY.cjs → solana-M3VOHCMO.cjs} +20 -0
- package/package.json +1 -1
- package/dist/algorand-GSFVZTBF.js +0 -389
- package/dist/algorand-HZS43N4P.cjs +0 -389
- package/dist/aptos-RIL56C7L.js +0 -352
- package/dist/aptos-TRCCJRZA.cjs +0 -352
package/dist/index.js
CHANGED
|
@@ -1469,9 +1469,24 @@ function parseExactPaymentHeader(value) {
|
|
|
1469
1469
|
const x402Version = typeof v.x402Version === "number" ? v.x402Version : 2;
|
|
1470
1470
|
const asset = accepted && typeof accepted.asset === "string" ? accepted.asset : void 0;
|
|
1471
1471
|
const base2 = { x402Version, network, ...asset ? { asset } : {}, raw: v };
|
|
1472
|
+
if (typeof payload.transaction === "string" && typeof payload.senderAuth === "string") {
|
|
1473
|
+
return { ...base2, method: "aptos", payload: { transaction: payload.transaction, senderAuth: payload.senderAuth } };
|
|
1474
|
+
}
|
|
1472
1475
|
if (typeof payload.transaction === "string") {
|
|
1473
1476
|
return { ...base2, method: "svm", payload: { transaction: payload.transaction } };
|
|
1474
1477
|
}
|
|
1478
|
+
if (Array.isArray(payload.paymentGroup)) {
|
|
1479
|
+
const group = payload.paymentGroup;
|
|
1480
|
+
const index = payload.paymentIndex;
|
|
1481
|
+
if (group.length === 0 || group.length > 16 || !group.every((t) => typeof t === "string") || typeof index !== "number" || !Number.isInteger(index) || index < 0 || index >= group.length) {
|
|
1482
|
+
return null;
|
|
1483
|
+
}
|
|
1484
|
+
return {
|
|
1485
|
+
...base2,
|
|
1486
|
+
method: "algorand",
|
|
1487
|
+
payload: { paymentIndex: index, paymentGroup: group }
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1475
1490
|
const signature = payload.signature;
|
|
1476
1491
|
if (typeof signature !== "string") return null;
|
|
1477
1492
|
const authorization = payload.authorization;
|
|
@@ -1792,6 +1807,9 @@ function makeEvmNetwork(resolved) {
|
|
|
1792
1807
|
if ("transaction" in payload) {
|
|
1793
1808
|
return { ok: false, error: "signature_invalid", detail: "An SVM (Solana) payload was submitted to an EVM exact rail." };
|
|
1794
1809
|
}
|
|
1810
|
+
if ("paymentGroup" in payload) {
|
|
1811
|
+
return { ok: false, error: "signature_invalid", detail: "An Algorand payload was submitted to an EVM exact rail." };
|
|
1812
|
+
}
|
|
1795
1813
|
return verifyAndSettleExactEvm({
|
|
1796
1814
|
publicClient,
|
|
1797
1815
|
walletClient: a.walletClient,
|
|
@@ -1816,7 +1834,7 @@ var loaders = {
|
|
|
1816
1834
|
solana: async () => {
|
|
1817
1835
|
let mod;
|
|
1818
1836
|
try {
|
|
1819
|
-
mod = await import("./solana-
|
|
1837
|
+
mod = await import("./solana-3FMCWSEE.js");
|
|
1820
1838
|
} catch (cause) {
|
|
1821
1839
|
throw new MissingDriverError(
|
|
1822
1840
|
`Solana selected, but its packages aren't installed. Run: npm install @solana/web3.js @solana/spl-token bs58`,
|
|
@@ -1900,7 +1918,7 @@ var loaders = {
|
|
|
1900
1918
|
aptos: async () => {
|
|
1901
1919
|
let mod;
|
|
1902
1920
|
try {
|
|
1903
|
-
mod = await import("./aptos-
|
|
1921
|
+
mod = await import("./aptos-QAAXIUY3.js");
|
|
1904
1922
|
} catch (cause) {
|
|
1905
1923
|
throw new MissingDriverError(
|
|
1906
1924
|
`Aptos selected, but its package isn't installed. Run: npm install @aptos-labs/ts-sdk`,
|
|
@@ -1912,7 +1930,7 @@ var loaders = {
|
|
|
1912
1930
|
algorand: async () => {
|
|
1913
1931
|
let mod;
|
|
1914
1932
|
try {
|
|
1915
|
-
mod = await import("./algorand-
|
|
1933
|
+
mod = await import("./algorand-WB6PBJU4.js");
|
|
1916
1934
|
} catch (cause) {
|
|
1917
1935
|
throw new MissingDriverError(
|
|
1918
1936
|
`Algorand selected, but its package isn't installed. Run: npm install algosdk`,
|
|
@@ -2008,7 +2026,7 @@ var SLUG_TO_CAIP2 = {
|
|
|
2008
2026
|
near: "near:mainnet",
|
|
2009
2027
|
sui: "sui:mainnet",
|
|
2010
2028
|
aptos: "aptos:1",
|
|
2011
|
-
algorand: "algorand:
|
|
2029
|
+
algorand: "algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=",
|
|
2012
2030
|
stellar: "stellar:pubnet",
|
|
2013
2031
|
xrpl: "xrpl:0"
|
|
2014
2032
|
};
|
|
@@ -3026,7 +3044,7 @@ var PipRailClient = class {
|
|
|
3026
3044
|
* before publishing, so retry with a brief backoff if a fresh listing is missing.
|
|
3027
3045
|
* - Results are cross-scheme (mostly the mainstream `exact` scheme); `fetch()` pays
|
|
3028
3046
|
* `onchain-proof` rails by default, and standard `exact` rails too once you opt in
|
|
3029
|
-
* with `schemes: ['onchain-proof', 'exact']` (EVM EIP-3009/Permit2 + Solana SVM).
|
|
3047
|
+
* with `schemes: ['onchain-proof', 'exact']` (EVM EIP-3009/Permit2 + Solana SVM + Algorand).
|
|
3030
3048
|
*/
|
|
3031
3049
|
async discover(opts = {}) {
|
|
3032
3050
|
const found = await searchOpenIndexes({
|
|
@@ -3224,7 +3242,7 @@ var PipRailClient = class {
|
|
|
3224
3242
|
);
|
|
3225
3243
|
if (schemes.includes("exact") && exactOnNet && typeof net.payExact !== "function") {
|
|
3226
3244
|
throw new UnsupportedSchemeError(
|
|
3227
|
-
`This 402 offers a standard 'exact' rail on ${net.network}, but the ${net.family} family can't pay 'exact' (supported on EVM +
|
|
3245
|
+
`This 402 offers a standard 'exact' rail on ${net.network}, but the ${net.family} family can't pay 'exact' (supported on EVM, Solana + Algorand today), and no 'onchain-proof' rail was offered.`
|
|
3228
3246
|
);
|
|
3229
3247
|
}
|
|
3230
3248
|
if (!schemes.includes("exact") && exactOnNet && typeof net.payExact === "function") {
|
|
@@ -3620,7 +3638,7 @@ var PipRailClient = class {
|
|
|
3620
3638
|
async payExactRail(net, wallet, accept, url, init, quote) {
|
|
3621
3639
|
if (!net.payExact) {
|
|
3622
3640
|
throw new UnsupportedSchemeError(
|
|
3623
|
-
`the ${net.family} family can't pay a standard 'exact' rail (supported on EVM +
|
|
3641
|
+
`the ${net.family} family can't pay a standard 'exact' rail (supported on EVM, Solana + Algorand today).`
|
|
3624
3642
|
);
|
|
3625
3643
|
}
|
|
3626
3644
|
throwIfAborted(init?.signal);
|
|
@@ -4189,7 +4207,8 @@ A 402 may offer up to two rails; you don't choose per payment \u2014 the client
|
|
|
4189
4207
|
(the native coin \u2014 ETH/SOL/\u2026). Works on every chain.
|
|
4190
4208
|
- exact (the ratified x402 rail, opt-in): you only SIGN; the server \u2014 or a facilitator it chose
|
|
4191
4209
|
(e.g. PayAI) \u2014 broadcasts it, so you pay ZERO gas (you need only the token, no native coin). It
|
|
4192
|
-
works on EVM +
|
|
4210
|
+
works on EVM, Solana + Algorand, and the on-chain method (EIP-3009 / Permit2 / SVM / Algorand
|
|
4211
|
+
fee-pooled group) is picked automatically.
|
|
4193
4212
|
When the exact scheme is enabled AND balance-aware routing is on, paying picks the cheapest
|
|
4194
4213
|
settleable rail \u2014 i.e. the gasless exact one. Nothing changes in your loop: quote \u2192 plan \u2192 pay is
|
|
4195
4214
|
identical. The exact scheme is OPT-IN by the operator (MCP: PIPRAIL_SCHEMES=onchain-proof,exact);
|
|
@@ -4891,6 +4910,167 @@ async function settleViaFacilitator(input) {
|
|
|
4891
4910
|
return { ok: true, receipt };
|
|
4892
4911
|
}
|
|
4893
4912
|
|
|
4913
|
+
// src/facilitators.ts
|
|
4914
|
+
var KNOWN_FACILITATORS = {
|
|
4915
|
+
// Base (eip155:8453). Every entry is keyless and LIVE-settled by us (a real EIP-3009
|
|
4916
|
+
// exact payment, buyer paid zero ETH) — not just a /supported read — on the dated day.
|
|
4917
|
+
"eip155:8453": [
|
|
4918
|
+
{
|
|
4919
|
+
url: "https://facilitator.payai.network",
|
|
4920
|
+
keyless: true,
|
|
4921
|
+
schemes: ["exact"],
|
|
4922
|
+
settles: ["eip3009"],
|
|
4923
|
+
note: "PayAI \u2014 keyless, sponsors gas (Base USDC EIP-3009). Verified 2026-06-14 (/supported + live demo)."
|
|
4924
|
+
},
|
|
4925
|
+
{
|
|
4926
|
+
url: "https://facilitator.xpay.sh",
|
|
4927
|
+
keyless: true,
|
|
4928
|
+
schemes: ["exact"],
|
|
4929
|
+
settles: ["eip3009"],
|
|
4930
|
+
note: "xpay \u2014 keyless, zero-fee, sponsors gas. LIVE-settled on Base 2026-06-15 (tx 0x2273d5\u2026)."
|
|
4931
|
+
},
|
|
4932
|
+
{
|
|
4933
|
+
url: "https://facilitator.ultravioletadao.xyz",
|
|
4934
|
+
keyless: true,
|
|
4935
|
+
schemes: ["exact"],
|
|
4936
|
+
settles: ["eip3009"],
|
|
4937
|
+
note: "Ultravioleta DAO \u2014 keyless, 100% gas-sponsored (Base USDC EIP-3009). LIVE-settled on Base 2026-06-17 (tx 0x58a69042a4129be649c5642456752ede95a5ff921daddfd30560ae70ae5907ff). 2nd UVD validation chain (after HyperEVM)."
|
|
4938
|
+
},
|
|
4939
|
+
{
|
|
4940
|
+
url: "https://x402.dexter.cash",
|
|
4941
|
+
keyless: true,
|
|
4942
|
+
schemes: ["exact"],
|
|
4943
|
+
settles: ["eip3009"],
|
|
4944
|
+
note: "Dexter \u2014 keyless, gas-sponsored (Base USDC EIP-3009). LIVE-settled on Base 2026-06-17 (tx 0xdf030e4d5bf41a88c5a3bfe73bb433dfbb90b058d3b8ded63b68e026eedb9de8). Note: ~$0.001 dynamic floor on Base."
|
|
4945
|
+
},
|
|
4946
|
+
{
|
|
4947
|
+
url: "https://facilitator.corbits.dev",
|
|
4948
|
+
keyless: true,
|
|
4949
|
+
schemes: ["exact"],
|
|
4950
|
+
settles: ["eip3009"],
|
|
4951
|
+
note: "Corbits (Faremeter) \u2014 keyless, sponsors gas (Base USDC EIP-3009). LIVE-settled on Base 2026-06-17 (tx 0x2e41c581af023100429ada81f1371bef82d41dd577ed438d72ddc18676d4213d). Also seeded on Monad + Solana."
|
|
4952
|
+
},
|
|
4953
|
+
{
|
|
4954
|
+
url: "https://facilitator.goplausible.xyz",
|
|
4955
|
+
keyless: true,
|
|
4956
|
+
schemes: ["exact"],
|
|
4957
|
+
settles: ["eip3009"],
|
|
4958
|
+
note: "GoPlausible \u2014 keyless, sponsors gas (Base USDC EIP-3009). LIVE-settled on Base 2026-06-17 (tx 0x9bcbc1f01fe1fd1aed2a79e5582555164cc4185e9800189df378a6b46eb9c59e). 2nd GoPlausible validation chain (after Algorand)."
|
|
4959
|
+
}
|
|
4960
|
+
],
|
|
4961
|
+
// Monad (eip155:143). Corbits + Ultravioleta DAO each keyless-settle the EVM EIP-3009 exact rail
|
|
4962
|
+
// (Monad's native Circle USDC), buyer paid zero gas — real LIVE settles, not just /supported reads.
|
|
4963
|
+
// Makes `exact: true` zero-config gasless on Monad.
|
|
4964
|
+
"eip155:143": [
|
|
4965
|
+
{
|
|
4966
|
+
url: "https://facilitator.corbits.dev",
|
|
4967
|
+
keyless: true,
|
|
4968
|
+
schemes: ["exact"],
|
|
4969
|
+
settles: ["eip3009"],
|
|
4970
|
+
note: "Corbits (Faremeter) \u2014 keyless, sponsors gas (Monad native USDC EIP-3009). LIVE-settled on Monad 2026-06-17 (tx 0x7797be27ce22c17f7433a0389bd22d46e338899b1faac505fffd068174428ae6)."
|
|
4971
|
+
},
|
|
4972
|
+
{
|
|
4973
|
+
url: "https://facilitator.ultravioletadao.xyz",
|
|
4974
|
+
keyless: true,
|
|
4975
|
+
schemes: ["exact"],
|
|
4976
|
+
settles: ["eip3009"],
|
|
4977
|
+
note: "Ultravioleta DAO \u2014 keyless, 100% gas-sponsored (Monad native USDC EIP-3009). LIVE-settled on Monad 2026-06-17 (tx 0xb107576effbaceb5586c07a9ddc996ef2b6d455f1858314995ef83a7c6e64d11). 3rd UVD validation chain."
|
|
4978
|
+
},
|
|
4979
|
+
{
|
|
4980
|
+
url: "https://facilitator.pieverse.io",
|
|
4981
|
+
keyless: true,
|
|
4982
|
+
schemes: ["exact"],
|
|
4983
|
+
settles: ["eip3009"],
|
|
4984
|
+
note: "Pieverse \u2014 keyless, sponsors gas (Monad USDC EIP-3009). LIVE-settled on Monad 2026-06-17 (tx 0x00cfeb93876e5ef57dcb002510038b7304913233ca286d7ab33c72a8b119eb0d)."
|
|
4985
|
+
}
|
|
4986
|
+
],
|
|
4987
|
+
// BNB Chain (eip155:56). Dexter keyless-settles the EVM EIP-3009 exact rail — but ONLY for BNB's
|
|
4988
|
+
// EIP-3009 tokens (FDUSD / USD1); BNB's USDC/USDT are Binance-Peg (Permit2), which no facilitator
|
|
4989
|
+
// settles. Dexter also enforces a ~$0.003 dynamic settlement floor on BNB, so a sub-$0.003 payment
|
|
4990
|
+
// is rejected (amount_too_low) — fine for real prices, but the floor is real. LIVE-settled with
|
|
4991
|
+
// FDUSD, buyer paid zero BNB — this beats the BNB token-overlap wall that blocked AEON/Pieverse.
|
|
4992
|
+
"eip155:56": [
|
|
4993
|
+
{
|
|
4994
|
+
url: "https://x402.dexter.cash",
|
|
4995
|
+
keyless: true,
|
|
4996
|
+
schemes: ["exact"],
|
|
4997
|
+
settles: ["eip3009"],
|
|
4998
|
+
note: "Dexter \u2014 keyless, gas-sponsored. BNB EIP-3009 tokens FDUSD/USD1 ONLY (Binance-Peg USDC/USDT are Permit2 \u2192 not facilitator-settleable); ~$0.003 dynamic floor. LIVE-settled on BNB 2026-06-17 with FDUSD (tx 0x6d9eb4e4939f3f3c74cb19424cc7822d66ec8ed5c5c7c330d9f88a5f9ad59e9e)."
|
|
4999
|
+
},
|
|
5000
|
+
{
|
|
5001
|
+
url: "https://facilitator.pieverse.io",
|
|
5002
|
+
keyless: true,
|
|
5003
|
+
schemes: ["exact"],
|
|
5004
|
+
settles: ["eip3009"],
|
|
5005
|
+
note: "Pieverse \u2014 keyless, sponsors gas. BNB EIP-3009 tokens FDUSD/USD1 (same Binance-Peg caveat as Dexter). LIVE-settled on BNB 2026-06-17 with FDUSD (tx 0xb9c76affc45bd07a51559efd813ca71516fc30625478724476c2cf42fc2203d3) \u2014 a 2nd keyless BNB facilitator (failover for Dexter)."
|
|
5006
|
+
}
|
|
5007
|
+
],
|
|
5008
|
+
// HyperEVM (eip155:999). Ultravioleta DAO keyless-settles the EVM EIP-3009 exact rail (HyperEVM's
|
|
5009
|
+
// native Circle USDC), buyer paid zero gas — a real LIVE settle. UVD is the broadest keyless
|
|
5010
|
+
// facilitator (it also lists Celo/Unichain/Optimism/Scroll + many non-EVM); only HyperEVM is
|
|
5011
|
+
// seeded here because THE RULE requires a per-chain live settle, and that's the one we proved.
|
|
5012
|
+
"eip155:999": [
|
|
5013
|
+
{
|
|
5014
|
+
url: "https://facilitator.ultravioletadao.xyz",
|
|
5015
|
+
keyless: true,
|
|
5016
|
+
schemes: ["exact"],
|
|
5017
|
+
settles: ["eip3009"],
|
|
5018
|
+
note: "Ultravioleta DAO \u2014 keyless, 100% gas-sponsored (HyperEVM native USDC EIP-3009). LIVE-settled on HyperEVM 2026-06-17 (tx 0x56af8148a92a291f0ce362e250919f7742074e5464ac0f315ad68abaec93bd0a)."
|
|
5019
|
+
}
|
|
5020
|
+
],
|
|
5021
|
+
// Algorand (mainnet, CAIP-2 = full base64 genesis hash). GoPlausible keyless-settles the ratified
|
|
5022
|
+
// x402 Algorand `exact` rail (atomic-group fee pooling): its sponsor pools the whole group fee and
|
|
5023
|
+
// submits, so NEITHER the buyer NOR the merchant pays ALGO — both-sides gasless. LIVE-settled by us
|
|
5024
|
+
// (a real USDCa exact payment; buyer 0 ALGO AND merchant 0 ALGO) — beyond a /supported read. This
|
|
5025
|
+
// makes Algorand a keyless chain (the first non-EVM/non-Solana one). GoPlausible authored the
|
|
5026
|
+
// ratified Algorand scheme; PipRail's group is byte-compatible (the gate sends `amount` + the full
|
|
5027
|
+
// genesis-hash network, which is all GoPlausible needs).
|
|
5028
|
+
"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=": [
|
|
5029
|
+
{
|
|
5030
|
+
url: "https://facilitator.goplausible.xyz",
|
|
5031
|
+
keyless: true,
|
|
5032
|
+
schemes: ["exact"],
|
|
5033
|
+
settles: ["algorand"],
|
|
5034
|
+
note: "GoPlausible \u2014 keyless, 100% gas-sponsored (Algorand USDCa, atomic-group fee pooling; both buyer AND merchant pay 0 ALGO). LIVE-settled on Algorand mainnet 2026-06-17 (tx PDVDVRFGJAG2K6AJ7L26OTSCSRL7AURVKEX4D4KHBAOLNSCYENXA). The only keyless Algorand x402 facilitator."
|
|
5035
|
+
}
|
|
5036
|
+
],
|
|
5037
|
+
// Solana (mainnet-beta). Keyless fee-payer sponsors for the SVM exact rail, each LIVE-settled
|
|
5038
|
+
// by us (a real SPL TransferChecked, buyer paid zero SOL) on the dated day — beyond a /supported
|
|
5039
|
+
// read. Daydreams + Questflow are intentionally ABSENT: their /supported is public but /verify
|
|
5040
|
+
// returns 401 (an API key is required), so they are not keyless for settlement.
|
|
5041
|
+
"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": [
|
|
5042
|
+
{
|
|
5043
|
+
url: "https://facilitator.payai.network",
|
|
5044
|
+
keyless: true,
|
|
5045
|
+
schemes: ["exact"],
|
|
5046
|
+
settles: ["svm"],
|
|
5047
|
+
note: "PayAI \u2014 keyless fee-payer sponsor (Solana SPL SVM). LIVE-settled 2026-06-14 (tx 4dL8jRKH\u2026)."
|
|
5048
|
+
},
|
|
5049
|
+
{
|
|
5050
|
+
url: "https://pay.openfacilitator.io",
|
|
5051
|
+
keyless: true,
|
|
5052
|
+
schemes: ["exact"],
|
|
5053
|
+
settles: ["svm"],
|
|
5054
|
+
note: "OpenFacilitator \u2014 keyless (no signup), fee-payer sponsor. LIVE-settled on Solana 2026-06-15 (tx 5BabDtX\u2026)."
|
|
5055
|
+
},
|
|
5056
|
+
{
|
|
5057
|
+
url: "https://facilitator.corbits.dev",
|
|
5058
|
+
keyless: true,
|
|
5059
|
+
schemes: ["exact"],
|
|
5060
|
+
settles: ["svm"],
|
|
5061
|
+
note: "Corbits \u2014 keyless, Solana-first fee-payer sponsor. LIVE-settled on Solana 2026-06-15 (tx BCreYer\u2026)."
|
|
5062
|
+
}
|
|
5063
|
+
]
|
|
5064
|
+
};
|
|
5065
|
+
function knownFacilitatorsFor(network) {
|
|
5066
|
+
return KNOWN_FACILITATORS[network] ?? [];
|
|
5067
|
+
}
|
|
5068
|
+
function firstKeylessFacilitator(network, method) {
|
|
5069
|
+
return knownFacilitatorsFor(network).find(
|
|
5070
|
+
(f) => f.keyless && f.schemes.includes("exact") && (method === void 0 || f.settles.includes(method))
|
|
5071
|
+
);
|
|
5072
|
+
}
|
|
5073
|
+
|
|
4894
5074
|
// src/server.ts
|
|
4895
5075
|
function toInvalidBody(result) {
|
|
4896
5076
|
return { x402Version: 2, status: "invalid", error: result.error, detail: result.detail };
|
|
@@ -4911,10 +5091,16 @@ function normaliseAccepts(options) {
|
|
|
4911
5091
|
"requirePayment: provide either { chain, token, amount } or a non-empty `accept: [{ chain, token, amount }, \u2026]`."
|
|
4912
5092
|
);
|
|
4913
5093
|
}
|
|
5094
|
+
function normaliseExactOption(exact) {
|
|
5095
|
+
if (!exact) return void 0;
|
|
5096
|
+
if (exact === true) return { settle: "keyless" };
|
|
5097
|
+
return exact;
|
|
5098
|
+
}
|
|
4914
5099
|
function createPaymentGate(options) {
|
|
4915
5100
|
const minConfirmations = options.minConfirmations ?? 1;
|
|
4916
5101
|
const maxTimeoutSeconds = options.maxTimeoutSeconds ?? 600;
|
|
4917
5102
|
const genNonce = options.generateNonce ?? (() => globalThis.crypto.randomUUID());
|
|
5103
|
+
const exactOption = normaliseExactOption(options.exact);
|
|
4918
5104
|
let resolved;
|
|
4919
5105
|
function ready() {
|
|
4920
5106
|
if (resolved) return resolved;
|
|
@@ -4934,7 +5120,7 @@ function createPaymentGate(options) {
|
|
|
4934
5120
|
const { asset, decimals, symbol } = net.resolveToken(a.token);
|
|
4935
5121
|
const amountBase = parseUnits(a.amount, decimals);
|
|
4936
5122
|
const spec = { net, asset, decimals, symbol, amountBase, amountFormatted: a.amount, payTo };
|
|
4937
|
-
if (
|
|
5123
|
+
if (exactOption) {
|
|
4938
5124
|
const outcome = await resolveExactRail(net, asset);
|
|
4939
5125
|
if (outcome.rail) spec.exact = outcome.rail;
|
|
4940
5126
|
else if (outcome.skipReason) exactSkips.push(outcome.skipReason);
|
|
@@ -4942,10 +5128,17 @@ function createPaymentGate(options) {
|
|
|
4942
5128
|
return spec;
|
|
4943
5129
|
})
|
|
4944
5130
|
);
|
|
4945
|
-
if (
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
5131
|
+
if (exactOption && !specs.some((s) => s.exact)) {
|
|
5132
|
+
const why = exactSkips.length > 0 ? exactSkips.join(" ") : "The standard `exact` rail is EVM ERC-20 (EIP-3009 \u2014 USDC / EURC \u2014 or Permit2, e.g. Binance-Peg USDC on BNB) or a Solana SPL token (SVM) \u2014 NOT native coins, NOT families without a standard `exact` scheme.";
|
|
5133
|
+
if (exactOption.settle === "keyless") {
|
|
5134
|
+
if (typeof process === "undefined" || !process?.env?.PIPRAIL_NO_HINTS) {
|
|
5135
|
+
console.warn(
|
|
5136
|
+
`[piprail] exact: true \u2014 no offered chain has a gasless \`exact\` rail available, so this gate serves ONCHAIN-PROOF ONLY (buyers PAY GAS \u2014 the fallback when no facilitator can sponsor). ${why} To be gasless: pin \`exact: { settle: { facilitator } }\` or self-settle \`exact: { settle: 'self', relayer }\`. (Suppress with PIPRAIL_NO_HINTS=1.)`
|
|
5137
|
+
);
|
|
5138
|
+
}
|
|
5139
|
+
} else {
|
|
5140
|
+
throw new Error("requirePayment: `exact` was requested but none of the offered rails support it. " + why);
|
|
5141
|
+
}
|
|
4949
5142
|
}
|
|
4950
5143
|
return specs;
|
|
4951
5144
|
})();
|
|
@@ -4956,9 +5149,23 @@ function createPaymentGate(options) {
|
|
|
4956
5149
|
return p;
|
|
4957
5150
|
}
|
|
4958
5151
|
async function resolveExactRail(net, asset) {
|
|
4959
|
-
const cfg =
|
|
4960
|
-
const settle = cfg.settle;
|
|
5152
|
+
const cfg = exactOption;
|
|
4961
5153
|
if (!net.resolveExactRail) return {};
|
|
5154
|
+
let settle = cfg.settle;
|
|
5155
|
+
if (settle === "keyless") {
|
|
5156
|
+
const picked = firstKeylessFacilitator(net.network);
|
|
5157
|
+
if (!picked) {
|
|
5158
|
+
return {
|
|
5159
|
+
skipReason: `${net.network}: \`exact: true\` found no known keyless facilitator for this network. Pass \`exact: { settle: { facilitator } }\`, \`exact: { settle: 'self', relayer }\`, or see the coverage map (KNOWN_FACILITATORS / docs.piprail.com).`
|
|
5160
|
+
};
|
|
5161
|
+
}
|
|
5162
|
+
if (typeof process === "undefined" || process?.env?.NODE_ENV !== "production" && !process?.env?.PIPRAIL_NO_HINTS) {
|
|
5163
|
+
console.warn(
|
|
5164
|
+
`[piprail] exact: keyless rail on ${net.network} auto-settles via ${picked.url} (zero-config; pin \`exact.settle.facilitator\` in production).`
|
|
5165
|
+
);
|
|
5166
|
+
}
|
|
5167
|
+
settle = { facilitator: picked.url };
|
|
5168
|
+
}
|
|
4962
5169
|
let relayer;
|
|
4963
5170
|
let feePayer;
|
|
4964
5171
|
if (settle === "self") {
|
|
@@ -4986,7 +5193,7 @@ function createPaymentGate(options) {
|
|
|
4986
5193
|
if (settle !== "self" && info.method === "permit2") {
|
|
4987
5194
|
if (cfg.method === "permit2") {
|
|
4988
5195
|
throw new Error(
|
|
4989
|
-
"requirePayment: exact `method: 'permit2'` can't be settled by a third-party facilitator \u2014 facilitators settle the standard EIP-3009 (EVM) / SVM (Solana) schemes, not PipRail\u2019s Permit2 proxy. Use an EIP-3009 token (USDC / EURC) with the facilitator, or `settle: 'self'` (your own relayer) to settle Permit2 yourself."
|
|
5196
|
+
"requirePayment: exact `method: 'permit2'` can't be settled by a third-party facilitator \u2014 facilitators settle the standard EIP-3009 (EVM) / SVM (Solana) / Algorand schemes, not PipRail\u2019s Permit2 proxy. Use an EIP-3009 token (USDC / EURC) with the facilitator, or `settle: 'self'` (your own relayer) to settle Permit2 yourself."
|
|
4990
5197
|
);
|
|
4991
5198
|
}
|
|
4992
5199
|
return {
|
|
@@ -5231,7 +5438,7 @@ function createPaymentGate(options) {
|
|
|
5231
5438
|
if (exactSpecs.length === 0) {
|
|
5232
5439
|
return rejection("transfer_not_found", "This resource offers no standard `exact` rail.");
|
|
5233
5440
|
}
|
|
5234
|
-
const isCaip = exact.network.
|
|
5441
|
+
const isCaip = exact.network.includes(":");
|
|
5235
5442
|
let candidates = isCaip ? exactSpecs.filter((s) => s.net.network === exact.network) : exactSpecs;
|
|
5236
5443
|
if (exact.asset) {
|
|
5237
5444
|
candidates = candidates.filter((s) => s.asset.toLowerCase() === exact.asset.toLowerCase());
|
|
@@ -5247,12 +5454,28 @@ function createPaymentGate(options) {
|
|
|
5247
5454
|
}
|
|
5248
5455
|
let nonce;
|
|
5249
5456
|
let evmAuth = null;
|
|
5250
|
-
if ("transaction" in exact.payload) {
|
|
5457
|
+
if ("senderAuth" in exact.payload && "transaction" in exact.payload) {
|
|
5458
|
+
nonce = [exact.payload.transaction, exact.payload.senderAuth].map((t) => {
|
|
5459
|
+
try {
|
|
5460
|
+
return Buffer.from(t, "base64").toString("base64");
|
|
5461
|
+
} catch {
|
|
5462
|
+
return t;
|
|
5463
|
+
}
|
|
5464
|
+
}).join("|");
|
|
5465
|
+
} else if ("transaction" in exact.payload) {
|
|
5251
5466
|
try {
|
|
5252
5467
|
nonce = Buffer.from(exact.payload.transaction, "base64").toString("base64");
|
|
5253
5468
|
} catch {
|
|
5254
5469
|
nonce = exact.payload.transaction;
|
|
5255
5470
|
}
|
|
5471
|
+
} else if ("paymentGroup" in exact.payload) {
|
|
5472
|
+
nonce = exact.payload.paymentGroup.map((t) => {
|
|
5473
|
+
try {
|
|
5474
|
+
return Buffer.from(t, "base64").toString("base64");
|
|
5475
|
+
} catch {
|
|
5476
|
+
return t;
|
|
5477
|
+
}
|
|
5478
|
+
}).join("|");
|
|
5256
5479
|
} else if ("permit2Authorization" in exact.payload) {
|
|
5257
5480
|
evmAuth = exact.payload.permit2Authorization;
|
|
5258
5481
|
nonce = evmAuth.nonce;
|
|
@@ -5270,6 +5493,13 @@ function createPaymentGate(options) {
|
|
|
5270
5493
|
if (mode.kind === "self") {
|
|
5271
5494
|
result = await spec.net.settleExactSelf({ relayer: mode.relayer, payload: exact.payload, accept });
|
|
5272
5495
|
} else {
|
|
5496
|
+
const ftMethod = accept.extra.assetTransferMethod;
|
|
5497
|
+
const needsFeePayer = ftMethod === "svm" || ftMethod === "algorand" || ftMethod === "aptos";
|
|
5498
|
+
if (needsFeePayer && !accept.extra.feePayer) {
|
|
5499
|
+
throw new SettlementError(
|
|
5500
|
+
`exact settle: the ${ftMethod} facilitator rail is missing extra.feePayer (the gas sponsor) \u2014 cannot settle.`
|
|
5501
|
+
);
|
|
5502
|
+
}
|
|
5273
5503
|
result = await settleViaFacilitator({
|
|
5274
5504
|
url: mode.url,
|
|
5275
5505
|
...mode.authHeaders ? { authHeaders: mode.authHeaders } : {},
|
|
@@ -5286,9 +5516,7 @@ function createPaymentGate(options) {
|
|
|
5286
5516
|
amount: accept.amount,
|
|
5287
5517
|
payTo: accept.payTo,
|
|
5288
5518
|
maxTimeoutSeconds: accept.maxTimeoutSeconds,
|
|
5289
|
-
|
|
5290
|
-
// facilitator's `feePayer` (the gas sponsor); EVM forwards the token's EIP-712 domain.
|
|
5291
|
-
extra: accept.extra.assetTransferMethod === "svm" ? { feePayer: accept.extra.feePayer ?? "" } : { name: accept.extra.name ?? "", version: accept.extra.version ?? "" }
|
|
5519
|
+
extra: needsFeePayer ? { feePayer: accept.extra.feePayer } : { name: accept.extra.name ?? "", version: accept.extra.version ?? "" }
|
|
5292
5520
|
},
|
|
5293
5521
|
receipt: { network: accept.network, asset: accept.asset, payTo: accept.payTo, amount: accept.amount },
|
|
5294
5522
|
// The buyer address, for the receipt's `payer` fallback. EVM carries it in the
|
|
@@ -5330,7 +5558,12 @@ function requirePayment(options) {
|
|
|
5330
5558
|
} catch (err) {
|
|
5331
5559
|
if (err instanceof SettlementError) {
|
|
5332
5560
|
res.status(502);
|
|
5333
|
-
res.json({
|
|
5561
|
+
res.json({
|
|
5562
|
+
x402Version: 2,
|
|
5563
|
+
error: "settlement_failed",
|
|
5564
|
+
detail: err.message,
|
|
5565
|
+
fallback: "The gasless `exact` settlement failed. This resource also accepts the `onchain-proof` scheme \u2014 retry by paying that rail yourself (you broadcast the transfer and pay the gas). It is the fallback when no facilitator can sponsor the gas."
|
|
5566
|
+
});
|
|
5334
5567
|
return;
|
|
5335
5568
|
}
|
|
5336
5569
|
next(err);
|
|
@@ -5359,63 +5592,6 @@ function normaliseHeader(value) {
|
|
|
5359
5592
|
return value;
|
|
5360
5593
|
}
|
|
5361
5594
|
|
|
5362
|
-
// src/facilitators.ts
|
|
5363
|
-
var KNOWN_FACILITATORS = {
|
|
5364
|
-
// Base (eip155:8453). Every entry is keyless and LIVE-settled by us (a real EIP-3009
|
|
5365
|
-
// exact payment, buyer paid zero ETH) — not just a /supported read — on the dated day.
|
|
5366
|
-
"eip155:8453": [
|
|
5367
|
-
{
|
|
5368
|
-
url: "https://facilitator.payai.network",
|
|
5369
|
-
keyless: true,
|
|
5370
|
-
schemes: ["exact"],
|
|
5371
|
-
settles: ["eip3009"],
|
|
5372
|
-
note: "PayAI \u2014 keyless, sponsors gas (Base USDC EIP-3009). Verified 2026-06-14 (/supported + live demo)."
|
|
5373
|
-
},
|
|
5374
|
-
{
|
|
5375
|
-
url: "https://facilitator.xpay.sh",
|
|
5376
|
-
keyless: true,
|
|
5377
|
-
schemes: ["exact"],
|
|
5378
|
-
settles: ["eip3009"],
|
|
5379
|
-
note: "xpay \u2014 keyless, zero-fee, sponsors gas. LIVE-settled on Base 2026-06-15 (tx 0x2273d5\u2026)."
|
|
5380
|
-
}
|
|
5381
|
-
],
|
|
5382
|
-
// Solana (mainnet-beta). Keyless fee-payer sponsors for the SVM exact rail, each LIVE-settled
|
|
5383
|
-
// by us (a real SPL TransferChecked, buyer paid zero SOL) on the dated day — beyond a /supported
|
|
5384
|
-
// read. Daydreams + Questflow are intentionally ABSENT: their /supported is public but /verify
|
|
5385
|
-
// returns 401 (an API key is required), so they are not keyless for settlement.
|
|
5386
|
-
"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": [
|
|
5387
|
-
{
|
|
5388
|
-
url: "https://facilitator.payai.network",
|
|
5389
|
-
keyless: true,
|
|
5390
|
-
schemes: ["exact"],
|
|
5391
|
-
settles: ["svm"],
|
|
5392
|
-
note: "PayAI \u2014 keyless fee-payer sponsor (Solana SPL SVM). LIVE-settled 2026-06-14 (tx 4dL8jRKH\u2026)."
|
|
5393
|
-
},
|
|
5394
|
-
{
|
|
5395
|
-
url: "https://pay.openfacilitator.io",
|
|
5396
|
-
keyless: true,
|
|
5397
|
-
schemes: ["exact"],
|
|
5398
|
-
settles: ["svm"],
|
|
5399
|
-
note: "OpenFacilitator \u2014 keyless (no signup), fee-payer sponsor. LIVE-settled on Solana 2026-06-15 (tx 5BabDtX\u2026)."
|
|
5400
|
-
},
|
|
5401
|
-
{
|
|
5402
|
-
url: "https://facilitator.corbits.dev",
|
|
5403
|
-
keyless: true,
|
|
5404
|
-
schemes: ["exact"],
|
|
5405
|
-
settles: ["svm"],
|
|
5406
|
-
note: "Corbits \u2014 keyless, Solana-first fee-payer sponsor. LIVE-settled on Solana 2026-06-15 (tx BCreYer\u2026)."
|
|
5407
|
-
}
|
|
5408
|
-
]
|
|
5409
|
-
};
|
|
5410
|
-
function knownFacilitatorsFor(network) {
|
|
5411
|
-
return KNOWN_FACILITATORS[network] ?? [];
|
|
5412
|
-
}
|
|
5413
|
-
function firstKeylessFacilitator(network, method) {
|
|
5414
|
-
return knownFacilitatorsFor(network).find(
|
|
5415
|
-
(f) => f.keyless && f.schemes.includes("exact") && (method === void 0 || f.settles.includes(method))
|
|
5416
|
-
);
|
|
5417
|
-
}
|
|
5418
|
-
|
|
5419
5595
|
// src/receipts.ts
|
|
5420
5596
|
var DEFAULT_RETRIES = 5;
|
|
5421
5597
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
@@ -221,6 +221,10 @@ import {
|
|
|
221
221
|
import bs58 from "bs58";
|
|
222
222
|
var COMPUTE_UNIT_LIMIT = 2e4;
|
|
223
223
|
var COMPUTE_UNIT_PRICE_MICROLAMPORTS = 1;
|
|
224
|
+
var MAX_COMPUTE_UNIT_LIMIT = 300000n;
|
|
225
|
+
var MAX_COMPUTE_UNIT_PRICE_MICROLAMPORTS = 100000n;
|
|
226
|
+
var CB_SET_UNIT_LIMIT = 2;
|
|
227
|
+
var CB_SET_UNIT_PRICE = 3;
|
|
224
228
|
function tokenProgramFor(accept) {
|
|
225
229
|
return accept.extra.tokenProgram === "token-2022" ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID;
|
|
226
230
|
}
|
|
@@ -380,6 +384,22 @@ async function verifyAndSettleExactSolana(input) {
|
|
|
380
384
|
}
|
|
381
385
|
}
|
|
382
386
|
}
|
|
387
|
+
for (const ix of message.compiledInstructions) {
|
|
388
|
+
const ixProgram = keys.get(ix.programIdIndex);
|
|
389
|
+
if (!ixProgram || !ixProgram.equals(ComputeBudgetProgram.programId)) continue;
|
|
390
|
+
const data = Buffer.from(ix.data);
|
|
391
|
+
if (data.length >= 5 && data[0] === CB_SET_UNIT_LIMIT) {
|
|
392
|
+
const limit = BigInt(data.readUInt32LE(1));
|
|
393
|
+
if (limit > MAX_COMPUTE_UNIT_LIMIT) {
|
|
394
|
+
return fail("signature_invalid", `Compute-unit limit ${limit} exceeds the ${MAX_COMPUTE_UNIT_LIMIT} cap (fee-payer drain guard).`);
|
|
395
|
+
}
|
|
396
|
+
} else if (data.length >= 9 && data[0] === CB_SET_UNIT_PRICE) {
|
|
397
|
+
const price = data.readBigUInt64LE(1);
|
|
398
|
+
if (price > MAX_COMPUTE_UNIT_PRICE_MICROLAMPORTS) {
|
|
399
|
+
return fail("signature_invalid", `Compute-unit price ${price} \xB5lamports exceeds the ${MAX_COMPUTE_UNIT_PRICE_MICROLAMPORTS} cap (fee-payer drain guard).`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
383
403
|
const isSignedSigner = (pubkey) => {
|
|
384
404
|
const idx = message.staticAccountKeys.findIndex((k) => k.equals(pubkey));
|
|
385
405
|
return idx >= 1 && idx < message.header.numRequiredSignatures && !isEmptySig(tx.signatures[idx]);
|
|
@@ -221,6 +221,10 @@ function notFound(signature) {
|
|
|
221
221
|
var _bs58 = require('bs58'); var _bs582 = _interopRequireDefault(_bs58);
|
|
222
222
|
var COMPUTE_UNIT_LIMIT = 2e4;
|
|
223
223
|
var COMPUTE_UNIT_PRICE_MICROLAMPORTS = 1;
|
|
224
|
+
var MAX_COMPUTE_UNIT_LIMIT = 300000n;
|
|
225
|
+
var MAX_COMPUTE_UNIT_PRICE_MICROLAMPORTS = 100000n;
|
|
226
|
+
var CB_SET_UNIT_LIMIT = 2;
|
|
227
|
+
var CB_SET_UNIT_PRICE = 3;
|
|
224
228
|
function tokenProgramFor(accept) {
|
|
225
229
|
return accept.extra.tokenProgram === "token-2022" ? _spltoken.TOKEN_2022_PROGRAM_ID : _spltoken.TOKEN_PROGRAM_ID;
|
|
226
230
|
}
|
|
@@ -380,6 +384,22 @@ async function verifyAndSettleExactSolana(input) {
|
|
|
380
384
|
}
|
|
381
385
|
}
|
|
382
386
|
}
|
|
387
|
+
for (const ix of message.compiledInstructions) {
|
|
388
|
+
const ixProgram = keys.get(ix.programIdIndex);
|
|
389
|
+
if (!ixProgram || !ixProgram.equals(_web3js.ComputeBudgetProgram.programId)) continue;
|
|
390
|
+
const data = Buffer.from(ix.data);
|
|
391
|
+
if (data.length >= 5 && data[0] === CB_SET_UNIT_LIMIT) {
|
|
392
|
+
const limit = BigInt(data.readUInt32LE(1));
|
|
393
|
+
if (limit > MAX_COMPUTE_UNIT_LIMIT) {
|
|
394
|
+
return fail("signature_invalid", `Compute-unit limit ${limit} exceeds the ${MAX_COMPUTE_UNIT_LIMIT} cap (fee-payer drain guard).`);
|
|
395
|
+
}
|
|
396
|
+
} else if (data.length >= 9 && data[0] === CB_SET_UNIT_PRICE) {
|
|
397
|
+
const price = data.readBigUInt64LE(1);
|
|
398
|
+
if (price > MAX_COMPUTE_UNIT_PRICE_MICROLAMPORTS) {
|
|
399
|
+
return fail("signature_invalid", `Compute-unit price ${price} \xB5lamports exceeds the ${MAX_COMPUTE_UNIT_PRICE_MICROLAMPORTS} cap (fee-payer drain guard).`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
383
403
|
const isSignedSigner = (pubkey) => {
|
|
384
404
|
const idx = message.staticAccountKeys.findIndex((k) => k.equals(pubkey));
|
|
385
405
|
return idx >= 1 && idx < message.header.numRequiredSignatures && !isEmptySig(tx.signatures[idx]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@piprail/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Accept x402 crypto payments across 29 chains — every major EVM chain plus Solana, TON, Tron, NEAR, Sui, Aptos, Algorand, Stellar & XRPL — in a couple of lines. No backend, no database, no fee; payments settle straight to your wallet.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|