@piprail/sdk 1.0.0 → 1.1.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.
@@ -9,7 +9,7 @@ var _chunkCQREG5LEcjs = require('./chunk-CQREG5LE.cjs');
9
9
 
10
10
 
11
11
 
12
- var _chunkWQWNPAYQcjs = require('./chunk-WQWNPAYQ.cjs');
12
+ var _chunkNK64H3RMcjs = require('./chunk-NK64H3RM.cjs');
13
13
 
14
14
  // src/drivers/tron/index.ts
15
15
  var _tronweb = require('tronweb');
@@ -61,7 +61,7 @@ async function payTron(params) {
61
61
  return _nullishCoalesce(_nullishCoalesce(broadcast.txid, () => ( _optionalChain([broadcast, 'access', _7 => _7.transaction, 'optionalAccess', _8 => _8.txID]))), () => ( signed.txID));
62
62
  }
63
63
  if (isTronAffordability(broadcast.code, broadcast.message)) {
64
- throw new (0, _chunkWQWNPAYQcjs.InsufficientFundsError)(
64
+ throw new (0, _chunkNK64H3RMcjs.InsufficientFundsError)(
65
65
  `Tron payment rejected (${_nullishCoalesce(broadcast.code, () => ( "broadcast"))}): not enough TRX for energy/bandwidth, or insufficient token balance.`
66
66
  );
67
67
  }
@@ -69,8 +69,30 @@ async function payTron(params) {
69
69
  `Tron broadcast failed: ${_nullishCoalesce(broadcast.code, () => ( ""))} ${decodeMaybeHex(broadcast.message)}`.trim()
70
70
  );
71
71
  } catch (err) {
72
- if (err instanceof _chunkWQWNPAYQcjs.InsufficientFundsError) throw err;
73
- throw _nullishCoalesce(_chunkWQWNPAYQcjs.toInsufficientFundsError.call(void 0, err), () => ( err));
72
+ if (err instanceof _chunkNK64H3RMcjs.InsufficientFundsError) throw err;
73
+ throw _nullishCoalesce(_chunkNK64H3RMcjs.toInsufficientFundsError.call(void 0, err), () => ( err));
74
+ }
75
+ }
76
+ async function payTronNative(params) {
77
+ const { client, from, privateKey, accept } = params;
78
+ try {
79
+ const unsigned = await client.transactionBuilder.sendTrx(accept.payTo, Number(accept.amount), from);
80
+ const signed = await client.trx.sign(unsigned, privateKey);
81
+ const broadcast = await client.trx.sendRawTransaction(signed);
82
+ if (broadcast.result === true || broadcast.txid || _optionalChain([broadcast, 'access', _9 => _9.transaction, 'optionalAccess', _10 => _10.txID])) {
83
+ return _nullishCoalesce(_nullishCoalesce(broadcast.txid, () => ( _optionalChain([broadcast, 'access', _11 => _11.transaction, 'optionalAccess', _12 => _12.txID]))), () => ( signed.txID));
84
+ }
85
+ if (isTronAffordability(broadcast.code, broadcast.message)) {
86
+ throw new (0, _chunkNK64H3RMcjs.InsufficientFundsError)(
87
+ `Tron TRX payment rejected (${_nullishCoalesce(broadcast.code, () => ( "broadcast"))}): not enough TRX for the amount plus bandwidth (and the ~1 TRX account-creation fee if the recipient is new).`
88
+ );
89
+ }
90
+ throw new Error(
91
+ `Tron TRX broadcast failed: ${_nullishCoalesce(broadcast.code, () => ( ""))} ${decodeMaybeHex(broadcast.message)}`.trim()
92
+ );
93
+ } catch (err) {
94
+ if (err instanceof _chunkNK64H3RMcjs.InsufficientFundsError) throw err;
95
+ throw _nullishCoalesce(_chunkNK64H3RMcjs.toInsufficientFundsError.call(void 0, err), () => ( err));
74
96
  }
75
97
  }
