@piprail/sdk 1.4.0 → 1.5.1
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 +2 -2
- package/CHANGELOG.md +65 -0
- package/ERRORS.md +15 -3
- package/README.md +38 -4
- package/dist/{algorand-XJ5OVWQB.js → algorand-B67G4335.js} +34 -0
- package/dist/{algorand-IDFUG5CI.cjs → algorand-IJJKE35X.cjs} +38 -4
- package/dist/{aptos-TPSOQ2VL.cjs → aptos-X3G2UBYW.cjs} +28 -0
- package/dist/{aptos-AWWSCPDH.js → aptos-YQWTGFRZ.js} +28 -0
- package/dist/index.cjs +307 -29
- package/dist/index.d.cts +174 -15
- package/dist/index.d.ts +174 -15
- package/dist/index.js +295 -17
- package/dist/{near-NOJTO4GX.js → near-7MBBCDUE.js} +50 -0
- package/dist/{near-KDA5DPTX.cjs → near-GGUHLXAF.cjs} +57 -7
- package/dist/{solana-HNRTS4KM.js → solana-7WJVZGDW.js} +22 -0
- package/dist/{solana-DVA6I55L.cjs → solana-W24TCJV4.cjs} +25 -3
- package/dist/{stellar-4TDVVJYO.js → stellar-HV6VGZX3.js} +50 -0
- package/dist/{stellar-4D5EWT3V.cjs → stellar-YMY3K2YB.cjs} +50 -0
- package/dist/{sui-ALUTM5GX.js → sui-2WFWVFJX.js} +23 -0
- package/dist/{sui-5HMIHOZK.cjs → sui-32KVESR5.cjs} +23 -0
- package/dist/{ton-3XMIM2FU.js → ton-DGZB7W4U.js} +23 -0
- package/dist/{ton-TVK4TEDX.cjs → ton-FIQGV2LC.cjs} +23 -0
- package/dist/{tron-6D65YJEU.js → tron-RLIL2FDI.js} +28 -0
- package/dist/{tron-Y5RZJZRT.cjs → tron-ZSXAPZ2C.cjs} +28 -0
- package/dist/{xrpl-ICO6G7UK.cjs → xrpl-2PKP7HOI.cjs} +61 -1
- package/dist/{xrpl-ISFG3SSN.js → xrpl-UEC2GYVV.js} +60 -0
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -716,6 +716,27 @@ function makeEvmNetwork(resolved) {
|
|
|
716
716
|
});
|
|
717
717
|
}
|
|
718
718
|
},
|
|
719
|
+
async balanceOf(wallet, asset) {
|
|
720
|
+
const owner = wallet._native.account.address;
|
|
721
|
+
const native = await publicClient.getBalance({ address: owner }).catch(() => null);
|
|
722
|
+
if (asset === "native") return { token: native, native };
|
|
723
|
+
let token = null;
|
|
724
|
+
try {
|
|
725
|
+
token = await publicClient.readContract({
|
|
726
|
+
address: _viem.getAddress.call(void 0, asset),
|
|
727
|
+
abi: _viem.erc20Abi,
|
|
728
|
+
functionName: "balanceOf",
|
|
729
|
+
args: [owner]
|
|
730
|
+
});
|
|
731
|
+
} catch (e11) {
|
|
732
|
+
token = null;
|
|
733
|
+
}
|
|
734
|
+
return { token, native };
|
|
735
|
+
},
|
|
736
|
+
// No receive prerequisite — any 0x address receives native or ERC-20 immediately.
|
|
737
|
+
async recipientReady() {
|
|
738
|
+
return { ready: "n/a" };
|
|
739
|
+
},
|
|
719
740
|
async verify(ref, accept) {
|
|
720
741
|
return verifyEvm({
|
|
721
742
|
publicClient,
|
|
@@ -739,7 +760,7 @@ var loaders = {
|
|
|
739
760
|
solana: async () => {
|
|
740
761
|
let mod;
|
|
741
762
|
try {
|
|
742
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./solana-
|
|
763
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./solana-W24TCJV4.cjs")));
|
|
743
764
|
} catch (cause) {
|
|
744
765
|
throw new (0, _chunkIQGT65WScjs.MissingDriverError)(
|
|
745
766
|
`Solana selected, but its packages aren't installed. Run: npm install @solana/web3.js @solana/spl-token bs58`,
|
|
@@ -751,7 +772,7 @@ var loaders = {
|
|
|
751
772
|
ton: async () => {
|
|
752
773
|
let mod;
|
|
753
774
|
try {
|
|
754
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./ton-
|
|
775
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./ton-FIQGV2LC.cjs")));
|
|
755
776
|
} catch (cause) {
|
|
756
777
|
throw new (0, _chunkIQGT65WScjs.MissingDriverError)(
|
|
757
778
|
`TON selected, but its packages aren't installed. Run: npm install @ton/ton @ton/core @ton/crypto`,
|
|
@@ -763,7 +784,7 @@ var loaders = {
|
|
|
763
784
|
stellar: async () => {
|
|
764
785
|
let mod;
|
|
765
786
|
try {
|
|
766
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./stellar-
|
|
787
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./stellar-YMY3K2YB.cjs")));
|
|
767
788
|
} catch (cause) {
|
|
768
789
|
throw new (0, _chunkIQGT65WScjs.MissingDriverError)(
|
|
769
790
|
`Stellar selected, but its package isn't installed. Run: npm install @stellar/stellar-sdk`,
|
|
@@ -775,7 +796,7 @@ var loaders = {
|
|
|
775
796
|
xrpl: async () => {
|
|
776
797
|
let mod;
|
|
777
798
|
try {
|
|
778
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./xrpl-
|
|
799
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./xrpl-2PKP7HOI.cjs")));
|
|
779
800
|
} catch (cause) {
|
|
780
801
|
throw new (0, _chunkIQGT65WScjs.MissingDriverError)(
|
|
781
802
|
`XRPL selected, but its package isn't installed. Run: npm install xrpl`,
|
|
@@ -787,7 +808,7 @@ var loaders = {
|
|
|
787
808
|
tron: async () => {
|
|
788
809
|
let mod;
|
|
789
810
|
try {
|
|
790
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./tron-
|
|
811
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./tron-ZSXAPZ2C.cjs")));
|
|
791
812
|
} catch (cause) {
|
|
792
813
|
throw new (0, _chunkIQGT65WScjs.MissingDriverError)(
|
|
793
814
|
`Tron selected, but its package isn't installed. Run: npm install tronweb`,
|
|
@@ -799,7 +820,7 @@ var loaders = {
|
|
|
799
820
|
sui: async () => {
|
|
800
821
|
let mod;
|
|
801
822
|
try {
|
|
802
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./sui-
|
|
823
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./sui-32KVESR5.cjs")));
|
|
803
824
|
} catch (cause) {
|
|
804
825
|
throw new (0, _chunkIQGT65WScjs.MissingDriverError)(
|
|
805
826
|
`Sui selected, but its package isn't installed. Run: npm install @mysten/sui`,
|
|
@@ -811,7 +832,7 @@ var loaders = {
|
|
|
811
832
|
near: async () => {
|
|
812
833
|
let mod;
|
|
813
834
|
try {
|
|
814
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./near-
|
|
835
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./near-GGUHLXAF.cjs")));
|
|
815
836
|
} catch (cause) {
|
|
816
837
|
throw new (0, _chunkIQGT65WScjs.MissingDriverError)(
|
|
817
838
|
`NEAR selected, but its package isn't installed. Run: npm install near-api-js`,
|
|
@@ -823,7 +844,7 @@ var loaders = {
|
|
|
823
844
|
aptos: async () => {
|
|
824
845
|
let mod;
|
|
825
846
|
try {
|
|
826
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./aptos-
|
|
847
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./aptos-X3G2UBYW.cjs")));
|
|
827
848
|
} catch (cause) {
|
|
828
849
|
throw new (0, _chunkIQGT65WScjs.MissingDriverError)(
|
|
829
850
|
`Aptos selected, but its package isn't installed. Run: npm install @aptos-labs/ts-sdk`,
|
|
@@ -835,7 +856,7 @@ var loaders = {
|
|
|
835
856
|
algorand: async () => {
|
|
836
857
|
let mod;
|
|
837
858
|
try {
|
|
838
|
-
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./algorand-
|
|
859
|
+
mod = await Promise.resolve().then(() => _interopRequireWildcard(require("./algorand-IJJKE35X.cjs")));
|
|
839
860
|
} catch (cause) {
|
|
840
861
|
throw new (0, _chunkIQGT65WScjs.MissingDriverError)(
|
|
841
862
|
`Algorand selected, but its package isn't installed. Run: npm install algosdk`,
|
|
@@ -976,6 +997,12 @@ var SpendLedger = (_class = class {constructor() { _class.prototype.__init.call(
|
|
|
976
997
|
}, _class);
|
|
977
998
|
|
|
978
999
|
// src/client.ts
|
|
1000
|
+
var RECIPIENT_FIX = {
|
|
1001
|
+
NO_TRUSTLINE: "the recipient needs a one-time trustline for this asset before it can receive",
|
|
1002
|
+
NOT_REGISTERED: "the recipient must be storage_deposit-registered on this token (NEP-145, one-time)",
|
|
1003
|
+
NOT_OPTED_IN: "the recipient must opt into this asset once (a 0-amount self-transfer)",
|
|
1004
|
+
INACTIVE: "the recipient account doesn't exist yet \u2014 fund it with the chain's base reserve to activate it"
|
|
1005
|
+
};
|
|
979
1006
|
var PipRailClient = (_class2 = class {
|
|
980
1007
|
|
|
981
1008
|
|
|
@@ -998,7 +1025,7 @@ var PipRailClient = (_class2 = class {
|
|
|
998
1025
|
safeEmit(event) {
|
|
999
1026
|
try {
|
|
1000
1027
|
this.onEvent(event);
|
|
1001
|
-
} catch (
|
|
1028
|
+
} catch (e12) {
|
|
1002
1029
|
}
|
|
1003
1030
|
}
|
|
1004
1031
|
/** Auto-mount the chain's driver, resolve the network, and bind the wallet — once. */
|
|
@@ -1084,6 +1111,44 @@ var PipRailClient = (_class2 = class {
|
|
|
1084
1111
|
spent() {
|
|
1085
1112
|
return this.ledger.summary();
|
|
1086
1113
|
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Plan a payment for a gated URL — WITHOUT paying. The read-only completion of
|
|
1116
|
+
* the `quote()` → `estimateCost()` → **`planPayment()`** trio: it surveys every
|
|
1117
|
+
* rail the 402 offers on this client's chain against the wallet's OWN holdings —
|
|
1118
|
+
* token balance, native-coin gas, and recipient-readiness (trustline / ATA /
|
|
1119
|
+
* storage_deposit / ASA opt-in) — and returns, crystal-clear:
|
|
1120
|
+
* - `payable` + `best` — the cheapest rail the wallet can actually settle
|
|
1121
|
+
* - `options[]` — every rail with typed `blockers` + soft `warnings`
|
|
1122
|
+
* - `fundingHint` — one human sentence on exactly what to top up
|
|
1123
|
+
*
|
|
1124
|
+
* NEVER throws for a read problem (a transient/RPC failure surfaces as a rail in
|
|
1125
|
+
* `state: 'unknown'` + a warning, never a false "unaffordable"); returns `null`
|
|
1126
|
+
* when the URL isn't payment-gated (no 402); and when the 402 offers no rail on
|
|
1127
|
+
* this client's chain it EXPLAINS that (status `blocked` + a hint), rather than
|
|
1128
|
+
* throwing. Throws `InvalidEnvelopeError` only on an unparseable challenge.
|
|
1129
|
+
*
|
|
1130
|
+
* Then pay the chosen rail with `fetch(url, { autoRoute: true })`, or branch on
|
|
1131
|
+
* the plan yourself. No funds move.
|
|
1132
|
+
*/
|
|
1133
|
+
async planPayment(url, init) {
|
|
1134
|
+
const res = await fetch(url, { ..._nullishCoalesce(init, () => ( {})), method: _nullishCoalesce(_optionalChain([init, 'optionalAccess', _9 => _9.method]), () => ( "GET")) });
|
|
1135
|
+
if (res.status !== 402) return null;
|
|
1136
|
+
const challenge = await parseChallenge(res);
|
|
1137
|
+
if (!challenge) {
|
|
1138
|
+
throw new (0, _chunkIQGT65WScjs.InvalidEnvelopeError)("402 response did not include a parseable x402 challenge.");
|
|
1139
|
+
}
|
|
1140
|
+
const { net, wallet } = await this.ensure();
|
|
1141
|
+
return this.planFromChallenge(net, wallet, challenge, url);
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Convenience over {@link planPayment}: can the wallet settle this URL right now?
|
|
1145
|
+
* `true` when at least one rail is payable — or when the URL isn't gated (a free
|
|
1146
|
+
* resource is trivially "affordable"). No funds move.
|
|
1147
|
+
*/
|
|
1148
|
+
async canAfford(url, init) {
|
|
1149
|
+
const plan = await this.planPayment(url, init);
|
|
1150
|
+
return plan == null ? true : plan.payable;
|
|
1151
|
+
}
|
|
1087
1152
|
/**
|
|
1088
1153
|
* Lower-level: drive any HTTP method through the 402 flow.
|
|
1089
1154
|
*
|
|
@@ -1092,7 +1157,7 @@ var PipRailClient = (_class2 = class {
|
|
|
1092
1157
|
* streams throw `NonReplayableBodyError`.
|
|
1093
1158
|
*/
|
|
1094
1159
|
async fetch(url, init) {
|
|
1095
|
-
const body = _optionalChain([init, 'optionalAccess',
|
|
1160
|
+
const body = _optionalChain([init, 'optionalAccess', _10 => _10.body]);
|
|
1096
1161
|
if (body !== void 0 && body !== null && !isReplayableBodyInit(body)) {
|
|
1097
1162
|
throw new (0, _chunkIQGT65WScjs.NonReplayableBodyError)(
|
|
1098
1163
|
"fetch(): init.body is not replayable. Pass a string, FormData, URLSearchParams, ArrayBuffer, or Blob \u2014 not a ReadableStream."
|
|
@@ -1100,10 +1165,19 @@ var PipRailClient = (_class2 = class {
|
|
|
1100
1165
|
}
|
|
1101
1166
|
const firstResponse = await fetch(url, init);
|
|
1102
1167
|
if (firstResponse.status !== 402) return firstResponse;
|
|
1103
|
-
const
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1168
|
+
const resolved = await this.resolveChallenge(url, firstResponse);
|
|
1169
|
+
const { net, wallet, challenge } = resolved;
|
|
1170
|
+
let accept = resolved.accept;
|
|
1171
|
+
let quote = resolved.quote;
|
|
1172
|
+
const autoRoute = _nullishCoalesce(_nullishCoalesce(_optionalChain([init, 'optionalAccess', _11 => _11.autoRoute]), () => ( this.opts.autoRoute)), () => ( false));
|
|
1173
|
+
if (autoRoute) {
|
|
1174
|
+
const plan = await this.planFromChallenge(net, wallet, challenge, url);
|
|
1175
|
+
if (!plan.best) {
|
|
1176
|
+
throw new (0, _chunkIQGT65WScjs.PaymentDeclinedError)(_nullishCoalesce(plan.fundingHint, () => ( "No rail is settleable for this payment.")));
|
|
1177
|
+
}
|
|
1178
|
+
accept = plan.best.accept;
|
|
1179
|
+
quote = plan.best.quote;
|
|
1180
|
+
}
|
|
1107
1181
|
this.safeEmit({ kind: "payment-required", challenge, accept });
|
|
1108
1182
|
await this.authorize(quote);
|
|
1109
1183
|
const { ref, confirmed } = await this.payAndConfirm(net, wallet, accept);
|
|
@@ -1125,9 +1199,7 @@ var PipRailClient = (_class2 = class {
|
|
|
1125
1199
|
);
|
|
1126
1200
|
}
|
|
1127
1201
|
const { net, wallet } = await this.ensure();
|
|
1128
|
-
const candidates =
|
|
1129
|
-
(a) => a.scheme === "onchain-proof" && net.supports(a.network)
|
|
1130
|
-
);
|
|
1202
|
+
const candidates = this.gatherCandidates(net, challenge);
|
|
1131
1203
|
if (candidates.length === 0) {
|
|
1132
1204
|
const networks = challenge.accepts.map((a) => a.network).join(", ");
|
|
1133
1205
|
throw new (0, _chunkIQGT65WScjs.NoCompatibleAcceptError)(
|
|
@@ -1141,6 +1213,111 @@ var PipRailClient = (_class2 = class {
|
|
|
1141
1213
|
const chosen = _nullishCoalesce(priced.find((p) => p.quote.withinPolicy), () => ( priced[0]));
|
|
1142
1214
|
return { net, wallet, accept: chosen.accept, challenge, quote: chosen.quote };
|
|
1143
1215
|
}
|
|
1216
|
+
/** The candidate accepts this client could pay: our scheme, on the bound network. */
|
|
1217
|
+
gatherCandidates(net, challenge) {
|
|
1218
|
+
return challenge.accepts.filter(
|
|
1219
|
+
(a) => a.scheme === "onchain-proof" && net.supports(a.network)
|
|
1220
|
+
);
|
|
1221
|
+
}
|
|
1222
|
+
/** Build the full {@link PaymentPlan} from an already-parsed challenge + bound
|
|
1223
|
+
* net/wallet. Shared by `planPayment` (read-only) and `fetch`'s autoRoute. */
|
|
1224
|
+
async planFromChallenge(net, wallet, challenge, url) {
|
|
1225
|
+
const chainLabel = typeof this.opts.chain === "string" ? this.opts.chain : net.network;
|
|
1226
|
+
const candidates = this.gatherCandidates(net, challenge);
|
|
1227
|
+
if (candidates.length === 0) {
|
|
1228
|
+
const offered = [...new Set(challenge.accepts.map((a) => a.network))].join(", ") || "none";
|
|
1229
|
+
return {
|
|
1230
|
+
url,
|
|
1231
|
+
network: net.network,
|
|
1232
|
+
status: "blocked",
|
|
1233
|
+
payable: false,
|
|
1234
|
+
best: null,
|
|
1235
|
+
options: [],
|
|
1236
|
+
fundingHint: `This 402 isn't offered on your chain (${chainLabel}); it's payable on: ${offered}.`
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1239
|
+
const analysed = await Promise.all(
|
|
1240
|
+
candidates.map(
|
|
1241
|
+
(accept) => this.analyzeRail(net, wallet, accept, url, challenge.resource.description)
|
|
1242
|
+
)
|
|
1243
|
+
);
|
|
1244
|
+
const options = rankOptions(analysed);
|
|
1245
|
+
const best = _nullishCoalesce(options.find((o) => o.state === "payable"), () => ( null));
|
|
1246
|
+
const status = best ? "ready" : options.some((o) => o.state === "unknown") ? "unknown" : "blocked";
|
|
1247
|
+
return {
|
|
1248
|
+
url,
|
|
1249
|
+
network: net.network,
|
|
1250
|
+
status,
|
|
1251
|
+
payable: best !== null,
|
|
1252
|
+
best,
|
|
1253
|
+
options,
|
|
1254
|
+
fundingHint: best ? null : buildFundingHint(options, chainLabel)
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
/** Analyse ONE rail against the wallet's holdings — quote (existing) + gas
|
|
1258
|
+
* (estimateCost, existing) + balanceOf + recipientReady → a {@link PayOption}. */
|
|
1259
|
+
async analyzeRail(net, wallet, accept, url, description) {
|
|
1260
|
+
const quote = this.buildQuote(net, accept, url, description);
|
|
1261
|
+
const cost = await net.estimateCost(accept);
|
|
1262
|
+
const bal = await net.balanceOf(wallet, accept.asset).catch(() => ({ token: null, native: null }));
|
|
1263
|
+
const rr = await net.recipientReady(accept.payTo, accept.asset).catch(() => ({ ready: "unknown" }));
|
|
1264
|
+
const amount = BigInt(accept.amount);
|
|
1265
|
+
const fee = safeBig(cost.fee);
|
|
1266
|
+
const isNative = accept.asset === "native";
|
|
1267
|
+
const blockers = [];
|
|
1268
|
+
const warnings = [];
|
|
1269
|
+
const shortfall = {};
|
|
1270
|
+
if (!quote.withinPolicy) blockers.push("OUTSIDE_POLICY");
|
|
1271
|
+
if (quote.symbolMismatch) warnings.push("SYMBOL_MISMATCH");
|
|
1272
|
+
if (cost.basis === "heuristic") warnings.push("GAS_HEURISTIC");
|
|
1273
|
+
const tokenKnown = bal.token != null;
|
|
1274
|
+
const nativeKnown = bal.native != null;
|
|
1275
|
+
if (!tokenKnown || !nativeKnown) warnings.push("BALANCE_UNREADABLE");
|
|
1276
|
+
if (isNative) {
|
|
1277
|
+
if (nativeKnown && bal.native < amount + fee) {
|
|
1278
|
+
blockers.push("INSUFFICIENT_TOKEN");
|
|
1279
|
+
shortfall.token = _chunkIQGT65WScjs.formatUnits.call(void 0, amount + fee - bal.native, quote.decimals);
|
|
1280
|
+
}
|
|
1281
|
+
} else {
|
|
1282
|
+
if (tokenKnown && bal.token < amount) {
|
|
1283
|
+
blockers.push("INSUFFICIENT_TOKEN");
|
|
1284
|
+
shortfall.token = _chunkIQGT65WScjs.formatUnits.call(void 0, amount - bal.token, quote.decimals);
|
|
1285
|
+
}
|
|
1286
|
+
if (nativeKnown && bal.native < fee) {
|
|
1287
|
+
blockers.push("INSUFFICIENT_GAS");
|
|
1288
|
+
shortfall.native = _chunkIQGT65WScjs.formatUnits.call(void 0, fee - bal.native, cost.feeDecimals);
|
|
1289
|
+
} else if (nativeKnown && fee > 0n && bal.native < fee * 3n / 2n) {
|
|
1290
|
+
warnings.push("THIN_GAS_MARGIN");
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
let recipient;
|
|
1294
|
+
if (rr.ready === false) {
|
|
1295
|
+
blockers.push("RECIPIENT_NOT_READY");
|
|
1296
|
+
recipient = rr.reason ? { ready: false, reason: rr.reason, fix: RECIPIENT_FIX[rr.reason] } : { ready: false };
|
|
1297
|
+
} else if (rr.ready === "unknown") {
|
|
1298
|
+
warnings.push("RECIPIENT_READINESS_UNKNOWN");
|
|
1299
|
+
recipient = { ready: "unknown" };
|
|
1300
|
+
} else {
|
|
1301
|
+
recipient = { ready: rr.ready };
|
|
1302
|
+
}
|
|
1303
|
+
const unreadable = isNative ? !nativeKnown : !tokenKnown || !nativeKnown;
|
|
1304
|
+
const state = blockers.length ? "blocked" : unreadable || rr.ready === "unknown" ? "unknown" : "payable";
|
|
1305
|
+
return {
|
|
1306
|
+
accept,
|
|
1307
|
+
quote,
|
|
1308
|
+
cost,
|
|
1309
|
+
state,
|
|
1310
|
+
blockers,
|
|
1311
|
+
warnings,
|
|
1312
|
+
balance: {
|
|
1313
|
+
token: bal.token != null ? _chunkIQGT65WScjs.formatUnits.call(void 0, bal.token, quote.decimals) : null,
|
|
1314
|
+
native: bal.native != null ? _chunkIQGT65WScjs.formatUnits.call(void 0, bal.native, cost.feeDecimals) : null
|
|
1315
|
+
},
|
|
1316
|
+
need: { token: quote.amountFormatted, native: cost.feeFormatted },
|
|
1317
|
+
...shortfall.token || shortfall.native ? { shortfall } : {},
|
|
1318
|
+
recipient
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1144
1321
|
/** Build the agent-facing quote for an accept: TRUE decimals/symbol (via the
|
|
1145
1322
|
* driver's describeAsset) + the policy verdict + a symbol-mismatch flag. */
|
|
1146
1323
|
buildQuote(net, accept, url, description) {
|
|
@@ -1151,8 +1328,8 @@ var PipRailClient = (_class2 = class {
|
|
|
1151
1328
|
}
|
|
1152
1329
|
const amountBase = BigInt(accept.amount);
|
|
1153
1330
|
const described = net.describeAsset(accept.asset);
|
|
1154
|
-
const decimals = _nullishCoalesce(_optionalChain([described, 'optionalAccess',
|
|
1155
|
-
const symbol = _nullishCoalesce(_optionalChain([described, 'optionalAccess',
|
|
1331
|
+
const decimals = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _12 => _12.decimals]), () => ( accept.extra.decimals));
|
|
1332
|
+
const symbol = _nullishCoalesce(_optionalChain([described, 'optionalAccess', _13 => _13.symbol]), () => ( accept.extra.symbol));
|
|
1156
1333
|
const amountFormatted = _chunkIQGT65WScjs.formatUnits.call(void 0, amountBase, decimals);
|
|
1157
1334
|
const intent = {
|
|
1158
1335
|
host: hostOf(url),
|
|
@@ -1261,7 +1438,7 @@ var PipRailClient = (_class2 = class {
|
|
|
1261
1438
|
accepted: accept,
|
|
1262
1439
|
payload: { nonce: accept.extra.nonce, txHash: ref }
|
|
1263
1440
|
};
|
|
1264
|
-
const headers = new Headers(_optionalChain([originalInit, 'optionalAccess',
|
|
1441
|
+
const headers = new Headers(_optionalChain([originalInit, 'optionalAccess', _14 => _14.headers]));
|
|
1265
1442
|
headers.set(HEADER_SIGNATURE, buildSignatureHeader(signature));
|
|
1266
1443
|
let lastResponse = null;
|
|
1267
1444
|
let lastReason = null;
|
|
@@ -1276,7 +1453,7 @@ var PipRailClient = (_class2 = class {
|
|
|
1276
1453
|
() => timeoutController.abort(),
|
|
1277
1454
|
this.retryTimeoutMs
|
|
1278
1455
|
);
|
|
1279
|
-
const signal = _optionalChain([originalInit, 'optionalAccess',
|
|
1456
|
+
const signal = _optionalChain([originalInit, 'optionalAccess', _15 => _15.signal]) && typeof AbortSignal.any === "function" ? AbortSignal.any([timeoutController.signal, originalInit.signal]) : timeoutController.signal;
|
|
1280
1457
|
try {
|
|
1281
1458
|
lastResponse = await fetch(url, {
|
|
1282
1459
|
..._nullishCoalesce(originalInit, () => ( {})),
|
|
@@ -1313,10 +1490,72 @@ var PipRailClient = (_class2 = class {
|
|
|
1313
1490
|
);
|
|
1314
1491
|
}
|
|
1315
1492
|
}, _class2);
|
|
1493
|
+
function safeBig(s) {
|
|
1494
|
+
try {
|
|
1495
|
+
return BigInt(s);
|
|
1496
|
+
} catch (e13) {
|
|
1497
|
+
return 0n;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
function shortAddr(a) {
|
|
1501
|
+
return a.length > 14 ? `${a.slice(0, 8)}\u2026${a.slice(-4)}` : a;
|
|
1502
|
+
}
|
|
1503
|
+
function rankOptions(options) {
|
|
1504
|
+
const rank = { payable: 0, unknown: 1, blocked: 2 };
|
|
1505
|
+
return [...options].sort((a, b) => {
|
|
1506
|
+
if (rank[a.state] !== rank[b.state]) return rank[a.state] - rank[b.state];
|
|
1507
|
+
if (a.state === "payable") {
|
|
1508
|
+
const fa = safeBig(a.cost.fee);
|
|
1509
|
+
const fb = safeBig(b.cost.fee);
|
|
1510
|
+
if (fa !== fb) return fa < fb ? -1 : 1;
|
|
1511
|
+
}
|
|
1512
|
+
return 0;
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
function buildFundingHint(options, chainLabel) {
|
|
1516
|
+
if (options.length === 0) return null;
|
|
1517
|
+
const target = [...options].sort((a, b) => a.blockers.length - b.blockers.length)[0];
|
|
1518
|
+
const sym = _nullishCoalesce(target.quote.symbol, () => ( "the token"));
|
|
1519
|
+
if (target.blockers.includes("RECIPIENT_NOT_READY")) {
|
|
1520
|
+
return `Recipient ${shortAddr(target.accept.payTo)} can't receive on ${chainLabel} yet \u2014 ${_nullishCoalesce(target.recipient.fix, () => ( "recipient not ready"))}.`;
|
|
1521
|
+
}
|
|
1522
|
+
if (target.blockers.includes("OUTSIDE_POLICY")) {
|
|
1523
|
+
return `Refused by spend policy: ${_nullishCoalesce(target.quote.policyReason, () => ( "not allowed"))}.`;
|
|
1524
|
+
}
|
|
1525
|
+
if (target.state === "unknown") {
|
|
1526
|
+
return `Couldn't fully read your wallet on ${chainLabel} (RPC throttled) \u2014 retry; you may already be able to pay ${target.quote.amountFormatted} ${sym}.`;
|
|
1527
|
+
}
|
|
1528
|
+
const parts = [];
|
|
1529
|
+
if (target.blockers.includes("INSUFFICIENT_TOKEN") && _optionalChain([target, 'access', _16 => _16.shortfall, 'optionalAccess', _17 => _17.token])) {
|
|
1530
|
+
parts.push(`top up ${target.shortfall.token} ${sym}`);
|
|
1531
|
+
}
|
|
1532
|
+
if (target.blockers.includes("INSUFFICIENT_GAS") && _optionalChain([target, 'access', _18 => _18.shortfall, 'optionalAccess', _19 => _19.native])) {
|
|
1533
|
+
parts.push(`add ~${target.shortfall.native} ${target.cost.feeSymbol} for gas`);
|
|
1534
|
+
}
|
|
1535
|
+
return parts.length ? `Can't settle on ${chainLabel}: ${parts.join(" and ")} (to pay ${target.quote.amountFormatted} ${sym}).` : `Can't settle on ${chainLabel} for ${target.quote.amountFormatted} ${sym}.`;
|
|
1536
|
+
}
|
|
1537
|
+
async function planAcross(clients, url, init) {
|
|
1538
|
+
const plans = await Promise.all(clients.map((c) => c.planPayment(url, init).catch(() => null)));
|
|
1539
|
+
const live = plans.filter((p) => p != null);
|
|
1540
|
+
if (live.length === 0) return null;
|
|
1541
|
+
const options = rankOptions(live.flatMap((p) => p.options));
|
|
1542
|
+
const best = _nullishCoalesce(options.find((o) => o.state === "payable"), () => ( null));
|
|
1543
|
+
const status = best ? "ready" : options.some((o) => o.state === "unknown") ? "unknown" : "blocked";
|
|
1544
|
+
return {
|
|
1545
|
+
url,
|
|
1546
|
+
network: _nullishCoalesce(_optionalChain([best, 'optionalAccess', _20 => _20.accept, 'access', _21 => _21.network]), () => ( live[0].network)),
|
|
1547
|
+
status,
|
|
1548
|
+
payable: best !== null,
|
|
1549
|
+
best,
|
|
1550
|
+
options,
|
|
1551
|
+
// First non-null hint across clients — each already names its chain.
|
|
1552
|
+
fundingHint: best ? null : _nullishCoalesce(live.map((p) => p.fundingHint).find(Boolean), () => ( null))
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
1316
1555
|
function hostOf(url) {
|
|
1317
1556
|
try {
|
|
1318
1557
|
return new URL(url).hostname;
|
|
1319
|
-
} catch (
|
|
1558
|
+
} catch (e14) {
|
|
1320
1559
|
return url;
|
|
1321
1560
|
}
|
|
1322
1561
|
}
|
|
@@ -1339,7 +1578,7 @@ async function readInvalidReason(response) {
|
|
|
1339
1578
|
detail: typeof body.detail === "string" ? body.detail : ""
|
|
1340
1579
|
};
|
|
1341
1580
|
}
|
|
1342
|
-
} catch (
|
|
1581
|
+
} catch (e15) {
|
|
1343
1582
|
}
|
|
1344
1583
|
return null;
|
|
1345
1584
|
}
|
|
@@ -1350,7 +1589,7 @@ async function readBody(res) {
|
|
|
1350
1589
|
if (!text) return null;
|
|
1351
1590
|
try {
|
|
1352
1591
|
return JSON.parse(text);
|
|
1353
|
-
} catch (
|
|
1592
|
+
} catch (e16) {
|
|
1354
1593
|
return text;
|
|
1355
1594
|
}
|
|
1356
1595
|
}
|
|
@@ -1372,6 +1611,44 @@ function paymentTools(client) {
|
|
|
1372
1611
|
return quote ? { gated: true, ...quote } : { gated: false, url: String(args.url) };
|
|
1373
1612
|
}
|
|
1374
1613
|
},
|
|
1614
|
+
{
|
|
1615
|
+
name: "piprail_plan_payment",
|
|
1616
|
+
description: "Check whether you CAN pay an x402-gated URL before paying. Reads your wallet balance, native gas, and whether the recipient can receive \u2014 across every rail the URL offers on your chain \u2014 and returns { gated, payable, best, options, fundingHint }. payable:false means do NOT attempt the payment; fundingHint says exactly what to top up. Call this before piprail_pay_request so you never commit to a payment you cannot finish. Returns { gated: false } when no payment is needed.",
|
|
1617
|
+
parameters: {
|
|
1618
|
+
type: "object",
|
|
1619
|
+
properties: {
|
|
1620
|
+
url: { type: "string", description: "Full URL of the gated resource." }
|
|
1621
|
+
},
|
|
1622
|
+
required: ["url"],
|
|
1623
|
+
additionalProperties: false
|
|
1624
|
+
},
|
|
1625
|
+
invoke: async (args) => {
|
|
1626
|
+
const plan = await client.planPayment(String(args.url));
|
|
1627
|
+
if (plan == null) return { gated: false, url: String(args.url) };
|
|
1628
|
+
return {
|
|
1629
|
+
gated: true,
|
|
1630
|
+
payable: plan.payable,
|
|
1631
|
+
status: plan.status,
|
|
1632
|
+
fundingHint: plan.fundingHint,
|
|
1633
|
+
best: plan.best ? {
|
|
1634
|
+
network: plan.best.accept.network,
|
|
1635
|
+
symbol: plan.best.quote.symbol,
|
|
1636
|
+
amount: plan.best.quote.amountFormatted,
|
|
1637
|
+
gasCoin: plan.best.cost.feeSymbol,
|
|
1638
|
+
gas: plan.best.cost.feeFormatted
|
|
1639
|
+
} : null,
|
|
1640
|
+
options: plan.options.map((o) => ({
|
|
1641
|
+
network: o.accept.network,
|
|
1642
|
+
symbol: o.quote.symbol,
|
|
1643
|
+
amount: o.quote.amountFormatted,
|
|
1644
|
+
state: o.state,
|
|
1645
|
+
blockers: o.blockers,
|
|
1646
|
+
warnings: o.warnings,
|
|
1647
|
+
recipientReady: o.recipient.ready
|
|
1648
|
+
}))
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
},
|
|
1375
1652
|
{
|
|
1376
1653
|
name: "piprail_pay_request",
|
|
1377
1654
|
description: "Fetch an x402 payment-gated URL, automatically paying the required on-chain payment if needed (subject to the spend policy + approval hook). 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.",
|
|
@@ -1565,7 +1842,7 @@ function createPaymentGate(options) {
|
|
|
1565
1842
|
if (options.onPaid) {
|
|
1566
1843
|
try {
|
|
1567
1844
|
options.onPaid(result.receipt);
|
|
1568
|
-
} catch (
|
|
1845
|
+
} catch (e17) {
|
|
1569
1846
|
}
|
|
1570
1847
|
}
|
|
1571
1848
|
return {
|
|
@@ -1672,8 +1949,8 @@ async function buildExactAuthorization(params) {
|
|
|
1672
1949
|
};
|
|
1673
1950
|
const signature = await account.signTypedData({
|
|
1674
1951
|
domain: {
|
|
1675
|
-
name: _nullishCoalesce(_optionalChain([accept, 'access',
|
|
1676
|
-
version: _nullishCoalesce(_optionalChain([accept, 'access',
|
|
1952
|
+
name: _nullishCoalesce(_optionalChain([accept, 'access', _22 => _22.extra, 'optionalAccess', _23 => _23.name]), () => ( "USD Coin")),
|
|
1953
|
+
version: _nullishCoalesce(_optionalChain([accept, 'access', _24 => _24.extra, 'optionalAccess', _25 => _25.version]), () => ( "2")),
|
|
1677
1954
|
chainId,
|
|
1678
1955
|
verifyingContract: accept.asset
|
|
1679
1956
|
},
|
|
@@ -1743,4 +2020,5 @@ function encodeXPaymentHeader(input) {
|
|
|
1743
2020
|
|
|
1744
2021
|
|
|
1745
2022
|
|
|
1746
|
-
|
|
2023
|
+
|
|
2024
|
+
exports.CHAINS = CHAINS; exports.ConfirmationTimeoutError = _chunkIQGT65WScjs.ConfirmationTimeoutError; exports.EIP3009_TYPES = EIP3009_TYPES; exports.EXACT_NETWORK_SLUGS = EXACT_NETWORK_SLUGS; exports.InsufficientFundsError = _chunkIQGT65WScjs.InsufficientFundsError; exports.InvalidEnvelopeError = _chunkIQGT65WScjs.InvalidEnvelopeError; exports.MaxRetriesExceededError = _chunkIQGT65WScjs.MaxRetriesExceededError; exports.MissingDriverError = _chunkIQGT65WScjs.MissingDriverError; exports.NoCompatibleAcceptError = _chunkIQGT65WScjs.NoCompatibleAcceptError; exports.NonReplayableBodyError = _chunkIQGT65WScjs.NonReplayableBodyError; exports.PaymentDeclinedError = _chunkIQGT65WScjs.PaymentDeclinedError; exports.PaymentTimeoutError = _chunkIQGT65WScjs.PaymentTimeoutError; exports.PipRailClient = PipRailClient; exports.PipRailError = _chunkIQGT65WScjs.PipRailError; exports.RecipientNotReadyError = _chunkIQGT65WScjs.RecipientNotReadyError; exports.UnknownTokenError = _chunkIQGT65WScjs.UnknownTokenError; exports.UnsupportedNetworkError = _chunkIQGT65WScjs.UnsupportedNetworkError; exports.WrongChainError = _chunkIQGT65WScjs.WrongChainError; exports.WrongFamilyError = _chunkIQGT65WScjs.WrongFamilyError; exports.buildChallengeHeader = buildChallengeHeader; exports.buildExactAuthorization = buildExactAuthorization; exports.buildReceiptHeader = buildReceiptHeader; exports.buildSignatureHeader = buildSignatureHeader; exports.chainIdForExactNetwork = chainIdForExactNetwork; exports.createPaymentGate = createPaymentGate; exports.encodeXPaymentHeader = encodeXPaymentHeader; exports.evaluatePolicy = evaluatePolicy; exports.parseChallenge = parseChallenge; exports.parseExactRequirements = parseExactRequirements; exports.parseReceipt = parseReceipt; exports.parseSignatureHeader = parseSignatureHeader; exports.paymentTools = paymentTools; exports.pickAccept = pickAccept; exports.planAcross = planAcross; exports.registerDriver = registerDriver; exports.requirePayment = requirePayment; exports.resolveChain = resolveChain; exports.toInsufficientFundsError = _chunkIQGT65WScjs.toInsufficientFundsError; exports.toInvalidBody = toInvalidBody;
|