@piprail/sdk 1.13.0 → 1.14.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/CHAINS.md +3 -2
- package/CHANGELOG.md +49 -0
- package/ERRORS.md +2 -1
- package/README.md +19 -2
- package/STANDARDS.md +2 -0
- package/dist/{algorand-WGVF4KTU.js → algorand-7EUZYL2Z.js} +1 -1
- package/dist/{algorand-MXUSKX46.cjs → algorand-OIHGJN5S.cjs} +17 -17
- package/dist/{aptos-LPBLSEIQ.js → aptos-CDEYDDM5.js} +1 -1
- package/dist/{aptos-YT7SXWPF.cjs → aptos-WDWZOU25.cjs} +16 -16
- package/dist/{chunk-MDLZJGLY.cjs → chunk-FTKVCP6K.cjs} +24 -13
- package/dist/{chunk-SVMGHASK.js → chunk-H3A4KWLJ.js} +12 -1
- package/dist/index.cjs +399 -145
- package/dist/index.d.cts +209 -31
- package/dist/index.d.ts +209 -31
- package/dist/index.js +308 -54
- package/dist/{near-K6BDBABG.js → near-DT6LRIKB.js} +1 -1
- package/dist/{near-7ZDNISUX.cjs → near-FUH3VAXT.cjs} +19 -19
- package/dist/{solana-S3UFI3FE.js → solana-3TRYD4QB.js} +1 -1
- package/dist/{solana-PU7N2M64.cjs → solana-QUVXPKBZ.cjs} +14 -14
- package/dist/{stellar-VDQOFQEO.cjs → stellar-APZEBFAD.cjs} +21 -21
- package/dist/{stellar-Q5PO23SC.js → stellar-IK3UML6O.js} +1 -1
- package/dist/{sui-FKSMLKRF.cjs → sui-L7BQNJWO.cjs} +17 -17
- package/dist/{sui-WOXRKJXS.js → sui-VSE63WQM.js} +1 -1
- package/dist/{ton-VK6KRJHP.cjs → ton-5DLKKOFE.cjs} +14 -14
- package/dist/{ton-WPTXGLVK.js → ton-QHGQLJX2.js} +1 -1
- package/dist/{tron-6GXBXTR4.js → tron-2N2GA62O.js} +1 -1
- package/dist/{tron-WLOF5OUV.cjs → tron-HHIT6WKY.cjs} +24 -24
- package/dist/{xrpl-HEAPEXAM.js → xrpl-2GZMDYW5.js} +1 -1
- package/dist/{xrpl-CMNI25BV.cjs → xrpl-USEG4AHX.cjs} +21 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
SettlementError,
|
|
14
14
|
UnknownTokenError,
|
|
15
15
|
UnsupportedNetworkError,
|
|
16
|
+
UnsupportedSchemeError,
|
|
16
17
|
WrongChainError,
|
|
17
18
|
WrongFamilyError,
|
|
18
19
|
floorUnits,
|
|
@@ -21,7 +22,7 @@ import {
|
|
|
21
22
|
parseUnits,
|
|
22
23
|
rejectForeignToken,
|
|
23
24
|
toInsufficientFundsError
|
|
24
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-H3A4KWLJ.js";
|
|
25
26
|
|
|
26
27
|
// src/drivers/registry.ts
|
|
27
28
|
var byFamily = /* @__PURE__ */ new Map();
|
|
@@ -97,13 +98,18 @@ var CHAINS = {
|
|
|
97
98
|
defaultRpc: "https://ethereum-rpc.publicnode.com",
|
|
98
99
|
tokens: {
|
|
99
100
|
USDC: { address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", decimals: 6, symbol: "USDC" },
|
|
100
|
-
USDT: { address: "0xdAC17F958D2ee523a2206206994597C13D831ec7", decimals: 6, symbol: "USDT" }
|
|
101
|
+
USDT: { address: "0xdAC17F958D2ee523a2206206994597C13D831ec7", decimals: 6, symbol: "USDT" },
|
|
102
|
+
// Circle EURC — EIP-3009 (exact-payable). On-chain EIP-712 domain name is "Euro Coin" here
|
|
103
|
+
// (NOT "EURC"); the buyer re-derives it on-chain, so the symbol below is display-only.
|
|
104
|
+
EURC: { address: "0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c", decimals: 6, symbol: "EURC" }
|
|
101
105
|
}
|
|
102
106
|
},
|
|
103
107
|
base: {
|
|
104
108
|
chain: base,
|
|
105
109
|
tokens: {
|
|
106
|
-
USDC: { address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", decimals: 6, symbol: "USDC" }
|
|
110
|
+
USDC: { address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", decimals: 6, symbol: "USDC" },
|
|
111
|
+
// Circle EURC — EIP-3009 (exact-payable). On-chain EIP-712 domain name is "EURC" here.
|
|
112
|
+
EURC: { address: "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42", decimals: 6, symbol: "EURC" }
|
|
107
113
|
}
|
|
108
114
|
},
|
|
109
115
|
arbitrum: {
|
|
@@ -139,7 +145,9 @@ var CHAINS = {
|
|
|
139
145
|
chain: avalanche,
|
|
140
146
|
tokens: {
|
|
141
147
|
USDC: { address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", decimals: 6, symbol: "USDC" },
|
|
142
|
-
USDT: { address: "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7", decimals: 6, symbol: "USDT" }
|
|
148
|
+
USDT: { address: "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7", decimals: 6, symbol: "USDT" },
|
|
149
|
+
// Circle EURC — EIP-3009 (exact-payable). On-chain EIP-712 domain name is "Euro Coin" here.
|
|
150
|
+
EURC: { address: "0xC891EB4cbdEFf6e073e859e987815Ed1505c2ACD", decimals: 6, symbol: "EURC" }
|
|
143
151
|
}
|
|
144
152
|
},
|
|
145
153
|
// ── More popular EVM mainnets. Every address below was verified on-chain
|
|
@@ -591,6 +599,53 @@ function encodeXPaymentHeader(input) {
|
|
|
591
599
|
};
|
|
592
600
|
return base64(JSON.stringify(payload));
|
|
593
601
|
}
|
|
602
|
+
async function payExactEvm(input) {
|
|
603
|
+
const { publicClient, walletClient, account, chainId, accept } = input;
|
|
604
|
+
let code;
|
|
605
|
+
try {
|
|
606
|
+
code = await publicClient.getCode({ address: account.address });
|
|
607
|
+
} catch {
|
|
608
|
+
code = void 0;
|
|
609
|
+
}
|
|
610
|
+
if (code && code !== "0x") {
|
|
611
|
+
throw new UnsupportedSchemeError(
|
|
612
|
+
`exact buyer rail requires an EOA signer; ${account.address} is a contract / EIP-1271 / EIP-7702-delegated account (no recoverable ECDSA signature). Pay via onchain-proof.`
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
const domain = await readExactDomain(publicClient, accept.asset);
|
|
616
|
+
if (!domain) {
|
|
617
|
+
throw new UnsupportedSchemeError(
|
|
618
|
+
`exact: ${accept.asset} on ${accept.network} isn't an EIP-3009 token (USDT needs Permit2; native coin and plain ERC-20s aren't exact-payable). Pay via onchain-proof.`
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
const g = globalThis.crypto;
|
|
622
|
+
if (!g?.getRandomValues) {
|
|
623
|
+
throw new UnsupportedSchemeError(
|
|
624
|
+
"this runtime lacks Web Crypto (globalThis.crypto.getRandomValues); the exact rail needs a CSPRNG nonce."
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
const raw = new Uint8Array(32);
|
|
628
|
+
g.getRandomValues(raw);
|
|
629
|
+
const nonce = `0x${[...raw].map((b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
630
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
631
|
+
const from = account.address;
|
|
632
|
+
const to = getAddress2(accept.payTo);
|
|
633
|
+
const value = accept.amount;
|
|
634
|
+
const validAfter = "0";
|
|
635
|
+
const validBefore = String(now + accept.maxTimeoutSeconds);
|
|
636
|
+
const signature = await walletClient.signTypedData({
|
|
637
|
+
account,
|
|
638
|
+
domain: { name: domain.name, version: domain.version, chainId, verifyingContract: getAddress2(accept.asset) },
|
|
639
|
+
types: EIP3009_TYPES,
|
|
640
|
+
primaryType: "TransferWithAuthorization",
|
|
641
|
+
message: { from, to, value: BigInt(value), validAfter: 0n, validBefore: BigInt(validBefore), nonce }
|
|
642
|
+
});
|
|
643
|
+
return {
|
|
644
|
+
payload: { signature, authorization: { from, to, value, validAfter, validBefore, nonce } },
|
|
645
|
+
payerFrom: from,
|
|
646
|
+
nonce
|
|
647
|
+
};
|
|
648
|
+
}
|
|
594
649
|
var eip3009Abi = [
|
|
595
650
|
{
|
|
596
651
|
type: "function",
|
|
@@ -873,6 +928,9 @@ function buildReceiptHeader(receipt) {
|
|
|
873
928
|
function buildSignatureHeader(signature) {
|
|
874
929
|
return toBase64Json(signature);
|
|
875
930
|
}
|
|
931
|
+
function buildExactSignatureHeader(input) {
|
|
932
|
+
return toBase64Json({ x402Version: 2, accepted: input.accepted, payload: input.payload });
|
|
933
|
+
}
|
|
876
934
|
async function parseChallenge(response) {
|
|
877
935
|
const headerValue = response.headers.get(HEADER_REQUIRED);
|
|
878
936
|
if (headerValue) {
|
|
@@ -887,11 +945,24 @@ async function parseChallenge(response) {
|
|
|
887
945
|
return null;
|
|
888
946
|
}
|
|
889
947
|
function parseReceipt(response) {
|
|
890
|
-
const headerValue = response.headers.get(HEADER_RESPONSE);
|
|
948
|
+
const headerValue = response.headers.get(HEADER_RESPONSE) ?? response.headers.get(HEADER_RESPONSE_V1);
|
|
891
949
|
if (!headerValue) return null;
|
|
892
950
|
const parsed = fromBase64Json(headerValue);
|
|
893
951
|
return isValidReceipt(parsed) ? parsed : null;
|
|
894
952
|
}
|
|
953
|
+
function parseSettleResponse(response) {
|
|
954
|
+
const headerValue = response.headers.get(HEADER_RESPONSE) ?? response.headers.get(HEADER_RESPONSE_V1);
|
|
955
|
+
if (!headerValue) return null;
|
|
956
|
+
const parsed = fromBase64Json(headerValue);
|
|
957
|
+
if (!parsed || typeof parsed !== "object" || typeof parsed.success !== "boolean") return null;
|
|
958
|
+
return {
|
|
959
|
+
success: parsed.success,
|
|
960
|
+
...typeof parsed.transaction === "string" ? { transaction: parsed.transaction } : {},
|
|
961
|
+
...typeof parsed.network === "string" ? { network: parsed.network } : {},
|
|
962
|
+
...typeof parsed.payer === "string" ? { payer: parsed.payer } : {},
|
|
963
|
+
...typeof parsed.errorReason === "string" ? { errorReason: parsed.errorReason } : {}
|
|
964
|
+
};
|
|
965
|
+
}
|
|
895
966
|
function parseSignatureHeader(value) {
|
|
896
967
|
const parsed = fromBase64Json(value);
|
|
897
968
|
if (!parsed || typeof parsed !== "object") return null;
|
|
@@ -1085,6 +1156,15 @@ function makeEvmNetwork(resolved) {
|
|
|
1085
1156
|
},
|
|
1086
1157
|
async estimateCost(accept) {
|
|
1087
1158
|
const { decimals, symbol } = resolved.chain.nativeCurrency;
|
|
1159
|
+
if (accept.scheme === "exact") {
|
|
1160
|
+
return nativeCost({
|
|
1161
|
+
symbol,
|
|
1162
|
+
decimals,
|
|
1163
|
+
fee: 0n,
|
|
1164
|
+
basis: "estimated",
|
|
1165
|
+
detail: "gasless \u2014 the server/facilitator settles the signed authorization"
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1088
1168
|
const gasLimit = accept.asset === "native" ? 21000n : 65000n;
|
|
1089
1169
|
try {
|
|
1090
1170
|
const gasPrice = await publicClient.getGasPrice();
|
|
@@ -1146,6 +1226,21 @@ function makeEvmNetwork(resolved) {
|
|
|
1146
1226
|
minConfirmations: accept.extra.minConfirmations
|
|
1147
1227
|
});
|
|
1148
1228
|
},
|
|
1229
|
+
// Standard x402 `exact` rail (EIP-3009), BUYER side — EVM only. Re-derives the
|
|
1230
|
+
// token's EIP-712 domain on-chain, signs an authorization with the agent's own
|
|
1231
|
+
// key, and returns it for the client to frame into PAYMENT-SIGNATURE. Never
|
|
1232
|
+
// broadcasts. Throws UnsupportedSchemeError for a non-EIP-3009 token / contract signer.
|
|
1233
|
+
async payExact(wallet, accept) {
|
|
1234
|
+
const a = wallet._native;
|
|
1235
|
+
const { payload, payerFrom, nonce } = await payExactEvm({
|
|
1236
|
+
publicClient,
|
|
1237
|
+
walletClient: a.walletClient,
|
|
1238
|
+
account: a.account,
|
|
1239
|
+
chainId: resolved.chainId,
|
|
1240
|
+
accept
|
|
1241
|
+
});
|
|
1242
|
+
return { payload, accepted: accept, payerFrom, nonce };
|
|
1243
|
+
},
|
|
1149
1244
|
// Standard x402 `exact` rail (EIP-3009), seller side — EVM only.
|
|
1150
1245
|
async exactDomain(asset) {
|
|
1151
1246
|
return readExactDomain(publicClient, asset);
|
|
@@ -1176,7 +1271,7 @@ var loaders = {
|
|
|
1176
1271
|
solana: async () => {
|
|
1177
1272
|
let mod;
|
|
1178
1273
|
try {
|
|
1179
|
-
mod = await import("./solana-
|
|
1274
|
+
mod = await import("./solana-3TRYD4QB.js");
|
|
1180
1275
|
} catch (cause) {
|
|
1181
1276
|
throw new MissingDriverError(
|
|
1182
1277
|
`Solana selected, but its packages aren't installed. Run: npm install @solana/web3.js @solana/spl-token bs58`,
|
|
@@ -1188,7 +1283,7 @@ var loaders = {
|
|
|
1188
1283
|
ton: async () => {
|
|
1189
1284
|
let mod;
|
|
1190
1285
|
try {
|
|
1191
|
-
mod = await import("./ton-
|
|
1286
|
+
mod = await import("./ton-QHGQLJX2.js");
|
|
1192
1287
|
} catch (cause) {
|
|
1193
1288
|
throw new MissingDriverError(
|
|
1194
1289
|
`TON selected, but its packages aren't installed. Run: npm install @ton/ton @ton/core @ton/crypto`,
|
|
@@ -1200,7 +1295,7 @@ var loaders = {
|
|
|
1200
1295
|
stellar: async () => {
|
|
1201
1296
|
let mod;
|
|
1202
1297
|
try {
|
|
1203
|
-
mod = await import("./stellar-
|
|
1298
|
+
mod = await import("./stellar-IK3UML6O.js");
|
|
1204
1299
|
} catch (cause) {
|
|
1205
1300
|
throw new MissingDriverError(
|
|
1206
1301
|
`Stellar selected, but its package isn't installed. Run: npm install @stellar/stellar-sdk`,
|
|
@@ -1212,7 +1307,7 @@ var loaders = {
|
|
|
1212
1307
|
xrpl: async () => {
|
|
1213
1308
|
let mod;
|
|
1214
1309
|
try {
|
|
1215
|
-
mod = await import("./xrpl-
|
|
1310
|
+
mod = await import("./xrpl-2GZMDYW5.js");
|
|
1216
1311
|
} catch (cause) {
|
|
1217
1312
|
throw new MissingDriverError(
|
|
1218
1313
|
`XRPL selected, but its package isn't installed. Run: npm install xrpl`,
|
|
@@ -1224,7 +1319,7 @@ var loaders = {
|
|
|
1224
1319
|
tron: async () => {
|
|
1225
1320
|
let mod;
|
|
1226
1321
|
try {
|
|
1227
|
-
mod = await import("./tron-
|
|
1322
|
+
mod = await import("./tron-2N2GA62O.js");
|
|
1228
1323
|
} catch (cause) {
|
|
1229
1324
|
throw new MissingDriverError(
|
|
1230
1325
|
`Tron selected, but its package isn't installed. Run: npm install tronweb`,
|
|
@@ -1236,7 +1331,7 @@ var loaders = {
|
|
|
1236
1331
|
sui: async () => {
|
|
1237
1332
|
let mod;
|
|
1238
1333
|
try {
|
|
1239
|
-
mod = await import("./sui-
|
|
1334
|
+
mod = await import("./sui-VSE63WQM.js");
|
|
1240
1335
|
} catch (cause) {
|
|
1241
1336
|
throw new MissingDriverError(
|
|
1242
1337
|
`Sui selected, but its package isn't installed. Run: npm install @mysten/sui`,
|
|
@@ -1248,7 +1343,7 @@ var loaders = {
|
|
|
1248
1343
|
near: async () => {
|
|
1249
1344
|
let mod;
|
|
1250
1345
|
try {
|
|
1251
|
-
mod = await import("./near-
|
|
1346
|
+
mod = await import("./near-DT6LRIKB.js");
|
|
1252
1347
|
} catch (cause) {
|
|
1253
1348
|
throw new MissingDriverError(
|
|
1254
1349
|
`NEAR selected, but its package isn't installed. Run: npm install near-api-js`,
|
|
@@ -1260,7 +1355,7 @@ var loaders = {
|
|
|
1260
1355
|
aptos: async () => {
|
|
1261
1356
|
let mod;
|
|
1262
1357
|
try {
|
|
1263
|
-
mod = await import("./aptos-
|
|
1358
|
+
mod = await import("./aptos-CDEYDDM5.js");
|
|
1264
1359
|
} catch (cause) {
|
|
1265
1360
|
throw new MissingDriverError(
|
|
1266
1361
|
`Aptos selected, but its package isn't installed. Run: npm install @aptos-labs/ts-sdk`,
|
|
@@ -1272,7 +1367,7 @@ var loaders = {
|
|
|
1272
1367
|
algorand: async () => {
|
|
1273
1368
|
let mod;
|
|
1274
1369
|
try {
|
|
1275
|
-
mod = await import("./algorand-
|
|
1370
|
+
mod = await import("./algorand-7EUZYL2Z.js");
|
|
1276
1371
|
} catch (cause) {
|
|
1277
1372
|
throw new MissingDriverError(
|
|
1278
1373
|
`Algorand selected, but its package isn't installed. Run: npm install algosdk`,
|
|
@@ -1339,7 +1434,7 @@ function getDirectoryInfo(source) {
|
|
|
1339
1434
|
}
|
|
1340
1435
|
function decorateOutcome(o) {
|
|
1341
1436
|
const info = DIRECTORY_INFO[o.source];
|
|
1342
|
-
return { ...o, visibility: o.ok ? info.onSuccess : "not-listable", note: info.caveat };
|
|
1437
|
+
return { ...o, visibility: o.visibility ?? (o.ok ? info.onSuccess : "not-listable"), note: info.caveat };
|
|
1343
1438
|
}
|
|
1344
1439
|
var BAZAAR_URL = "https://api.cdp.coinbase.com/platform/v2/x402/discovery/resources";
|
|
1345
1440
|
var INDEX402_SEARCH = "https://402index.io/api/v1/services";
|
|
@@ -1495,12 +1590,15 @@ async function register402Index(input) {
|
|
|
1495
1590
|
body: JSON.stringify(payload)
|
|
1496
1591
|
});
|
|
1497
1592
|
if (res.ok) {
|
|
1498
|
-
const
|
|
1593
|
+
const body = await res.json().catch(() => ({}));
|
|
1594
|
+
const msg = typeof body.message === "string" && body.message.length > 0 ? body.message : void 0;
|
|
1595
|
+
const live = body.service?.status === "active";
|
|
1499
1596
|
return {
|
|
1500
1597
|
source: "402index",
|
|
1501
1598
|
ok: true,
|
|
1502
1599
|
status: res.status,
|
|
1503
|
-
|
|
1600
|
+
...live ? { visibility: "live" } : {},
|
|
1601
|
+
detail: msg ?? (live ? "Registered + live on 402 Index (domain verified)." : "Registered on 402 Index \u2014 pending review (verify your domain on 402index.io for instant approval).")
|
|
1504
1602
|
};
|
|
1505
1603
|
}
|
|
1506
1604
|
const why = await readIndexError(res);
|
|
@@ -1514,14 +1612,6 @@ async function register402Index(input) {
|
|
|
1514
1612
|
return { source: "402index", ok: false, detail: errMsg(err) };
|
|
1515
1613
|
}
|
|
1516
1614
|
}
|
|
1517
|
-
async function readIndexMessage(res) {
|
|
1518
|
-
try {
|
|
1519
|
-
const body = await res.json();
|
|
1520
|
-
return typeof body.message === "string" && body.message.length > 0 ? body.message : void 0;
|
|
1521
|
-
} catch {
|
|
1522
|
-
return void 0;
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
1615
|
async function readIndexError(res) {
|
|
1526
1616
|
try {
|
|
1527
1617
|
const body = await res.json();
|
|
@@ -1864,6 +1954,7 @@ var SpendLedger = class {
|
|
|
1864
1954
|
};
|
|
1865
1955
|
|
|
1866
1956
|
// src/client.ts
|
|
1957
|
+
var DEFAULT_SCHEMES = ["onchain-proof"];
|
|
1867
1958
|
var RECIPIENT_FIX = {
|
|
1868
1959
|
NO_TRUSTLINE: "the recipient needs a one-time trustline for this asset before it can receive",
|
|
1869
1960
|
NOT_REGISTERED: "the recipient must be storage_deposit-registered on this token (NEP-145, one-time)",
|
|
@@ -1906,6 +1997,11 @@ var PipRailClient = class {
|
|
|
1906
1997
|
return { net, wallet };
|
|
1907
1998
|
})();
|
|
1908
1999
|
}
|
|
2000
|
+
/** Resolve the effective scheme set: a per-call override, else the constructor's
|
|
2001
|
+
* `schemes`, else the `onchain-proof`-only default. */
|
|
2002
|
+
resolveSchemes(perCall) {
|
|
2003
|
+
return perCall ?? this.opts.schemes ?? DEFAULT_SCHEMES;
|
|
2004
|
+
}
|
|
1909
2005
|
/** GET that auto-handles 402. Pass a full URL to any x402-gated endpoint. */
|
|
1910
2006
|
get(url, init) {
|
|
1911
2007
|
return this.fetch(url, { ...init ?? {}, method: "GET" });
|
|
@@ -1950,7 +2046,7 @@ var PipRailClient = class {
|
|
|
1950
2046
|
async quote(url, init) {
|
|
1951
2047
|
const res = await fetch(url, { ...init ?? {}, method: init?.method ?? "GET" });
|
|
1952
2048
|
if (res.status !== 402) return null;
|
|
1953
|
-
const { quote } = await this.resolveChallenge(url, res);
|
|
2049
|
+
const { quote } = await this.resolveChallenge(url, res, this.resolveSchemes());
|
|
1954
2050
|
return quote;
|
|
1955
2051
|
}
|
|
1956
2052
|
/**
|
|
@@ -1969,7 +2065,7 @@ var PipRailClient = class {
|
|
|
1969
2065
|
async estimateCost(url, init) {
|
|
1970
2066
|
const res = await fetch(url, { ...init ?? {}, method: init?.method ?? "GET" });
|
|
1971
2067
|
if (res.status !== 402) return null;
|
|
1972
|
-
const { net, accept, quote } = await this.resolveChallenge(url, res);
|
|
2068
|
+
const { net, accept, quote } = await this.resolveChallenge(url, res, this.resolveSchemes());
|
|
1973
2069
|
const cost = await net.estimateCost(accept);
|
|
1974
2070
|
return { quote, cost };
|
|
1975
2071
|
}
|
|
@@ -2005,7 +2101,7 @@ var PipRailClient = class {
|
|
|
2005
2101
|
throw new InvalidEnvelopeError("402 response did not include a parseable x402 challenge.");
|
|
2006
2102
|
}
|
|
2007
2103
|
const { net, wallet } = await this.ensure();
|
|
2008
|
-
return this.planFromChallenge(net, wallet, challenge, url);
|
|
2104
|
+
return this.planFromChallenge(net, wallet, challenge, url, this.resolveSchemes());
|
|
2009
2105
|
}
|
|
2010
2106
|
/**
|
|
2011
2107
|
* Convenience over {@link planPayment}: can the wallet settle this URL right now?
|
|
@@ -2033,8 +2129,8 @@ var PipRailClient = class {
|
|
|
2033
2129
|
* - A resource just listed via {@link register} may not appear yet — 402 Index reviews
|
|
2034
2130
|
* before publishing, so retry with a brief backoff if a fresh listing is missing.
|
|
2035
2131
|
* - Results are cross-scheme (mostly the mainstream `exact` scheme); `fetch()` pays
|
|
2036
|
-
*
|
|
2037
|
-
* `
|
|
2132
|
+
* `onchain-proof` rails by default, and standard `exact` rails too once you opt in
|
|
2133
|
+
* with `schemes: ['onchain-proof', 'exact']` (EVM + EIP-3009 — USDC/EURC).
|
|
2038
2134
|
*/
|
|
2039
2135
|
async discover(opts = {}) {
|
|
2040
2136
|
const found = await searchOpenIndexes({
|
|
@@ -2173,13 +2269,14 @@ var PipRailClient = class {
|
|
|
2173
2269
|
}
|
|
2174
2270
|
const firstResponse = await fetch(url, init);
|
|
2175
2271
|
if (firstResponse.status !== 402) return firstResponse;
|
|
2176
|
-
const
|
|
2272
|
+
const schemes = this.resolveSchemes(init?.schemes);
|
|
2273
|
+
const resolved = await this.resolveChallenge(url, firstResponse, schemes);
|
|
2177
2274
|
const { net, wallet, challenge } = resolved;
|
|
2178
2275
|
let accept = resolved.accept;
|
|
2179
2276
|
let quote = resolved.quote;
|
|
2180
2277
|
const autoRoute = init?.autoRoute ?? this.opts.autoRoute ?? false;
|
|
2181
2278
|
if (autoRoute) {
|
|
2182
|
-
const plan = await this.planFromChallenge(net, wallet, challenge, url);
|
|
2279
|
+
const plan = await this.planFromChallenge(net, wallet, challenge, url, schemes);
|
|
2183
2280
|
if (!plan.best) {
|
|
2184
2281
|
throw new PaymentDeclinedError(plan.fundingHint ?? "No rail is settleable for this payment.");
|
|
2185
2282
|
}
|
|
@@ -2188,6 +2285,9 @@ var PipRailClient = class {
|
|
|
2188
2285
|
}
|
|
2189
2286
|
this.safeEmit({ kind: "payment-required", challenge, accept });
|
|
2190
2287
|
await this.authorize(quote);
|
|
2288
|
+
if (accept.scheme === "exact") {
|
|
2289
|
+
return this.payExactRail(net, wallet, accept, url, init, quote);
|
|
2290
|
+
}
|
|
2191
2291
|
const { ref, confirmed } = await this.payAndConfirm(net, wallet, accept);
|
|
2192
2292
|
const response = await this.retryWithProof(url, init, accept, ref, confirmed);
|
|
2193
2293
|
this.recordSpend(quote, ref);
|
|
@@ -2199,7 +2299,7 @@ var PipRailClient = class {
|
|
|
2199
2299
|
* network, pick the accept the client can pay, and build its quote. Shared by
|
|
2200
2300
|
* `quote()` (read-only) and `fetch()` (which then authorises + pays).
|
|
2201
2301
|
*/
|
|
2202
|
-
async resolveChallenge(url, response) {
|
|
2302
|
+
async resolveChallenge(url, response, schemes) {
|
|
2203
2303
|
const challenge = await parseChallenge(response);
|
|
2204
2304
|
if (!challenge) {
|
|
2205
2305
|
throw new InvalidEnvelopeError(
|
|
@@ -2207,11 +2307,29 @@ var PipRailClient = class {
|
|
|
2207
2307
|
);
|
|
2208
2308
|
}
|
|
2209
2309
|
const { net, wallet } = await this.ensure();
|
|
2210
|
-
const candidates = this.gatherCandidates(net, challenge);
|
|
2310
|
+
const candidates = this.gatherCandidates(net, challenge, schemes);
|
|
2211
2311
|
if (candidates.length === 0) {
|
|
2212
|
-
const
|
|
2312
|
+
const exactOnNet = challenge.accepts.some(
|
|
2313
|
+
(a) => a.scheme === "exact" && net.supports(a.network)
|
|
2314
|
+
);
|
|
2315
|
+
if (schemes.includes("exact") && exactOnNet && typeof net.payExact !== "function") {
|
|
2316
|
+
throw new UnsupportedSchemeError(
|
|
2317
|
+
`This 402 offers a standard 'exact' rail on ${net.network}, but the ${net.family} family can't pay 'exact' (EVM + EIP-3009 only), and no 'onchain-proof' rail was offered.`
|
|
2318
|
+
);
|
|
2319
|
+
}
|
|
2320
|
+
if (!schemes.includes("exact") && exactOnNet && typeof net.payExact === "function") {
|
|
2321
|
+
const payable = challenge.accepts.some(
|
|
2322
|
+
(a) => a.scheme === "exact" && net.supports(a.network) && net.describeAsset(a.asset) != null
|
|
2323
|
+
);
|
|
2324
|
+
if (payable) {
|
|
2325
|
+
throw new NoCompatibleAcceptError(
|
|
2326
|
+
`This 402 is payable only via the standard 'exact' rail on ${net.network}, which is OFF by default. Enable it: new PipRailClient({ \u2026, schemes: ['onchain-proof', 'exact'] }) or per call fetch(url, { schemes: ['exact'] }) (MCP: PIPRAIL_SCHEMES=onchain-proof,exact).`
|
|
2327
|
+
);
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
const networks = [...new Set(challenge.accepts.map((a) => a.network))].join(", ");
|
|
2213
2331
|
throw new NoCompatibleAcceptError(
|
|
2214
|
-
`No accepts[] entry
|
|
2332
|
+
`No accepts[] entry payable by this client on ${net.network} (schemes: ${schemes.join(", ")}; challenge offered: ${networks || "none"}).`
|
|
2215
2333
|
);
|
|
2216
2334
|
}
|
|
2217
2335
|
const priced = candidates.map((accept) => ({
|
|
@@ -2221,20 +2339,37 @@ var PipRailClient = class {
|
|
|
2221
2339
|
const chosen = priced.find((p) => p.quote.withinPolicy) ?? priced[0];
|
|
2222
2340
|
return { net, wallet, accept: chosen.accept, challenge, quote: chosen.quote };
|
|
2223
2341
|
}
|
|
2224
|
-
/** The candidate accepts this client could pay
|
|
2225
|
-
*
|
|
2226
|
-
*
|
|
2227
|
-
*
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
)
|
|
2342
|
+
/** The candidate accepts this client could pay, on the bound network. Always the
|
|
2343
|
+
* backendless `onchain-proof` rails; PLUS standard `exact` rails when `schemes`
|
|
2344
|
+
* enables them AND the driver can settle them (EVM `payExact` + a recognised
|
|
2345
|
+
* EIP-3009 token). `onchain-proof` is gathered FIRST so default selection is
|
|
2346
|
+
* unchanged when `exact` is off. */
|
|
2347
|
+
gatherCandidates(net, challenge, schemes) {
|
|
2348
|
+
const out = [];
|
|
2349
|
+
if (schemes.includes("onchain-proof")) {
|
|
2350
|
+
out.push(
|
|
2351
|
+
...challenge.accepts.filter(
|
|
2352
|
+
(a) => a.scheme === "onchain-proof" && net.supports(a.network)
|
|
2353
|
+
)
|
|
2354
|
+
);
|
|
2355
|
+
}
|
|
2356
|
+
if (schemes.includes("exact")) {
|
|
2357
|
+
out.push(
|
|
2358
|
+
...challenge.accepts.filter(
|
|
2359
|
+
(a) => a.scheme === "exact" && net.supports(a.network) && typeof net.payExact === "function" && net.describeAsset(a.asset) != null && // a foreign rail's maxTimeoutSeconds must be a usable positive integer, or
|
|
2360
|
+
// signing it would build a NaN/garbage validBefore — drop it silently
|
|
2361
|
+
// (symmetric with an unrecognised token) rather than leak a raw SyntaxError.
|
|
2362
|
+
Number.isInteger(a.maxTimeoutSeconds) && a.maxTimeoutSeconds > 0
|
|
2363
|
+
)
|
|
2364
|
+
);
|
|
2365
|
+
}
|
|
2366
|
+
return out;
|
|
2232
2367
|
}
|
|
2233
2368
|
/** Build the full {@link PaymentPlan} from an already-parsed challenge + bound
|
|
2234
2369
|
* net/wallet. Shared by `planPayment` (read-only) and `fetch`'s autoRoute. */
|
|
2235
|
-
async planFromChallenge(net, wallet, challenge, url) {
|
|
2370
|
+
async planFromChallenge(net, wallet, challenge, url, schemes) {
|
|
2236
2371
|
const chainLabel = typeof this.opts.chain === "string" ? this.opts.chain : net.network;
|
|
2237
|
-
const candidates = this.gatherCandidates(net, challenge);
|
|
2372
|
+
const candidates = this.gatherCandidates(net, challenge, schemes);
|
|
2238
2373
|
if (candidates.length === 0) {
|
|
2239
2374
|
const offered = [...new Set(challenge.accepts.map((a) => a.network))].join(", ") || "none";
|
|
2240
2375
|
return {
|
|
@@ -2274,17 +2409,23 @@ var PipRailClient = class {
|
|
|
2274
2409
|
const rr = await net.recipientReady(accept.payTo, accept.asset).catch(() => ({ ready: "unknown" }));
|
|
2275
2410
|
const amount = BigInt(accept.amount);
|
|
2276
2411
|
const fee = safeBig(cost.fee);
|
|
2412
|
+
const isExact = accept.scheme === "exact";
|
|
2277
2413
|
const isNative = accept.asset === "native";
|
|
2278
2414
|
const blockers = [];
|
|
2279
2415
|
const warnings = [];
|
|
2280
2416
|
const shortfall = {};
|
|
2281
2417
|
if (!quote.withinPolicy) blockers.push("OUTSIDE_POLICY");
|
|
2282
2418
|
if (quote.symbolMismatch) warnings.push("SYMBOL_MISMATCH");
|
|
2283
|
-
if (cost.basis === "heuristic") warnings.push("GAS_HEURISTIC");
|
|
2419
|
+
if (!isExact && cost.basis === "heuristic") warnings.push("GAS_HEURISTIC");
|
|
2284
2420
|
const tokenKnown = bal.token != null;
|
|
2285
2421
|
const nativeKnown = bal.native != null;
|
|
2286
|
-
if (!tokenKnown || !nativeKnown) warnings.push("BALANCE_UNREADABLE");
|
|
2287
|
-
if (
|
|
2422
|
+
if (isExact ? !tokenKnown : !tokenKnown || !nativeKnown) warnings.push("BALANCE_UNREADABLE");
|
|
2423
|
+
if (isExact) {
|
|
2424
|
+
if (tokenKnown && bal.token < amount) {
|
|
2425
|
+
blockers.push("INSUFFICIENT_TOKEN");
|
|
2426
|
+
shortfall.token = formatUnits(amount - bal.token, quote.decimals);
|
|
2427
|
+
}
|
|
2428
|
+
} else if (isNative) {
|
|
2288
2429
|
if (nativeKnown && bal.native < amount + fee) {
|
|
2289
2430
|
blockers.push("INSUFFICIENT_TOKEN");
|
|
2290
2431
|
shortfall.token = formatUnits(amount + fee - bal.native, quote.decimals);
|
|
@@ -2311,7 +2452,7 @@ var PipRailClient = class {
|
|
|
2311
2452
|
} else {
|
|
2312
2453
|
recipient = { ready: rr.ready };
|
|
2313
2454
|
}
|
|
2314
|
-
const unreadable = isNative ? !nativeKnown : !tokenKnown || !nativeKnown;
|
|
2455
|
+
const unreadable = isExact ? !tokenKnown : isNative ? !nativeKnown : !tokenKnown || !nativeKnown;
|
|
2315
2456
|
const state = blockers.length ? "blocked" : unreadable || rr.ready === "unknown" ? "unknown" : "payable";
|
|
2316
2457
|
return {
|
|
2317
2458
|
accept,
|
|
@@ -2340,6 +2481,11 @@ var PipRailClient = class {
|
|
|
2340
2481
|
const amountBase = BigInt(accept.amount);
|
|
2341
2482
|
const described = net.describeAsset(accept.asset);
|
|
2342
2483
|
const decimals = described?.decimals ?? accept.extra.decimals;
|
|
2484
|
+
if (decimals === void 0) {
|
|
2485
|
+
throw new InvalidEnvelopeError(
|
|
2486
|
+
`challenge for ${accept.asset} on ${accept.network} states no decimals and the SDK doesn't recognise the token \u2014 refusing to price it.`
|
|
2487
|
+
);
|
|
2488
|
+
}
|
|
2343
2489
|
const symbol = described?.symbol ?? accept.extra.symbol;
|
|
2344
2490
|
const amountFormatted = formatUnits(amountBase, decimals);
|
|
2345
2491
|
const intent = {
|
|
@@ -2500,7 +2646,102 @@ var PipRailClient = class {
|
|
|
2500
2646
|
{ ref }
|
|
2501
2647
|
);
|
|
2502
2648
|
}
|
|
2649
|
+
/**
|
|
2650
|
+
* Pay a standard x402 `exact` rail — a SEPARATE, fundamentally more conservative
|
|
2651
|
+
* path than {@link retryWithProof}. The buyer SIGNS an EIP-3009 authorization ONCE
|
|
2652
|
+
* (the driver's `payExact`) and the server / merchant-chosen facilitator BROADCASTS
|
|
2653
|
+
* it synchronously, so a blind re-POST of a still-in-flight authorization could
|
|
2654
|
+
* double-BROADCAST it. Hence, unlike the onchain-proof loop:
|
|
2655
|
+
*
|
|
2656
|
+
* • sign exactly once — reuse the SAME header on every retry, never re-sign;
|
|
2657
|
+
* • retry ONLY an explicit 402 (a definitive pre-broadcast rejection), bounded
|
|
2658
|
+
* well under `maxTimeoutSeconds` so the loop can't outlive the authorization;
|
|
2659
|
+
* • a post-POST transport error/timeout → {@link PaymentTimeoutError} carrying the
|
|
2660
|
+
* nonce (the facilitator MAY have settled — verify on-chain, NEVER re-pay);
|
|
2661
|
+
* • a 5xx → return as-is (server settle failure; the authorization stays valid +
|
|
2662
|
+
* its nonce unused) — no settled event, no spend;
|
|
2663
|
+
* • a 200 whose SettleResponse says `success:false` → a rejection, NEVER a spend;
|
|
2664
|
+
* • the spend is recorded EXACTLY ONCE, on an affirmative settlement only.
|
|
2665
|
+
*/
|
|
2666
|
+
async payExactRail(net, wallet, accept, url, init, quote) {
|
|
2667
|
+
if (!net.payExact) {
|
|
2668
|
+
throw new UnsupportedSchemeError(
|
|
2669
|
+
`the ${net.family} family can't pay a standard 'exact' rail (EVM + EIP-3009 only).`
|
|
2670
|
+
);
|
|
2671
|
+
}
|
|
2672
|
+
throwIfAborted(init?.signal);
|
|
2673
|
+
const { payload, accepted, payerFrom, nonce } = await net.payExact(wallet, accept);
|
|
2674
|
+
const headers = new Headers(init?.headers);
|
|
2675
|
+
headers.set(HEADER_SIGNATURE, buildExactSignatureHeader({ accepted, payload }));
|
|
2676
|
+
const rejectDefinitive = (why2) => {
|
|
2677
|
+
this.safeEmit({ kind: "payment-failed", reason: `exact: facilitator rejected nonce=${nonce} (${why2})` });
|
|
2678
|
+
throw new MaxRetriesExceededError(
|
|
2679
|
+
`exact: the facilitator rejected the payment (${why2}). Fix the cause, then re-present the SAME signed authorization (nonce=${nonce}) \u2014 do NOT re-sign a fresh nonce. ref=${nonce}.`,
|
|
2680
|
+
{ ref: nonce }
|
|
2681
|
+
);
|
|
2682
|
+
};
|
|
2683
|
+
const deadline = Date.now() + Math.max(1, Math.floor(accept.maxTimeoutSeconds / 2)) * 1e3;
|
|
2684
|
+
const maxAttempts = Math.min(this.maxRetries, 3);
|
|
2685
|
+
let lastReason = null;
|
|
2686
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
2687
|
+
if (attempt > 0) {
|
|
2688
|
+
if (Date.now() >= deadline) break;
|
|
2689
|
+
await new Promise((r) => setTimeout(r, Math.min(2e3, 400 * 2 ** (attempt - 1))));
|
|
2690
|
+
}
|
|
2691
|
+
throwIfAborted(init?.signal);
|
|
2692
|
+
const budget = Math.min(this.retryTimeoutMs, deadline - Date.now());
|
|
2693
|
+
if (budget <= 0) break;
|
|
2694
|
+
const timeoutController = new AbortController();
|
|
2695
|
+
const timeoutId = setTimeout(() => timeoutController.abort(), budget);
|
|
2696
|
+
const signal = init?.signal && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, init.signal]) : timeoutController.signal;
|
|
2697
|
+
let response;
|
|
2698
|
+
try {
|
|
2699
|
+
response = await fetch(url, { ...init ?? {}, headers, signal });
|
|
2700
|
+
} catch (err) {
|
|
2701
|
+
throw new PaymentTimeoutError(
|
|
2702
|
+
`exact: no response after submitting the authorization (nonce=${nonce}) to ${hostOf2(url)}. The facilitator may have already settled it \u2014 verify on-chain with authorizationState(${payerFrom}, ${nonce}) before re-presenting; do NOT re-pay.`,
|
|
2703
|
+
{ cause: err, ref: nonce }
|
|
2704
|
+
);
|
|
2705
|
+
} finally {
|
|
2706
|
+
clearTimeout(timeoutId);
|
|
2707
|
+
}
|
|
2708
|
+
const settle = parseSettleResponse(response);
|
|
2709
|
+
if (response.status === 402) {
|
|
2710
|
+
if (settle && settle.success === false) rejectDefinitive(settle.errorReason ?? "the facilitator reported success:false");
|
|
2711
|
+
lastReason = await readInvalidReason(response) ?? lastReason;
|
|
2712
|
+
continue;
|
|
2713
|
+
}
|
|
2714
|
+
if (response.ok && !(settle && settle.success === false)) {
|
|
2715
|
+
const receipt = parseReceipt(response);
|
|
2716
|
+
this.safeEmit({ kind: "payment-settled", receipt, ...settle ? { settle } : {} });
|
|
2717
|
+
const ref = settle?.transaction || receipt?.transaction || `eip3009-nonce:${nonce}`;
|
|
2718
|
+
this.recordSpend(quote, ref);
|
|
2719
|
+
return response;
|
|
2720
|
+
}
|
|
2721
|
+
if (response.status >= 500) {
|
|
2722
|
+
this.safeEmit({ kind: "payment-failed", reason: `exact: server ${response.status} \u2014 authorization nonce=${nonce} not settled` });
|
|
2723
|
+
return response;
|
|
2724
|
+
}
|
|
2725
|
+
if (settle && settle.success === false) rejectDefinitive(settle.errorReason ?? "the facilitator reported success:false");
|
|
2726
|
+
this.safeEmit({ kind: "payment-failed", reason: `exact: server ${response.status} \u2014 authorization nonce=${nonce} not settled` });
|
|
2727
|
+
return response;
|
|
2728
|
+
}
|
|
2729
|
+
const why = lastReason ? `${lastReason.error}${lastReason.detail ? ` \u2014 ${lastReason.detail}` : ""}` : "server gave no reason";
|
|
2730
|
+
this.safeEmit({
|
|
2731
|
+
kind: "payment-failed",
|
|
2732
|
+
reason: `exact: 402 after submitting authorization nonce=${nonce} (${why})`
|
|
2733
|
+
});
|
|
2734
|
+
throw new MaxRetriesExceededError(
|
|
2735
|
+
`exact: server still returned 402 after submitting the signed authorization (nonce=${nonce}). Last rejection: ${why}. Re-present the SAME authorization \u2014 do NOT re-sign a fresh nonce; verify authorizationState(${payerFrom}, ${nonce}) first. ref=${nonce}.`,
|
|
2736
|
+
{ ref: nonce }
|
|
2737
|
+
);
|
|
2738
|
+
}
|
|
2503
2739
|
};
|
|
2740
|
+
function throwIfAborted(signal) {
|
|
2741
|
+
if (signal?.aborted) {
|
|
2742
|
+
throw signal.reason ?? new DOMException("This operation was aborted.", "AbortError");
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2504
2745
|
function safeBig(s) {
|
|
2505
2746
|
try {
|
|
2506
2747
|
return BigInt(s);
|
|
@@ -2601,8 +2842,16 @@ async function readInvalidReason(response) {
|
|
|
2601
2842
|
detail: typeof body.detail === "string" ? body.detail : ""
|
|
2602
2843
|
};
|
|
2603
2844
|
}
|
|
2845
|
+
if (body && body.isValid === false && typeof body.invalidReason === "string") {
|
|
2846
|
+
return {
|
|
2847
|
+
error: body.invalidReason,
|
|
2848
|
+
detail: typeof body.invalidMessage === "string" ? body.invalidMessage : ""
|
|
2849
|
+
};
|
|
2850
|
+
}
|
|
2604
2851
|
} catch {
|
|
2605
2852
|
}
|
|
2853
|
+
const settle = parseSettleResponse(response);
|
|
2854
|
+
if (settle?.errorReason) return { error: settle.errorReason, detail: "" };
|
|
2606
2855
|
return null;
|
|
2607
2856
|
}
|
|
2608
2857
|
|
|
@@ -2731,17 +2980,17 @@ function paymentTools(client) {
|
|
|
2731
2980
|
},
|
|
2732
2981
|
{
|
|
2733
2982
|
name: "piprail_pay_request",
|
|
2734
|
-
description: "Fetch an x402 payment-gated URL, automatically
|
|
2983
|
+
description: "Fetch an x402 payment-gated URL, automatically making the required payment if needed (subject to the spend policy + approval hook). Pays whichever rail the client is configured for \u2014 PipRail's backendless on-chain rail, or, when enabled, the standard `exact` rail (where the buyer signs and the server settles, so no buyer gas). Returns the HTTP status, the response body, and a payment receipt if one settled. If the payment is refused by policy or the approval hook, returns { declined: true, reason } \u2014 no funds moved.",
|
|
2735
2984
|
annotations: {
|
|
2736
2985
|
title: "Pay an x402 request",
|
|
2737
2986
|
readOnlyHint: false,
|
|
2738
2987
|
// this is the one tool that MOVES FUNDS
|
|
2739
2988
|
destructiveHint: true,
|
|
2740
|
-
//
|
|
2989
|
+
// a payment is value-moving and not reversible
|
|
2741
2990
|
idempotentHint: false,
|
|
2742
2991
|
// paying twice = two payments
|
|
2743
2992
|
openWorldHint: true
|
|
2744
|
-
// fetches a URL and settles
|
|
2993
|
+
// fetches a URL and settles a payment
|
|
2745
2994
|
},
|
|
2746
2995
|
parameters: {
|
|
2747
2996
|
type: "object",
|
|
@@ -3283,7 +3532,9 @@ function createPaymentGate(options) {
|
|
|
3283
3532
|
amount: accept.amount,
|
|
3284
3533
|
payTo: accept.payTo,
|
|
3285
3534
|
maxTimeoutSeconds: accept.maxTimeoutSeconds,
|
|
3286
|
-
|
|
3535
|
+
// name/version are OPTIONAL on the wire type (a foreign rail may omit them), but the
|
|
3536
|
+
// gate's OWN exact rail always read them on-chain at resolution — so they're present here.
|
|
3537
|
+
extra: { name: accept.extra.name ?? "", version: accept.extra.version ?? "" }
|
|
3287
3538
|
},
|
|
3288
3539
|
receipt: { network: accept.network, asset: accept.asset, payTo: accept.payTo, amount: accept.amount },
|
|
3289
3540
|
payerHint: exact.payload.authorization.from
|
|
@@ -3377,11 +3628,13 @@ export {
|
|
|
3377
3628
|
SettlementError,
|
|
3378
3629
|
UnknownTokenError,
|
|
3379
3630
|
UnsupportedNetworkError,
|
|
3631
|
+
UnsupportedSchemeError,
|
|
3380
3632
|
WrongChainError,
|
|
3381
3633
|
WrongFamilyError,
|
|
3382
3634
|
buildBazaarExtension,
|
|
3383
3635
|
buildChallengeHeader,
|
|
3384
3636
|
buildExactAuthorization,
|
|
3637
|
+
buildExactSignatureHeader,
|
|
3385
3638
|
buildOpenApi,
|
|
3386
3639
|
buildReceiptHeader,
|
|
3387
3640
|
buildSignatureHeader,
|
|
@@ -3400,6 +3653,7 @@ export {
|
|
|
3400
3653
|
parseExactPaymentHeader,
|
|
3401
3654
|
parseExactRequirements,
|
|
3402
3655
|
parseReceipt,
|
|
3656
|
+
parseSettleResponse,
|
|
3403
3657
|
parseSignatureHeader,
|
|
3404
3658
|
paymentTools,
|
|
3405
3659
|
pickAccept,
|