76
98
  function decodeMaybeHex(message) {
@@ -104,11 +126,11 @@ async function verifyTron(params) {
104
126
  return txNotFound(txid);
105
127
  }
106
128
  if (!info || !info.id) return txNotFound(txid);
107
- if (_optionalChain([info, 'access', _9 => _9.receipt, 'optionalAccess', _10 => _10.result]) !== "SUCCESS") {
129
+ if (_optionalChain([info, 'access', _13 => _13.receipt, 'optionalAccess', _14 => _14.result]) !== "SUCCESS") {
108
130
  return {
109
131
  ok: false,
110
132
  error: "tx_reverted",
111
- detail: `Tron tx ${txid} did not succeed (receipt.result=${_nullishCoalesce(_optionalChain([info, 'access', _11 => _11.receipt, 'optionalAccess', _12 => _12.result]), () => ( "none"))}).`
133
+ detail: `Tron tx ${txid} did not succeed (receipt.result=${_nullishCoalesce(_optionalChain([info, 'access', _15 => _15.receipt, 'optionalAccess', _16 => _16.result]), () => ( "none"))}).`
112
134
  };
113
135
  }
114
136
  const ageSeconds = Math.floor(Date.now() / 1e3) - Math.floor(info.blockTimeStamp / 1e3);
@@ -142,6 +164,74 @@ async function verifyTron(params) {
142
164
  }
143
165
  };
144
166
  }
167
+ async function verifyTronNative(params) {
168
+ const { reader, accept, txid, payToHex41, toBase58 } = params;
169
+ const required = BigInt(accept.amount);
170
+ let info;
171
+ try {
172
+ info = await reader.getTransactionInfo(txid);
173
+ } catch (e3) {
174
+ return txNotFound(txid);
175
+ }
176
+ if (!info || !info.id) return txNotFound(txid);
177
+ const ageSeconds = Math.floor(Date.now() / 1e3) - Math.floor(info.blockTimeStamp / 1e3);
178
+ if (Number.isFinite(ageSeconds) && ageSeconds > accept.maxTimeoutSeconds) {
179
+ return {
180
+ ok: false,
181
+ error: "payment_expired",
182
+ detail: `Payment is ${ageSeconds}s old; max allowed is ${accept.maxTimeoutSeconds}s.`
183
+ };
184
+ }
185
+ let tx;
186
+ try {
187
+ tx = await reader.getTransaction(txid);
188
+ } catch (e4) {
189
+ return txNotFound(txid);
190
+ }
191
+ if (!tx) return txNotFound(txid);
192
+ const ret = _optionalChain([tx, 'access', _17 => _17.ret, 'optionalAccess', _18 => _18[0], 'optionalAccess', _19 => _19.contractRet]);
193
+ if (ret && ret !== "SUCCESS") {
194
+ return { ok: false, error: "tx_reverted", detail: `Tron tx ${txid} did not succeed (contractRet=${ret}).` };
195
+ }
196
+ const contract = (_nullishCoalesce(_optionalChain([tx, 'access', _20 => _20.raw_data, 'optionalAccess', _21 => _21.contract]), () => ( [])))[0];
197
+ if (!contract || contract.type !== "TransferContract") {
198
+ return {
199
+ ok: false,
200
+ error: "transfer_not_found",
201
+ detail: `Tron tx ${txid} is not a native TRX transfer (TransferContract).`
202
+ };
203
+ }
204
+ const value = _nullishCoalesce(_optionalChain([contract, 'access', _22 => _22.parameter, 'optionalAccess', _23 => _23.value]), () => ( {}));
205
+ const to = String(_nullishCoalesce(value.to_address, () => ( ""))).toLowerCase().replace(/^0x/, "");
206
+ if (to !== payToHex41) {
207
+ return { ok: false, error: "wrong_recipient", detail: `Native TRX in ${txid} was not sent to ${accept.payTo}.` };
208
+ }
209
+ const amount = (() => {
210
+ try {
211
+ return BigInt(_nullishCoalesce(value.amount, () => ( 0)));
212
+ } catch (e5) {
213
+ return 0n;
214
+ }
215
+ })();
216
+ if (amount < required) {
217
+ return { ok: false, error: "amount_too_low", detail: `Sent ${amount} sun, required ${required}.` };
218
+ }
219
+ const from = String(_nullishCoalesce(value.owner_address, () => ( ""))).toLowerCase().replace(/^0x/, "");
220
+ return {
221
+ ok: true,
222
+ receipt: {
223
+ scheme: "onchain-proof",
224
+ success: true,
225
+ network: accept.network,
226
+ transaction: txid,
227
+ asset: "native",
228
+ amount: accept.amount,
229
+ payer: from ? toBase58(from) : "",
230
+ payTo: accept.payTo,
231
+ verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
232
+ }
233
+ };
234
+ }
145
235
  function sumTransfersTo(logs, tokenHex20, payToHex20) {
146
236
  let total = 0n;
147
237
  let from = null;
@@ -154,7 +244,7 @@ function sumTransfersTo(logs, tokenHex20, payToHex20) {
154
244
  try {
155
245
  total += BigInt(`0x${log.data}`);
156
246
  from = last20(topics[1]);
157
- } catch (e3) {
247
+ } catch (e6) {
158
248
  }
159
249
  }
160
250
  return { total, from };
@@ -173,22 +263,22 @@ function txNotFound(txid) {
173
263
  // src/drivers/tron/wallet.ts
174
264
  function assertTronWallet(wallet, network) {
175
265
  if (typeof wallet !== "object" || wallet === null) {
176
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
266
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
177
267
  `chain ${network} is Tron; wallet must be { privateKey } (32-byte hex).`
178
268
  );
179
269
  }
180
270
  if ("walletClient" in wallet) {
181
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
271
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
182
272
  `chain ${network} is Tron; a viem { walletClient } can't be used \u2014 pass { privateKey } (32-byte hex).`
183
273
  );
184
274
  }
185
275
  if ("secretKey" in wallet || "signer" in wallet || "mnemonic" in wallet || "keyPair" in wallet || "secret" in wallet || "keypair" in wallet || "seed" in wallet) {
186
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
276
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
187
277
  `chain ${network} is Tron; that looks like a Solana/TON/Stellar/XRPL wallet \u2014 pass { privateKey } (32-byte hex).`
188
278
  );
189
279
  }
