@piprail/sdk 2.15.0 → 2.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +260 -0
- package/README.md +2 -2
- package/dist/{algorand-CSEUTWNW.js → algorand-25FMBCT3.js} +13 -4
- package/dist/{algorand-I2O24RSM.cjs → algorand-Q3TUQLPK.cjs} +49 -40
- package/dist/{aptos-J7QGCWNW.js → aptos-SBV6SGDP.js} +3 -3
- package/dist/{aptos-GA5AEYYL.cjs → aptos-VBJONBFY.cjs} +36 -36
- package/dist/chunk-2CX7XRZK.js +649 -0
- package/dist/chunk-C52H5TYB.js +474 -0
- package/dist/chunk-OXEFPLZA.cjs +474 -0
- package/dist/{chunk-3FR22M3O.cjs → chunk-QONQSZHJ.cjs} +2 -2
- package/dist/{chunk-3ZWM3DKM.js → chunk-QU25LSVS.js} +2 -2
- package/dist/chunk-V2IJ5HUW.cjs +649 -0
- package/dist/index.cjs +507 -1347
- package/dist/index.d.cts +39 -6
- package/dist/index.d.ts +39 -6
- package/dist/index.js +192 -1032
- package/dist/{ledger-uFtXlIHY.d.cts → ledger-Crc1bZox.d.cts} +164 -10
- package/dist/{ledger-uFtXlIHY.d.ts → ledger-Crc1bZox.d.ts} +164 -10
- package/dist/{near-NDQLGI4U.cjs → near-5LTTDU6G.cjs} +27 -27
- package/dist/{near-YA4Y4IGC.js → near-OLKCMTBI.js} +1 -1
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/{solana-OU6IUMQM.cjs → solana-AI2G7V33.cjs} +73 -52
- package/dist/{solana-MB6G3OA5.js → solana-HTKDRTD3.js} +32 -11
- package/dist/{stellar-RSNVOIKL.cjs → stellar-E2KWEV2E.cjs} +20 -20
- package/dist/{stellar-WDWSYOED.js → stellar-EUFZLX6J.js} +1 -1
- package/dist/{sui-GVSFNNJX.js → sui-KCIITCYH.js} +6 -2
- package/dist/{sui-MVC46BFJ.cjs → sui-Q3NJOJZS.cjs} +21 -17
- package/dist/{ton-MYOI3U3H.cjs → ton-J7TQWRN4.cjs} +16 -16
- package/dist/{ton-EJMGM3YI.js → ton-WH2JVQOO.js} +1 -1
- package/dist/{tron-VAPVMGJK.js → tron-FXBXDNEY.js} +1 -1
- package/dist/{tron-42X5N4GQ.cjs → tron-HIPMOX7S.cjs} +25 -25
- package/dist/{xrpl-Q2UWPCTJ.js → xrpl-RUOB37QH.js} +283 -5
- package/dist/{xrpl-MGUBUNSZ.cjs → xrpl-SVVS445B.cjs} +312 -34
- package/package.json +1 -1
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
var _chunkOXEFPLZAcjs = require('./chunk-OXEFPLZA.cjs');
|
|
4
|
+
require('./chunk-V2IJ5HUW.cjs');
|
|
5
|
+
|
|
2
6
|
|
|
3
7
|
var _chunkCQREG5LEcjs = require('./chunk-CQREG5LE.cjs');
|
|
4
8
|
|
|
@@ -12,7 +16,9 @@ var _chunkCQREG5LEcjs = require('./chunk-CQREG5LE.cjs');
|
|
|
12
16
|
|
|
13
17
|
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
var _chunkQONQSZHJcjs = require('./chunk-QONQSZHJ.cjs');
|
|
16
22
|
|
|
17
23
|
// src/drivers/xrpl/index.ts
|
|
18
24
|
var _xrpl = require('xrpl');
|
|
@@ -48,8 +54,11 @@ var XRPL_MAINNET = {
|
|
|
48
54
|
function xrplAssetId(currencyHex, issuer) {
|
|
49
55
|
return `${currencyHex}:${issuer}`;
|
|
50
56
|
}
|
|
57
|
+
function isXrpNative(asset) {
|
|
58
|
+
return asset === "native" || asset === "XRP";
|
|
59
|
+
}
|
|
51
60
|
function parseXrplAssetId(asset) {
|
|
52
|
-
if (asset
|
|
61
|
+
if (isXrpNative(asset)) return null;
|
|
53
62
|
const i = asset.indexOf(":");
|
|
54
63
|
if (i <= 0 || i === asset.length - 1) return null;
|
|
55
64
|
return { currencyHex: asset.slice(0, i), issuer: asset.slice(i + 1) };
|
|
@@ -94,17 +103,17 @@ async function payXrpl(params) {
|
|
|
94
103
|
const code = res.engine_result;
|
|
95
104
|
if (code.startsWith("tes")) return _nullishCoalesce(_optionalChain([res, 'access', _ => _.tx_json, 'optionalAccess', _2 => _2.hash]), () => ( signed.hash));
|
|
96
105
|
const recipientMsg = recipientNotReadyMessage(code);
|
|
97
|
-
if (recipientMsg) throw new (0,
|
|
106
|
+
if (recipientMsg) throw new (0, _chunkQONQSZHJcjs.RecipientNotReadyError)(recipientMsg, { cause: makeEngineError(res) });
|
|
98
107
|
if (isAffordabilityCode(code)) {
|
|
99
|
-
throw new (0,
|
|
108
|
+
throw new (0, _chunkQONQSZHJcjs.InsufficientFundsError)(
|
|
100
109
|
`XRPL payment rejected: the sender can't cover it \u2014 balance or the 1 XRP base reserve. (XRPL: ${code})`,
|
|
101
110
|
{ cause: makeEngineError(res) }
|
|
102
111
|
);
|
|
103
112
|
}
|
|
104
113
|
throw makeEngineError(res);
|
|
105
114
|
} catch (err) {
|
|
106
|
-
if (err instanceof
|
|
107
|
-
throw _nullishCoalesce(
|
|
115
|
+
if (err instanceof _chunkQONQSZHJcjs.InsufficientFundsError || err instanceof _chunkQONQSZHJcjs.RecipientNotReadyError) throw err;
|
|
116
|
+
throw _nullishCoalesce(_chunkQONQSZHJcjs.toInsufficientFundsError.call(void 0, err), () => ( err));
|
|
108
117
|
}
|
|
109
118
|
}
|
|
110
119
|
function amountForAccept(accept) {
|
|
@@ -141,6 +150,216 @@ function isAffordabilityCode(code) {
|
|
|
141
150
|
return /^(tecUNFUNDED|tecINSUFF|terINSUF)/.test(code);
|
|
142
151
|
}
|
|
143
152
|
|
|
153
|
+
// src/drivers/xrpl/exact.ts
|
|
154
|
+
var _crypto = require('crypto');
|
|
155
|
+
var SECONDS_PER_LEDGER = 4;
|
|
156
|
+
var MIN_LEDGER_WINDOW = 5;
|
|
157
|
+
var MAX_LEDGER_WINDOW = 60;
|
|
158
|
+
function ledgerWindowFor(maxTimeoutSeconds) {
|
|
159
|
+
const secs = Number.isFinite(maxTimeoutSeconds) && maxTimeoutSeconds > 0 ? maxTimeoutSeconds : 60;
|
|
160
|
+
const ledgers = Math.floor(secs / SECONDS_PER_LEDGER);
|
|
161
|
+
return Math.min(MAX_LEDGER_WINDOW, Math.max(MIN_LEDGER_WINDOW, ledgers));
|
|
162
|
+
}
|
|
163
|
+
var MIN_FEE_DROPS = 12;
|
|
164
|
+
var MAX_FEE_DROPS = 1e6;
|
|
165
|
+
var TF_FULLY_CANONICAL_SIG = 2147483648;
|
|
166
|
+
var TF_PARTIAL_PAYMENT = 131072;
|
|
167
|
+
function feesAreSponsored(accept) {
|
|
168
|
+
return _optionalChain([accept, 'access', _3 => _3.extra, 'optionalAccess', _4 => _4.areFeesSponsored]) === true;
|
|
169
|
+
}
|
|
170
|
+
function invoiceIdHash(invoiceId) {
|
|
171
|
+
return _crypto.createHash.call(void 0, "sha256").update(invoiceId, "utf8").digest("hex").toUpperCase();
|
|
172
|
+
}
|
|
173
|
+
function feeForSubmit2(openLedgerFee) {
|
|
174
|
+
const fee = Number(openLedgerFee);
|
|
175
|
+
const chosen = Number.isFinite(fee) && fee > MIN_FEE_DROPS ? Math.ceil(fee) : MIN_FEE_DROPS;
|
|
176
|
+
return String(Math.min(chosen, MAX_FEE_DROPS));
|
|
177
|
+
}
|
|
178
|
+
async function payExactXrpl(input) {
|
|
179
|
+
const { client, wallet, accept } = input;
|
|
180
|
+
if (!isXrpNative(accept.asset)) {
|
|
181
|
+
throw new (0, _chunkQONQSZHJcjs.UnsupportedSchemeError)(
|
|
182
|
+
`XRPL exact currently signs native XRP only \u2014 ${accept.asset} is an issued currency, whose wire amount is a decimal rather than base units and would misprice the spend cap. Pay it via onchain-proof.`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
if (feesAreSponsored(accept)) {
|
|
186
|
+
throw new (0, _chunkQONQSZHJcjs.UnsupportedSchemeError)(
|
|
187
|
+
"XRPL exact rail advertises areFeesSponsored:true, but the XRP Ledger charges the fee to the transaction Account \u2014 the scheme requires false and PipRail cannot honour a sponsor here."
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
const method = _chunkOXEFPLZAcjs.exactTransferMethod.call(void 0, accept, "xrpl");
|
|
191
|
+
if (method !== "sequence") {
|
|
192
|
+
throw new (0, _chunkQONQSZHJcjs.UnsupportedSchemeError)(
|
|
193
|
+
`XRPL exact: unsupported assetTransferMethod "${method}". Only "sequence" is signable \u2014 "ticketSequence" needs a Ticket pre-minted on the payer account, which PipRail does not manage.`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
if (!/^[0-9]+$/.test(accept.amount)) {
|
|
197
|
+
throw new (0, _chunkQONQSZHJcjs.UnsupportedSchemeError)(
|
|
198
|
+
`XRPL exact: native amount "${accept.amount}" is not an integer drops string.`
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
let sequence;
|
|
202
|
+
let openLedgerFee;
|
|
203
|
+
let ledgerIndex;
|
|
204
|
+
try {
|
|
205
|
+
;
|
|
206
|
+
[sequence, openLedgerFee, ledgerIndex] = await Promise.all([
|
|
207
|
+
client.accountSequence(wallet.classicAddress),
|
|
208
|
+
client.feeDrops(),
|
|
209
|
+
client.currentLedgerIndex()
|
|
210
|
+
]);
|
|
211
|
+
} catch (err) {
|
|
212
|
+
if (/actNotFound/i.test(String(_nullishCoalesce(_optionalChain([err, 'optionalAccess', _5 => _5.message]), () => ( err))))) {
|
|
213
|
+
throw new (0, _chunkQONQSZHJcjs.InsufficientFundsError)(
|
|
214
|
+
`XRPL exact: the payer account ${wallet.classicAddress} does not exist on the ledger. An XRPL account must hold the base reserve (currently 1 XRP) before it can send anything \u2014 fund it first.`,
|
|
215
|
+
{ cause: err instanceof Error ? err : new Error(String(err)) }
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
throw err;
|
|
219
|
+
}
|
|
220
|
+
const tx = {
|
|
221
|
+
TransactionType: "Payment",
|
|
222
|
+
Account: wallet.classicAddress,
|
|
223
|
+
Destination: accept.payTo,
|
|
224
|
+
Amount: accept.amount,
|
|
225
|
+
// integer drops, verbatim from the trusted accept
|
|
226
|
+
Sequence: sequence,
|
|
227
|
+
Fee: feeForSubmit2(openLedgerFee),
|
|
228
|
+
LastLedgerSequence: ledgerIndex + ledgerWindowFor(accept.maxTimeoutSeconds),
|
|
229
|
+
// tfFullyCanonicalSig ON, tfPartialPayment OFF — the shape every other XRPL client sends.
|
|
230
|
+
Flags: TF_FULLY_CANONICAL_SIG
|
|
231
|
+
};
|
|
232
|
+
if (typeof _optionalChain([accept, 'access', _6 => _6.extra, 'optionalAccess', _7 => _7.destinationTag]) === "number") {
|
|
233
|
+
tx.DestinationTag = accept.extra.destinationTag;
|
|
234
|
+
}
|
|
235
|
+
if (typeof _optionalChain([accept, 'access', _8 => _8.extra, 'optionalAccess', _9 => _9.sourceTag]) === "number") {
|
|
236
|
+
tx.SourceTag = accept.extra.sourceTag;
|
|
237
|
+
}
|
|
238
|
+
if (typeof _optionalChain([accept, 'access', _10 => _10.extra, 'optionalAccess', _11 => _11.invoiceId]) === "string" && accept.extra.invoiceId.length > 0) {
|
|
239
|
+
tx.InvoiceID = invoiceIdHash(accept.extra.invoiceId);
|
|
240
|
+
}
|
|
241
|
+
let signed;
|
|
242
|
+
try {
|
|
243
|
+
signed = wallet.sign(tx);
|
|
244
|
+
} catch (err) {
|
|
245
|
+
throw new (0, _chunkQONQSZHJcjs.UnsupportedSchemeError)(
|
|
246
|
+
`XRPL exact: could not sign this rail \u2014 ${err instanceof Error ? err.message : String(err)}`,
|
|
247
|
+
{ cause: err instanceof Error ? err : new Error(String(err)) }
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
payload: { signedTxBlob: signed.tx_blob },
|
|
252
|
+
accepted: accept,
|
|
253
|
+
payerFrom: wallet.classicAddress,
|
|
254
|
+
// The tx hash is the natural single-use key: the ledger itself enforces one settlement per
|
|
255
|
+
// (account, sequence), so a replay can never move funds twice even if a gate forgot to dedupe.
|
|
256
|
+
nonce: signed.hash
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
var fail = (error, detail) => ({ ok: false, error, detail });
|
|
260
|
+
async function verifyAndSettleExactXrpl(input) {
|
|
261
|
+
const { client, decode, payload, accept } = input;
|
|
262
|
+
const attempts = _nullishCoalesce(input.pollAttempts, () => ( 8));
|
|
263
|
+
const sleep = _nullishCoalesce(input.sleep, () => ( ((ms) => new Promise((r) => setTimeout(r, ms)))));
|
|
264
|
+
if (feesAreSponsored(accept)) {
|
|
265
|
+
throw new (0, _chunkQONQSZHJcjs.SettlementError)("XRPL exact: rail advertises areFeesSponsored:true, which this ledger cannot honour.");
|
|
266
|
+
}
|
|
267
|
+
let tx;
|
|
268
|
+
try {
|
|
269
|
+
tx = decode(payload.signedTxBlob);
|
|
270
|
+
} catch (err) {
|
|
271
|
+
return fail("signature_invalid", `XRPL exact: undecodable tx blob (${err instanceof Error ? err.message : String(err)}).`);
|
|
272
|
+
}
|
|
273
|
+
if (tx.TransactionType !== "Payment") {
|
|
274
|
+
return fail("signature_invalid", `XRPL exact: expected a Payment, got ${String(tx.TransactionType)}.`);
|
|
275
|
+
}
|
|
276
|
+
if (tx.Memos !== void 0) return fail("signature_invalid", "XRPL exact: Memos are rejected by the scheme.");
|
|
277
|
+
if (tx.Delegate !== void 0) return fail("signature_invalid", "XRPL exact: Delegate is rejected by the scheme.");
|
|
278
|
+
if (tx.Paths !== void 0) return fail("signature_invalid", "XRPL exact: Paths must be omitted.");
|
|
279
|
+
if (tx.DeliverMin !== void 0) return fail("signature_invalid", "XRPL exact: DeliverMin must be omitted.");
|
|
280
|
+
if (tx.Amount !== void 0 && tx.DeliverMax !== void 0) {
|
|
281
|
+
return fail("signature_invalid", "XRPL exact: Amount and DeliverMax must not both be present.");
|
|
282
|
+
}
|
|
283
|
+
if (typeof tx.Flags === "number" && (tx.Flags & TF_PARTIAL_PAYMENT) !== 0) {
|
|
284
|
+
return fail("signature_invalid", "XRPL exact: tfPartialPayment is rejected.");
|
|
285
|
+
}
|
|
286
|
+
if (typeof tx.LastLedgerSequence !== "number") {
|
|
287
|
+
return fail("signature_invalid", "XRPL exact: LastLedgerSequence must be present.");
|
|
288
|
+
}
|
|
289
|
+
if (!tx.TxnSignature && !tx.Signers) {
|
|
290
|
+
return fail("signature_invalid", "XRPL exact: the transaction is not signed.");
|
|
291
|
+
}
|
|
292
|
+
if (tx.Destination !== accept.payTo) {
|
|
293
|
+
return fail("wrong_recipient", `XRPL exact: pays ${String(tx.Destination)}, not payTo ${accept.payTo}.`);
|
|
294
|
+
}
|
|
295
|
+
if (tx.Amount !== accept.amount) {
|
|
296
|
+
return fail("amount_too_low", `XRPL exact: Amount ${String(tx.Amount)} \u2260 the rail's ${accept.amount} drops.`);
|
|
297
|
+
}
|
|
298
|
+
if (typeof _optionalChain([accept, 'access', _12 => _12.extra, 'optionalAccess', _13 => _13.destinationTag]) === "number" && tx.DestinationTag !== accept.extra.destinationTag) {
|
|
299
|
+
return fail("signature_invalid", `XRPL exact: DestinationTag ${String(tx.DestinationTag)} \u2260 the rail's ${accept.extra.destinationTag}.`);
|
|
300
|
+
}
|
|
301
|
+
if (typeof _optionalChain([accept, 'access', _14 => _14.extra, 'optionalAccess', _15 => _15.invoiceId]) === "string" && accept.extra.invoiceId.length > 0) {
|
|
302
|
+
const want = invoiceIdHash(accept.extra.invoiceId);
|
|
303
|
+
if (String(_nullishCoalesce(tx.InvoiceID, () => ( ""))).toUpperCase() !== want) {
|
|
304
|
+
return fail("signature_invalid", "XRPL exact: InvoiceID does not bind this challenge.");
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const feeDrops = Number(tx.Fee);
|
|
308
|
+
if (!Number.isFinite(feeDrops) || feeDrops < 0 || feeDrops > MAX_FEE_DROPS) {
|
|
309
|
+
return fail("signature_invalid", `XRPL exact: implausible Fee ${String(tx.Fee)} drops.`);
|
|
310
|
+
}
|
|
311
|
+
let hash;
|
|
312
|
+
try {
|
|
313
|
+
const res = await client.submit(payload.signedTxBlob);
|
|
314
|
+
if (!res.engine_result.startsWith("tes") && !res.engine_result.startsWith("ter")) {
|
|
315
|
+
return fail(
|
|
316
|
+
"transfer_not_found",
|
|
317
|
+
`XRPL exact: submit rejected \u2014 ${res.engine_result}${res.engine_result_message ? ` (${res.engine_result_message})` : ""}.`
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
hash = _nullishCoalesce(_optionalChain([res, 'access', _16 => _16.tx_json, 'optionalAccess', _17 => _17.hash]), () => ( ""));
|
|
321
|
+
if (!hash) throw new Error("submit returned no transaction hash");
|
|
322
|
+
} catch (err) {
|
|
323
|
+
throw new (0, _chunkQONQSZHJcjs.SettlementError)(`XRPL exact: submit failed (${err instanceof Error ? err.message : String(err)}).`);
|
|
324
|
+
}
|
|
325
|
+
for (let i = 0; i < attempts; i += 1) {
|
|
326
|
+
let record;
|
|
327
|
+
try {
|
|
328
|
+
record = await client.txByHash(hash);
|
|
329
|
+
} catch (e2) {
|
|
330
|
+
record = null;
|
|
331
|
+
}
|
|
332
|
+
if (_optionalChain([record, 'optionalAccess', _18 => _18.validated])) {
|
|
333
|
+
const code = _optionalChain([record, 'access', _19 => _19.meta, 'optionalAccess', _20 => _20.TransactionResult]);
|
|
334
|
+
if (code !== "tesSUCCESS") {
|
|
335
|
+
return fail("transfer_not_found", `XRPL exact: validated with ${String(code)}, not tesSUCCESS.`);
|
|
336
|
+
}
|
|
337
|
+
const delivered = _optionalChain([record, 'access', _21 => _21.meta, 'optionalAccess', _22 => _22.delivered_amount]);
|
|
338
|
+
if (delivered !== void 0 && typeof delivered === "string" && delivered !== accept.amount) {
|
|
339
|
+
return fail("amount_too_low", `XRPL exact: delivered ${delivered} drops, expected ${accept.amount}.`);
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
ok: true,
|
|
343
|
+
receipt: {
|
|
344
|
+
scheme: "exact",
|
|
345
|
+
success: true,
|
|
346
|
+
network: accept.network,
|
|
347
|
+
transaction: hash,
|
|
348
|
+
asset: accept.asset,
|
|
349
|
+
amount: accept.amount,
|
|
350
|
+
payer: String(_nullishCoalesce(_nullishCoalesce(record.Account, () => ( tx.Account)), () => ( ""))),
|
|
351
|
+
payTo: accept.payTo,
|
|
352
|
+
verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
await sleep(1200);
|
|
357
|
+
}
|
|
358
|
+
throw new (0, _chunkQONQSZHJcjs.SettlementError)(
|
|
359
|
+
`XRPL exact: submitted ${hash} but it was not validated within ${attempts} polls. It may still settle; retry the request.`
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
144
363
|
// src/drivers/xrpl/verify.ts
|
|
145
364
|
var RIPPLE_EPOCH_OFFSET = 946684800;
|
|
146
365
|
async function verifyXrpl(params) {
|
|
@@ -152,7 +371,7 @@ async function verifyXrpl(params) {
|
|
|
152
371
|
let txs;
|
|
153
372
|
try {
|
|
154
373
|
txs = await reader.transactionsForAccount(accept.payTo, 200);
|
|
155
|
-
} catch (
|
|
374
|
+
} catch (e3) {
|
|
156
375
|
return rpcFailed(nonce);
|
|
157
376
|
}
|
|
158
377
|
const tx = txs.find(
|
|
@@ -216,7 +435,7 @@ async function verifyXrpl(params) {
|
|
|
216
435
|
}
|
|
217
436
|
function hasNonceMemo(memos, wantMemo) {
|
|
218
437
|
if (!memos) return false;
|
|
219
|
-
return memos.some((m) => (_nullishCoalesce(_optionalChain([m, 'access',
|
|
438
|
+
return memos.some((m) => (_nullishCoalesce(_optionalChain([m, 'access', _23 => _23.Memo, 'optionalAccess', _24 => _24.MemoData]), () => ( ""))).toUpperCase() === wantMemo);
|
|
220
439
|
}
|
|
221
440
|
function deliveredBaseUnits(delivered, want, decimals) {
|
|
222
441
|
if (delivered === void 0) return null;
|
|
@@ -230,8 +449,8 @@ function deliveredBaseUnits(delivered, want, decimals) {
|
|
|
230
449
|
return null;
|
|
231
450
|
}
|
|
232
451
|
try {
|
|
233
|
-
return
|
|
234
|
-
} catch (
|
|
452
|
+
return _chunkQONQSZHJcjs.floorUnits.call(void 0, delivered.value, decimals);
|
|
453
|
+
} catch (e4) {
|
|
235
454
|
return null;
|
|
236
455
|
}
|
|
237
456
|
}
|
|
@@ -254,13 +473,13 @@ function rpcFailed(nonce) {
|
|
|
254
473
|
|
|
255
474
|
function assertXrplWallet(wallet, network) {
|
|
256
475
|
if (typeof wallet !== "object" || wallet === null) {
|
|
257
|
-
throw new (0,
|
|
476
|
+
throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
|
|
258
477
|
`chain ${network} is XRPL; wallet must be { key } (s\u2026 seed) or { wallet }.`
|
|
259
478
|
);
|
|
260
479
|
}
|
|
261
|
-
|
|
480
|
+
_chunkQONQSZHJcjs.assertNoLegacyWalletKey.call(void 0, wallet, "XRPL");
|
|
262
481
|
if (!("key" in wallet) && !("wallet" in wallet)) {
|
|
263
|
-
throw new (0,
|
|
482
|
+
throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
|
|
264
483
|
`chain ${network} is XRPL; wallet must be { key } (s\u2026 seed) or { wallet }.`
|
|
265
484
|
);
|
|
266
485
|
}
|
|
@@ -270,16 +489,16 @@ function resolveXrplWallet(config) {
|
|
|
270
489
|
if (config.wallet) return config.wallet;
|
|
271
490
|
if (config.key) {
|
|
272
491
|
if (!_xrpl.isValidSecret.call(void 0, config.key)) {
|
|
273
|
-
throw new (0,
|
|
492
|
+
throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)("XRPL wallet { key } is not a valid s\u2026 secret seed.");
|
|
274
493
|
}
|
|
275
494
|
return _xrpl.Wallet.fromSeed(config.key);
|
|
276
495
|
}
|
|
277
|
-
throw new (0,
|
|
496
|
+
throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)("XRPL wallet needs { key } (s\u2026 seed) or { wallet }.");
|
|
278
497
|
}
|
|
279
498
|
|
|
280
499
|
// src/drivers/xrpl/index.ts
|
|
281
500
|
function isXrplActNotFound(e) {
|
|
282
|
-
return /actNotFound/i.test(String(_nullishCoalesce(_optionalChain([e, 'optionalAccess',
|
|
501
|
+
return /actNotFound/i.test(String(_nullishCoalesce(_optionalChain([e, 'optionalAccess', _25 => _25.message]), () => ( e))));
|
|
283
502
|
}
|
|
284
503
|
var xrplDriver = {
|
|
285
504
|
family: "xrpl",
|
|
@@ -301,7 +520,7 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
301
520
|
const json = await res.json();
|
|
302
521
|
const result = json.result;
|
|
303
522
|
if (!result || result.status === "error" || result.error) {
|
|
304
|
-
throw new Error(`XRPL RPC ${method} error: ${_nullishCoalesce(_optionalChain([result, 'optionalAccess',
|
|
523
|
+
throw new Error(`XRPL RPC ${method} error: ${_nullishCoalesce(_optionalChain([result, 'optionalAccess', _26 => _26.error]), () => ( "unknown"))}`);
|
|
305
524
|
}
|
|
306
525
|
return result;
|
|
307
526
|
}
|
|
@@ -325,6 +544,16 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
325
544
|
return rpc("submit", { tx_blob: txBlob });
|
|
326
545
|
}
|
|
327
546
|
};
|
|
547
|
+
const exactSettleClient = {
|
|
548
|
+
submit: (txBlob) => payClient.submit(txBlob),
|
|
549
|
+
async txByHash(hash) {
|
|
550
|
+
try {
|
|
551
|
+
return await rpc("tx", { transaction: hash, binary: false });
|
|
552
|
+
} catch (e5) {
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
};
|
|
328
557
|
const reader = {
|
|
329
558
|
async transactionsForAccount(account, limit) {
|
|
330
559
|
const r = await rpc("account_tx", {
|
|
@@ -349,7 +578,7 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
349
578
|
const info = preset.tokens[token.toUpperCase()];
|
|
350
579
|
if (!info) {
|
|
351
580
|
const known = Object.keys(preset.tokens).join(", ") || "(none built in)";
|
|
352
|
-
throw new (0,
|
|
581
|
+
throw new (0, _chunkQONQSZHJcjs.UnknownTokenError)(
|
|
353
582
|
`token "${token}" isn't built in for XRPL (known: ${known}). Pass { issuer, currencyHex, decimals } for a custom IOU, or use 'native'.`
|
|
354
583
|
);
|
|
355
584
|
}
|
|
@@ -359,10 +588,10 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
359
588
|
symbol: info.symbol
|
|
360
589
|
};
|
|
361
590
|
}
|
|
362
|
-
|
|
591
|
+
_chunkQONQSZHJcjs.rejectForeignToken.call(void 0, token, "xrpl", network);
|
|
363
592
|
const t = token;
|
|
364
593
|
if (!t.issuer || !t.currencyHex || typeof t.decimals !== "number") {
|
|
365
|
-
throw new (0,
|
|
594
|
+
throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
|
|
366
595
|
`chain ${network} is XRPL; a custom token must be { issuer, currencyHex, decimals }.`
|
|
367
596
|
);
|
|
368
597
|
}
|
|
@@ -373,7 +602,7 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
373
602
|
};
|
|
374
603
|
},
|
|
375
604
|
describeAsset(asset) {
|
|
376
|
-
if (asset
|
|
605
|
+
if (isXrpNative(asset)) return { symbol: XRP_SYMBOL, decimals: XRP_DECIMALS };
|
|
377
606
|
for (const info of Object.values(preset.tokens)) {
|
|
378
607
|
if (xrplAssetId(info.currencyHex, info.issuer) === asset) {
|
|
379
608
|
return { symbol: info.symbol, decimals: info.decimals };
|
|
@@ -383,12 +612,12 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
383
612
|
},
|
|
384
613
|
assertValidPayTo(payTo) {
|
|
385
614
|
if (payTo.startsWith("0x")) {
|
|
386
|
-
throw new (0,
|
|
615
|
+
throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
|
|
387
616
|
`chain ${network} is XRPL, but payTo "${payTo}" looks like an EVM address.`
|
|
388
617
|
);
|
|
389
618
|
}
|
|
390
619
|
if (!_xrpl.isValidClassicAddress.call(void 0, payTo)) {
|
|
391
|
-
throw new (0,
|
|
620
|
+
throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
|
|
392
621
|
`chain ${network} is XRPL, but payTo "${payTo}" is not a valid XRPL account (r\u2026).`
|
|
393
622
|
);
|
|
394
623
|
}
|
|
@@ -400,6 +629,55 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
400
629
|
const w = resolveXrplWallet(wallet._native);
|
|
401
630
|
return payXrpl({ client: payClient, wallet: w, accept });
|
|
402
631
|
},
|
|
632
|
+
/*
|
|
633
|
+
* ── Standard x402 `exact` rail (scheme_exact_xrpl.md) ───────────────────────────
|
|
634
|
+
* XRPL is the family where the PAYER pays the fee, so there is no sponsor to wire: the buyer
|
|
635
|
+
* signs a complete `Payment` and the merchant simply submits it. See drivers/xrpl/exact.ts for
|
|
636
|
+
* why the binding is `InvoiceID` (never a memo) and why only native XRP is exact-payable today.
|
|
637
|
+
*/
|
|
638
|
+
// Native XRP only. An issued currency states its amount as a DECIMAL on the wire while the SDK
|
|
639
|
+
// prices and spend-caps in base units — signing one would let a 12-RLUSD rail pass a cap set in
|
|
640
|
+
// base units. Dropping it here means the gather skips it, rather than planning `payable` and
|
|
641
|
+
// throwing at signing time.
|
|
642
|
+
exactPayableAsset: (asset) => isXrpNative(asset),
|
|
643
|
+
async payExact(wallet, accept) {
|
|
644
|
+
const w = resolveXrplWallet(wallet._native);
|
|
645
|
+
return payExactXrpl({ client: payClient, wallet: w, accept });
|
|
646
|
+
},
|
|
647
|
+
// The gate advertises the rail. No `feePayer` and no sponsor key: uniquely on XRPL the buyer's
|
|
648
|
+
// own signed transaction carries its fee, so settlement is a bare submit.
|
|
649
|
+
//
|
|
650
|
+
// NB `server.ts` still requires a `relayer` for `settle: 'self'` — an unconditional config
|
|
651
|
+
// guard, and correctly chain-agnostic, so it cannot special-case XRPL. The relayer is accepted
|
|
652
|
+
// and then ignored here. Making it optional needs a driver-declared capability flag (the same
|
|
653
|
+
// shape as `exactPermit2Supported`); worth doing, since an XRPL merchant currently has to hand
|
|
654
|
+
// the gate a key it never uses, but it is a separate change from the buyer this rail is for.
|
|
655
|
+
async resolveExactRail({ asset }) {
|
|
656
|
+
if (asset !== "native") return null;
|
|
657
|
+
return {
|
|
658
|
+
method: "sequence",
|
|
659
|
+
// `areFeesSponsored` is REQUIRED by the scheme and must be false; we emit it explicitly
|
|
660
|
+
// even though no deployed rail does, because emitting the conformant shape costs nothing
|
|
661
|
+
// and a strict third-party buyer may look for it. (Our own buyer reads it as false when
|
|
662
|
+
// absent — requiring it would have rejected the entire deployed XRPL web.)
|
|
663
|
+
extra: { areFeesSponsored: false }
|
|
664
|
+
};
|
|
665
|
+
},
|
|
666
|
+
// SELLER side — decode + check the buyer's blob against the trusted accept, submit it, and wait
|
|
667
|
+
// for a validated tesSUCCESS. `relayer` is accepted for signature-compatibility with the other
|
|
668
|
+
// families and deliberately unused: the payer already paid the fee inside the signed blob.
|
|
669
|
+
async settleExactSelf({ payload, accept }) {
|
|
670
|
+
if (!("signedTxBlob" in payload)) {
|
|
671
|
+
return { ok: false, error: "signature_invalid", detail: "XRPL exact expects a { signedTxBlob } payload." };
|
|
672
|
+
}
|
|
673
|
+
const { decode } = await Promise.resolve().then(() => _interopRequireWildcard(require("xrpl")));
|
|
674
|
+
return verifyAndSettleExactXrpl({
|
|
675
|
+
client: exactSettleClient,
|
|
676
|
+
decode: (blob) => decode(blob),
|
|
677
|
+
payload,
|
|
678
|
+
accept
|
|
679
|
+
});
|
|
680
|
+
},
|
|
403
681
|
async confirm(ref) {
|
|
404
682
|
for (let i = 0; i < 12; i += 1) {
|
|
405
683
|
try {
|
|
@@ -407,26 +685,26 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
407
685
|
transaction: ref
|
|
408
686
|
});
|
|
409
687
|
if (tx.validated) return { height: String(_nullishCoalesce(tx.ledger_index, () => ( 0))) };
|
|
410
|
-
} catch (
|
|
688
|
+
} catch (e6) {
|
|
411
689
|
}
|
|
412
690
|
await _chunkCQREG5LEcjs.delay.call(void 0, 1500);
|
|
413
691
|
}
|
|
414
|
-
throw new (0,
|
|
692
|
+
throw new (0, _chunkQONQSZHJcjs.ConfirmationTimeoutError)(`XRPL tx ${ref} not validated on-ledger in time.`);
|
|
415
693
|
},
|
|
416
694
|
async estimateCost() {
|
|
417
695
|
try {
|
|
418
696
|
const drops = await payClient.feeDrops();
|
|
419
697
|
const n = Number(drops);
|
|
420
698
|
const fee = BigInt(Number.isFinite(n) && n > 12 ? Math.ceil(n) : 12);
|
|
421
|
-
return
|
|
699
|
+
return _chunkQONQSZHJcjs.nativeCost.call(void 0, {
|
|
422
700
|
symbol: XRP_SYMBOL,
|
|
423
701
|
decimals: XRP_DECIMALS,
|
|
424
702
|
fee,
|
|
425
703
|
basis: "estimated",
|
|
426
704
|
detail: `network fee ${fee} drops`
|
|
427
705
|
});
|
|
428
|
-
} catch (
|
|
429
|
-
return
|
|
706
|
+
} catch (e7) {
|
|
707
|
+
return _chunkQONQSZHJcjs.nativeCost.call(void 0, {
|
|
430
708
|
symbol: XRP_SYMBOL,
|
|
431
709
|
decimals: XRP_DECIMALS,
|
|
432
710
|
fee: 12n,
|
|
@@ -439,7 +717,7 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
439
717
|
let owner;
|
|
440
718
|
try {
|
|
441
719
|
owner = resolveXrplWallet(wallet._native).classicAddress;
|
|
442
|
-
} catch (
|
|
720
|
+
} catch (e8) {
|
|
443
721
|
return { token: null, native: null };
|
|
444
722
|
}
|
|
445
723
|
let native = null;
|
|
@@ -452,7 +730,7 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
452
730
|
} catch (e) {
|
|
453
731
|
native = isXrplActNotFound(e) ? 0n : null;
|
|
454
732
|
}
|
|
455
|
-
if (asset
|
|
733
|
+
if (isXrpNative(asset)) return { token: native, native };
|
|
456
734
|
let token = null;
|
|
457
735
|
try {
|
|
458
736
|
const [currencyHex, issuer] = asset.split(":");
|
|
@@ -463,7 +741,7 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
463
741
|
const line = r.lines.find(
|
|
464
742
|
(l) => l.currency.toUpperCase() === (_nullishCoalesce(currencyHex, () => ( ""))).toUpperCase() && l.account === issuer
|
|
465
743
|
);
|
|
466
|
-
token = line ?
|
|
744
|
+
token = line ? _chunkQONQSZHJcjs.floorUnits.call(void 0, line.balance, XRP_DECIMALS) : 0n;
|
|
467
745
|
} catch (e) {
|
|
468
746
|
token = isXrplActNotFound(e) ? 0n : null;
|
|
469
747
|
}
|
|
@@ -476,7 +754,7 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
476
754
|
if (isXrplActNotFound(e)) return { ready: false, reason: "INACTIVE" };
|
|
477
755
|
return { ready: "unknown" };
|
|
478
756
|
}
|
|
479
|
-
if (asset
|
|
757
|
+
if (isXrpNative(asset)) return { ready: true };
|
|
480
758
|
try {
|
|
481
759
|
const [currencyHex, issuer] = asset.split(":");
|
|
482
760
|
const r = await rpc("account_lines", {
|
|
@@ -487,7 +765,7 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
487
765
|
(l) => l.currency.toUpperCase() === (_nullishCoalesce(currencyHex, () => ( ""))).toUpperCase() && l.account === issuer
|
|
488
766
|
);
|
|
489
767
|
return has ? { ready: true } : { ready: false, reason: "NO_TRUSTLINE" };
|
|
490
|
-
} catch (
|
|
768
|
+
} catch (e9) {
|
|
491
769
|
return { ready: "unknown" };
|
|
492
770
|
}
|
|
493
771
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@piprail/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.16.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",
|