190
280
  if (!("privateKey" in wallet)) {
191
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
281
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
192
282
  `chain ${network} is Tron; wallet must be { privateKey } (32-byte hex).`
193
283
  );
194
284
  }
@@ -196,11 +286,11 @@ function assertTronWallet(wallet, network) {
196
286
  }
197
287
  function resolveTronPrivateKey(config) {
198
288
  if (!config.privateKey) {
199
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)("Tron wallet needs { privateKey } (32-byte hex).");
289
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)("Tron wallet needs { privateKey } (32-byte hex).");
200
290
  }
201
291
  const hex = config.privateKey.replace(/^0x/i, "").toLowerCase();
202
292
  if (!/^[0-9a-f]{64}$/.test(hex)) {
203
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
293
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
204
294
  "Tron wallet { privateKey } must be a 32-byte hex string (64 hex chars)."
205
295
  );
206
296
  }
@@ -227,6 +317,15 @@ function makeTronNetwork(preset, rpcUrl) {
227
317
  const info = await tronWeb.trx.getTransactionInfo(txid);
228
318
  if (!info || !info.id) return null;
229
319
  return info;
320
+ },
321
+ // Raw tx (contracts + per-contract ret) for the native-TRX path. getTransaction
322
+ // throws on an unknown tx, so swallow → null; native verify gates finality on
323
+ // getTransactionInfo first anyway.
324
+ async getTransaction(txid) {
325
+ const tx = await tronWeb.trx.getTransaction(txid).catch(() => null);
326
+ const raw = tx;
327
+ if (!raw || !raw.raw_data) return null;
328
+ return raw;
230
329
  }
231
330
  };
232
331
  return {
@@ -235,29 +334,27 @@ function makeTronNetwork(preset, rpcUrl) {
235
334
  supports: (n) => n === network,
236
335
  resolveToken(token) {
237
336
  if (token === "native") {
238
- throw new (0, _chunkWQWNPAYQcjs.UnknownTokenError)(
239
- `Tron payments are TRC-20 only \u2014 native TRX isn't a built-in asset. Use 'USDT' or a custom { address, decimals }.`
240
- );
337
+ return { asset: "native", decimals: TRX_DECIMALS, symbol: "TRX" };
241
338
  }
242
339
  if (typeof token === "string") {
243
340
  const info = preset.tokens[token.toUpperCase()];
244
341
  if (!info) {
245
342
  const known = Object.keys(preset.tokens).join(", ") || "(none built in)";
246
- throw new (0, _chunkWQWNPAYQcjs.UnknownTokenError)(
343
+ throw new (0, _chunkNK64H3RMcjs.UnknownTokenError)(
247
344
  `token "${token}" isn't built in for Tron (known: ${known}). Note: native USDC doesn't exist on Tron. Pass { address, decimals } for a custom TRC-20.`
248
345
  );
249
346
  }
250
347
  return { asset: info.address, decimals: info.decimals, symbol: info.symbol };
251
348
  }
252
- _chunkWQWNPAYQcjs.rejectForeignToken.call(void 0, token, "tron", network);
349
+ _chunkNK64H3RMcjs.rejectForeignToken.call(void 0, token, "tron", network);
253
350
  if (!("address" in token)) {
254
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
351
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
255
352
  `chain ${network} is Tron; a custom token must be { address, decimals } (Base58 T\u2026 contract).`
256
353
  );
257
354
  }
258
355
  const t = token;
259
356
  if (t.address.startsWith("0x") || !tronWeb.isAddress(t.address)) {
260
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
357
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
261
358
  `chain ${network} is Tron, but token address "${t.address}" is not a valid Tron contract (T\u2026).`
262
359
  );
263
360
  }
@@ -268,7 +365,7 @@ function makeTronNetwork(preset, rpcUrl) {
268
365
  };
269
366
  },
270
367
  describeAsset(asset) {
271
- if (asset === "native") return null;
368
+ if (asset === "native") return { symbol: "TRX", decimals: TRX_DECIMALS };
272
369
  for (const info of Object.values(preset.tokens)) {
273
370
  if (info.address === asset) return { symbol: info.symbol, decimals: info.decimals };
274
371
  }
@@ -276,12 +373,12 @@ function makeTronNetwork(preset, rpcUrl) {
276
373
  },
277
374
  assertValidPayTo(payTo) {
278
375
  if (payTo.startsWith("0x")) {
279
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
376
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
280
377
  `chain ${network} is Tron, but payTo "${payTo}" looks like an EVM address.`
281
378
  );
282
379
  }
283
380
  if (!tronWeb.isAddress(payTo)) {
284
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
381
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
285
382
  `chain ${network} is Tron, but payTo "${payTo}" is not a valid Tron address (T\u2026).`
286
383
  );
287
384
  }
@@ -293,7 +390,10 @@ function makeTronNetwork(preset, rpcUrl) {
293
390
  const privateKey = resolveTronPrivateKey(wallet._native);
294
391
  const from = tronWeb.address.fromPrivateKey(privateKey);
295
392
  if (!from) {
296
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)("Tron wallet { privateKey } could not derive an address.");
393
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)("Tron wallet { privateKey } could not derive an address.");
394
+ }
395
+ if (accept.asset === "native") {
396
+ return payTronNative({ client: tronWeb, from, privateKey, accept });
297
397
  }
298
398
  return payTron({
299
399
  client: tronWeb,
@@ -309,16 +409,25 @@ function makeTronNetwork(preset, rpcUrl) {
309
409
  try {
310
410
  const info = await reader.getTransactionInfo(txid);
311
411
  if (info) return { height: String(_nullishCoalesce(info.blockNumber, () => ( 0))) };
312
- } catch (e4) {
412
+ } catch (e7) {
313
413
  }
314
414
  await _chunkCQREG5LEcjs.delay.call(void 0, 2500);
315
415
  }
316
- throw new (0, _chunkWQWNPAYQcjs.ConfirmationTimeoutError)(`Tron tx ${txid} did not solidify in time.`);
416
+ throw new (0, _chunkNK64H3RMcjs.ConfirmationTimeoutError)(`Tron tx ${txid} did not solidify in time.`);
317
417
  },
318
418
  async estimateCost(accept, opts) {
419
+ if (accept.asset === "native") {
420
+ return _chunkNK64H3RMcjs.nativeCost.call(void 0, {
421
+ symbol: "TRX",
422
+ decimals: TRX_DECIMALS,
423
+ fee: 1300000n,
424
+ basis: "heuristic",
425
+ detail: "\u22480.27 TRX bandwidth (sender unstaked) + up to ~1 TRX if the recipient account is new"
426
+ });
427
+ }
319
428
  const ENERGY_PRICE = 420n;
320
429
  const BANDWIDTH_SUN = 345000n;
321
- if (_optionalChain([opts, 'optionalAccess', _13 => _13.from])) {
430
+ if (_optionalChain([opts, 'optionalAccess', _24 => _24.from])) {
322
431
  try {
323
432
  const r = await tronWeb.transactionBuilder.triggerConstantContract(
324
433
  accept.asset,
@@ -332,7 +441,7 @@ function makeTronNetwork(preset, rpcUrl) {
332
441
  );
333
442
  const energy = BigInt(_nullishCoalesce(r.energy_used, () => ( 0)));
334
443
  if (energy > 0n) {
335
- return _chunkWQWNPAYQcjs.nativeCost.call(void 0, {
444
+ return _chunkNK64H3RMcjs.nativeCost.call(void 0, {
336
445
  symbol: "TRX",
337
446
  decimals: TRX_DECIMALS,
338
447
  fee: energy * ENERGY_PRICE + BANDWIDTH_SUN,
@@ -340,10 +449,10 @@ function makeTronNetwork(preset, rpcUrl) {
340
449
  detail: `energy ${energy} @ ${ENERGY_PRICE} sun + bandwidth`
341
450
  });
342
451
  }
343
- } catch (e5) {
452
+ } catch (e8) {
344
453
  }
345
454
  }
346
- return _chunkWQWNPAYQcjs.nativeCost.call(void 0, {
455
+ return _chunkNK64H3RMcjs.nativeCost.call(void 0, {
347
456
  symbol: "TRX",
348
457
  decimals: TRX_DECIMALS,
349
458
  fee: 30000n * ENERGY_PRICE + BANDWIDTH_SUN,
@@ -353,6 +462,15 @@ function makeTronNetwork(preset, rpcUrl) {
353
462
  },
354
463
  async verify(ref, accept) {
355
464
  const txid = stripTronPrefix(ref);
465
+ if (accept.asset === "native") {
466
+ return verifyTronNative({
467
+ reader,
468
+ accept,
469
+ txid,
470
+ payToHex41: tronWeb.address.toHex(accept.payTo).toLowerCase(),
471
+ toBase58: (h) => tronWeb.address.fromHex(h)
472
+ });
473
+ }
356
474
  return verifyTron({
357
475
  reader,
358
476
  accept,
@@ -10,7 +10,8 @@ var _chunkCQREG5LEcjs = require('./chunk-CQREG5LE.cjs');
10
10
 
11
11
 
12
12
 
13
- var _chunkWQWNPAYQcjs = require('./chunk-WQWNPAYQ.cjs');
13
+
14
+ var _chunkNK64H3RMcjs = require('./chunk-NK64H3RM.cjs');
14
15
 
15
16
  // src/drivers/xrpl/index.ts
16
17
  var _xrpl = require('xrpl');
@@ -91,17 +92,18 @@ async function payXrpl(params) {
91
92
  const res = await client.submit(signed.tx_blob);
92
93
  const code = res.engine_result;
93
94
  if (code.startsWith("tes")) return _nullishCoalesce(_optionalChain([res, 'access', _ => _.tx_json, 'optionalAccess', _2 => _2.hash]), () => ( signed.hash));
95
+ const recipientMsg = recipientNotReadyMessage(code);
96
+ if (recipientMsg) throw new (0, _chunkNK64H3RMcjs.RecipientNotReadyError)(recipientMsg, { cause: makeEngineError(res) });
94
97
  if (isAffordabilityCode(code)) {
95
- throw new (0, _chunkWQWNPAYQcjs.InsufficientFundsError)(
96
- `XRPL payment rejected (${code}): insufficient balance/reserve, or no trustline for this asset.`
98
+ throw new (0, _chunkNK64H3RMcjs.InsufficientFundsError)(
99
+ `XRPL payment rejected: the sender can't cover it \u2014 balance or the 1 XRP base reserve. (XRPL: ${code})`,
100
+ { cause: makeEngineError(res) }
97
101
  );
98
102
  }
99
- throw new Error(
100
- `XRPL payment rejected: ${code}${res.engine_result_message ? ` \u2014 ${res.engine_result_message}` : ""}`
101
- );
103
+ throw makeEngineError(res);
102
104
  } catch (err) {
103
- if (err instanceof _chunkWQWNPAYQcjs.InsufficientFundsError) throw err;
104
- throw _nullishCoalesce(_chunkWQWNPAYQcjs.toInsufficientFundsError.call(void 0, err), () => ( err));
105
+ if (err instanceof _chunkNK64H3RMcjs.InsufficientFundsError || err instanceof _chunkNK64H3RMcjs.RecipientNotReadyError) throw err;
106
+ throw _nullishCoalesce(_chunkNK64H3RMcjs.toInsufficientFundsError.call(void 0, err), () => ( err));
105
107
  }
106
108
  }
107
109
  function amountForAccept(accept) {
@@ -118,11 +120,25 @@ function feeForSubmit(openLedgerFee) {
118
120
  const fee = Number(openLedgerFee);
119
121
  return String(Number.isFinite(fee) && fee > 12 ? Math.ceil(fee) : 12);
120
122
  }
121
- function isAffordabilityCode(code) {
122
- return /^(tecUNFUNDED|tecPATH_DRY|tecPATH_PARTIAL|tecINSUFF|tecNO_LINE_INSUF|terINSUF|tecNO_LINE)/.test(
123
- code
123
+ function makeEngineError(res) {
124
+ return new Error(
125
+ `XRPL engine result ${res.engine_result}${res.engine_result_message ? ` \u2014 ${res.engine_result_message}` : ""}`
124
126
  );
125
127
  }
128
+ function recipientNotReadyMessage(code) {
129
+ if (/^tecNO_DST/.test(code))
130
+ return `XRPL destination isn't activated \u2014 an account must hold \u22651 XRP (the base reserve) to exist. Fund the destination with \u22651 XRP before it can receive. (XRPL: ${code})`;
131
+ if (/^tecNO_LINE/.test(code) || code === "tecPATH_DRY")
132
+ return `XRPL destination can't receive this currency \u2014 it needs a trustline for it (and the reserve to hold one). (XRPL: ${code})`;
133
+ if (code === "tecDST_TAG_NEEDED")
134
+ return `XRPL destination requires a DestinationTag to receive. (XRPL: ${code})`;
135
+ if (/^(tecNO_AUTH|tecNO_PERMISSION)/.test(code))
136
+ return `XRPL destination isn't authorized to hold this asset. (XRPL: ${code})`;
137
+ return null;
138
+ }
139
+ function isAffordabilityCode(code) {
140
+ return /^(tecUNFUNDED|tecINSUFF|terINSUF)/.test(code);
141
+ }
126
142
 
127
143
  // src/drivers/xrpl/verify.ts
128
144
  var RIPPLE_EPOCH_OFFSET = 946684800;
@@ -212,7 +228,7 @@ function deliveredBaseUnits(delivered, want, decimals) {
212
228
  return null;
213
229
  }
214
230
  try {
215
- return _chunkWQWNPAYQcjs.floorUnits.call(void 0, delivered.value, decimals);
231
+ return _chunkNK64H3RMcjs.floorUnits.call(void 0, delivered.value, decimals);
216
232
  } catch (e3) {
217
233
  return null;
218
234
  }
@@ -236,22 +252,22 @@ function rpcFailed(nonce) {
236
252
 
237
253
  function assertXrplWallet(wallet, network) {
238
254
  if (typeof wallet !== "object" || wallet === null) {
239
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
255
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
240
256
  `chain ${network} is XRPL; wallet must be { seed } (s\u2026 seed) or { wallet }.`
241
257
  );
242
258
  }
243
259
  if ("privateKey" in wallet || "walletClient" in wallet) {
244
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
260
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
245
261
  `chain ${network} is XRPL; an EVM wallet can't be used \u2014 pass { seed } (s\u2026 seed) or { wallet }.`
246
262
  );
247
263
  }
248
264
  if ("secretKey" in wallet || "signer" in wallet || "mnemonic" in wallet || "keyPair" in wallet || "secret" in wallet || "keypair" in wallet) {
249
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
265
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
250
266
  `chain ${network} is XRPL; that looks like a Solana/TON/Stellar wallet \u2014 pass { seed } (s\u2026 seed) or { wallet }.`
251
267
  );
252
268
  }
253
269
  if (!("seed" in wallet) && !("wallet" in wallet)) {
254
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
270
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
255
271
  `chain ${network} is XRPL; wallet must be { seed } (s\u2026 seed) or { wallet }.`
256
272
  );
257
273
  }
@@ -261,11 +277,11 @@ function resolveXrplWallet(config) {
261
277
  if (config.wallet) return config.wallet;
262
278
  if (config.seed) {
263
279
  if (!_xrpl.isValidSecret.call(void 0, config.seed)) {
264
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)("XRPL wallet { seed } is not a valid s\u2026 secret seed.");
280
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)("XRPL wallet { seed } is not a valid s\u2026 secret seed.");
265
281
  }
266
282
  return _xrpl.Wallet.fromSeed(config.seed);
267
283
  }
268
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)("XRPL wallet needs { seed } (s\u2026 seed) or { wallet }.");
284
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)("XRPL wallet needs { seed } (s\u2026 seed) or { wallet }.");
269
285
  }
270
286
 
271
287
  // src/drivers/xrpl/index.ts
@@ -337,7 +353,7 @@ function makeXrplNetwork(preset, rpcUrl) {
337
353
  const info = preset.tokens[token.toUpperCase()];
338
354
  if (!info) {
339
355
  const known = Object.keys(preset.tokens).join(", ") || "(none built in)";
340
- throw new (0, _chunkWQWNPAYQcjs.UnknownTokenError)(
356
+ throw new (0, _chunkNK64H3RMcjs.UnknownTokenError)(
341
357
  `token "${token}" isn't built in for XRPL (known: ${known}). Pass { issuer, currencyHex, decimals } for a custom IOU, or use 'native'.`
342
358
  );
343
359
  }
@@ -347,10 +363,10 @@ function makeXrplNetwork(preset, rpcUrl) {
347
363
  symbol: info.symbol
348
364
  };
349
365
  }
350
- _chunkWQWNPAYQcjs.rejectForeignToken.call(void 0, token, "xrpl", network);
366
+ _chunkNK64H3RMcjs.rejectForeignToken.call(void 0, token, "xrpl", network);
351
367
  const t = token;
352
368
  if (!t.issuer || !t.currencyHex || typeof t.decimals !== "number") {
353
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
369
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
354
370
  `chain ${network} is XRPL; a custom token must be { issuer, currencyHex, decimals }.`
355
371
  );
356
372
  }
@@ -371,12 +387,12 @@ function makeXrplNetwork(preset, rpcUrl) {
371
387
  },
372
388
  assertValidPayTo(payTo) {
373
389
  if (payTo.startsWith("0x")) {
374
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
390
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
375
391
  `chain ${network} is XRPL, but payTo "${payTo}" looks like an EVM address.`
376
392
  );
377
393
  }
378
394
  if (!_xrpl.isValidClassicAddress.call(void 0, payTo)) {
379
- throw new (0, _chunkWQWNPAYQcjs.WrongFamilyError)(
395
+ throw new (0, _chunkNK64H3RMcjs.WrongFamilyError)(
380
396
  `chain ${network} is XRPL, but payTo "${payTo}" is not a valid XRPL account (r\u2026).`
381
397
  );
382
398
  }
@@ -399,14 +415,14 @@ function makeXrplNetwork(preset, rpcUrl) {
399
415
  }
400
416
  await _chunkCQREG5LEcjs.delay.call(void 0, 1500);
401
417
  }
402
- throw new (0, _chunkWQWNPAYQcjs.ConfirmationTimeoutError)(`XRPL tx ${ref} not validated on-ledger in time.`);
418
+ throw new (0, _chunkNK64H3RMcjs.ConfirmationTimeoutError)(`XRPL tx ${ref} not validated on-ledger in time.`);
403
419
  },
404
420
  async estimateCost() {
405
421
  try {
406
422
  const drops = await payClient.feeDrops();
407
423
  const n = Number(drops);
408
424
  const fee = BigInt(Number.isFinite(n) && n > 12 ? Math.ceil(n) : 12);
409
- return _chunkWQWNPAYQcjs.nativeCost.call(void 0, {
425
+ return _chunkNK64H3RMcjs.nativeCost.call(void 0, {
410
426
  symbol: XRP_SYMBOL,
411
427
  decimals: XRP_DECIMALS,
412
428
  fee,
@@ -414,7 +430,7 @@ function makeXrplNetwork(preset, rpcUrl) {
414
430
  detail: `network fee ${fee} drops`
415
431
  });
416
432
  } catch (e5) {
417
- return _chunkWQWNPAYQcjs.nativeCost.call(void 0, {
433
+ return _chunkNK64H3RMcjs.nativeCost.call(void 0, {
418
434
  symbol: XRP_SYMBOL,
419
435
  decimals: XRP_DECIMALS,
420
436
  fee: 12n,
@@ -4,13 +4,14 @@ import {
4
4
  import {
5
5
  ConfirmationTimeoutError,
6
6
  InsufficientFundsError,
7
+ RecipientNotReadyError,
7
8
  UnknownTokenError,
8
9
  WrongFamilyError,
9
10
  floorUnits,
10
11
  nativeCost,
11
12
  rejectForeignToken,
12
13
  toInsufficientFundsError
13
- } from "./chunk-3TQJJ4SQ.js";
14
+ } from "./chunk-DTIJYDG6.js";
14
15
 
15
16
  // src/drivers/xrpl/index.ts
16
17
  import { isValidClassicAddress } from "xrpl";
@@ -91,16 +92,17 @@ async function payXrpl(params) {
91
92
  const res = await client.submit(signed.tx_blob);
92
93
  const code = res.engine_result;
93
94
  if (code.startsWith("tes")) return res.tx_json?.hash ?? signed.hash;
95
+ const recipientMsg = recipientNotReadyMessage(code);
96
+ if (recipientMsg) throw new RecipientNotReadyError(recipientMsg, { cause: makeEngineError(res) });
94
97
  if (isAffordabilityCode(code)) {
95
98
  throw new InsufficientFundsError(
96
- `XRPL payment rejected (${code}): insufficient balance/reserve, or no trustline for this asset.`
99
+ `XRPL payment rejected: the sender can't cover it \u2014 balance or the 1 XRP base reserve. (XRPL: ${code})`,
100
+ { cause: makeEngineError(res) }
97
101
  );
98
102
  }
99
- throw new Error(
100
- `XRPL payment rejected: ${code}${res.engine_result_message ? ` \u2014 ${res.engine_result_message}` : ""}`
101
- );
103
+ throw makeEngineError(res);
102
104
  } catch (err) {
103
- if (err instanceof InsufficientFundsError) throw err;
105
+ if (err instanceof InsufficientFundsError || err instanceof RecipientNotReadyError) throw err;
104
106
  throw toInsufficientFundsError(err) ?? err;
105
107
  }
106
108
  }
@@ -118,11 +120,25 @@ function feeForSubmit(openLedgerFee) {
118
120
  const fee = Number(openLedgerFee);
119
121
  return String(Number.isFinite(fee) && fee > 12 ? Math.ceil(fee) : 12);
120
122
  }
121
- function isAffordabilityCode(code) {
122
- return /^(tecUNFUNDED|tecPATH_DRY|tecPATH_PARTIAL|tecINSUFF|tecNO_LINE_INSUF|terINSUF|tecNO_LINE)/.test(
123
- code
123
+ function makeEngineError(res) {
124
+ return new Error(
125
+ `XRPL engine result ${res.engine_result}${res.engine_result_message ? ` \u2014 ${res.engine_result_message}` : ""}`
124
126
  );
125
127
  }
128
+ function recipientNotReadyMessage(code) {
129
+ if (/^tecNO_DST/.test(code))
130
+ return `XRPL destination isn't activated \u2014 an account must hold \u22651 XRP (the base reserve) to exist. Fund the destination with \u22651 XRP before it can receive. (XRPL: ${code})`;
131
+ if (/^tecNO_LINE/.test(code) || code === "tecPATH_DRY")
132
+ return `XRPL destination can't receive this currency \u2014 it needs a trustline for it (and the reserve to hold one). (XRPL: ${code})`;
133
+ if (code === "tecDST_TAG_NEEDED")
134
+ return `XRPL destination requires a DestinationTag to receive. (XRPL: ${code})`;
135
+ if (/^(tecNO_AUTH|tecNO_PERMISSION)/.test(code))
136
+ return `XRPL destination isn't authorized to hold this asset. (XRPL: ${code})`;
137
+ return null;
138
+ }
139
+ function isAffordabilityCode(code) {
140
+ return /^(tecUNFUNDED|tecINSUFF|terINSUF)/.test(code);
141
+ }
126
142
 
127
143
  // src/drivers/xrpl/verify.ts
128
144
  var RIPPLE_EPOCH_OFFSET = 946684800;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piprail/sdk",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Accept x402 crypto payments across 24 chains — every major EVM chain plus Solana, TON, Tron, NEAR, Sui, 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",
@@ -21,6 +21,7 @@
21
21
  "files": [
22
22
  "dist",
23
23
  "README.md",
24
+ "CHAINS.md",
24
25
  "ERRORS.md",
25
26
  "STANDARDS.md",
26
27
  "CHANGELOG.md",
@@ -70,6 +71,9 @@
70
71
  },
71
72
  "homepage": "https://piprail.com",
72
73
  "bugs": "https://github.com/piprail/piprail/issues",
74
+ "publishConfig": {
75
+ "access": "public"
76
+ },
73
77
  "engines": {
74
78
  "node": ">=20"
75
79
  },