@piprail/sdk 2.16.2 → 3.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.
- package/CHANGELOG.md +448 -0
- package/README.md +1 -0
- package/dist/{algorand-Q3TUQLPK.cjs → algorand-4FTTEV7X.cjs} +212 -51
- package/dist/{algorand-25FMBCT3.js → algorand-KJ5XHSMT.js} +164 -3
- package/dist/{aptos-VBJONBFY.cjs → aptos-5QBXJ6NV.cjs} +255 -39
- package/dist/{aptos-SBV6SGDP.js → aptos-H5VLH2QL.js} +217 -1
- package/dist/{chunk-QONQSZHJ.cjs → chunk-6XTNI2OQ.cjs} +44 -44
- package/dist/{chunk-OXEFPLZA.cjs → chunk-6ZRAIQXF.cjs} +4 -4
- package/dist/chunk-GVCGUSTE.js +30 -0
- package/dist/{chunk-V2IJ5HUW.cjs → chunk-MZAJQYM3.cjs} +1 -0
- package/dist/chunk-MZXVXC3C.cjs +30 -0
- package/dist/{chunk-2CX7XRZK.js → chunk-TZDVZCTC.js} +1 -0
- package/dist/{chunk-C52H5TYB.js → chunk-YXBQKBDH.js} +2 -2
- package/dist/index.cjs +2906 -1262
- package/dist/index.d.cts +679 -21
- package/dist/index.d.ts +679 -21
- package/dist/index.js +2678 -1034
- package/dist/{ledger-Crc1bZox.d.cts → ledger-DkHUORUe.d.cts} +7 -0
- package/dist/{ledger-Crc1bZox.d.ts → ledger-DkHUORUe.d.ts} +7 -0
- package/dist/{near-OLKCMTBI.js → near-5X2CQGML.js} +197 -1
- package/dist/{near-5LTTDU6G.cjs → near-XSQXEIHT.cjs} +242 -46
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/{solana-AI2G7V33.cjs → solana-TIEJV742.cjs} +218 -53
- package/dist/{solana-HTKDRTD3.js → solana-WRXL54MR.js} +174 -9
- package/dist/{stellar-E2KWEV2E.cjs → stellar-5GMZJBTA.cjs} +200 -30
- package/dist/{stellar-EUFZLX6J.js → stellar-YQLOIFDD.js} +172 -2
- package/dist/{sui-KCIITCYH.js → sui-JFQSDNSZ.js} +165 -3
- package/dist/{sui-Q3NJOJZS.cjs → sui-WIZEAO7E.cjs} +185 -23
- package/dist/{ton-J7TQWRN4.cjs → ton-AGTKLQA5.cjs} +264 -22
- package/dist/{ton-WH2JVQOO.js → ton-OZKFNRLT.js} +244 -2
- package/dist/{tron-FXBXDNEY.js → tron-HG3IOOCG.js} +283 -1
- package/dist/{tron-HIPMOX7S.cjs → tron-S4WX7OHY.cjs} +323 -41
- package/dist/{xrpl-RUOB37QH.js → xrpl-5K444RGX.js} +238 -6
- package/dist/{xrpl-SVVS445B.cjs → xrpl-G2FKFXRI.cjs} +289 -57
- package/package.json +12 -2
- package/dist/{chunk-QU25LSVS.js → chunk-5GRBEMCA.js} +44 -44
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
exactTransferMethod
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-YXBQKBDH.js";
|
|
4
|
+
import "./chunk-TZDVZCTC.js";
|
|
5
|
+
import {
|
|
6
|
+
applySlippage
|
|
7
|
+
} from "./chunk-GVCGUSTE.js";
|
|
5
8
|
import {
|
|
6
9
|
delay
|
|
7
10
|
} from "./chunk-FURB5RP7.js";
|
|
@@ -15,10 +18,11 @@ import {
|
|
|
15
18
|
WrongFamilyError,
|
|
16
19
|
assertNoLegacyWalletKey,
|
|
17
20
|
floorUnits,
|
|
21
|
+
formatUnits,
|
|
18
22
|
nativeCost,
|
|
19
23
|
rejectForeignToken,
|
|
20
24
|
toInsufficientFundsError
|
|
21
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-5GRBEMCA.js";
|
|
22
26
|
|
|
23
27
|
// src/drivers/xrpl/index.ts
|
|
24
28
|
import { isValidClassicAddress } from "xrpl";
|
|
@@ -85,6 +89,13 @@ async function payXrpl(params) {
|
|
|
85
89
|
client.feeDrops(),
|
|
86
90
|
client.currentLedgerIndex()
|
|
87
91
|
]);
|
|
92
|
+
for (const [field, value] of [["Sequence", sequence], ["LastLedgerSequence", ledgerIndex]]) {
|
|
93
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`XRPL: could not read ${field} from the ledger (got ${String(value)}) \u2014 the RPC read failed or was throttled. NOTHING was signed or submitted, so this is safe to retry. A dedicated rpcUrl avoids the public cluster's rate limits.`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
88
99
|
const tx = {
|
|
89
100
|
TransactionType: "Payment",
|
|
90
101
|
Account: wallet.classicAddress,
|
|
@@ -150,6 +161,197 @@ function isAffordabilityCode(code) {
|
|
|
150
161
|
return /^(tecUNFUNDED|tecINSUFF|terINSUF)/.test(code);
|
|
151
162
|
}
|
|
152
163
|
|
|
164
|
+
// src/drivers/xrpl/swap.ts
|
|
165
|
+
var SOURCE = {
|
|
166
|
+
kind: "protocol",
|
|
167
|
+
name: "XRPL DEX + AMM",
|
|
168
|
+
note: "Rate from the ledger\u2019s own order books and AMM pools, read live via ripple_path_find, with auto-bridging through XRP where cheaper. Order books charge no protocol fee; AMM pools charge a per-pool fee (0%\u20131%) paid to the pool."
|
|
169
|
+
};
|
|
170
|
+
function toAmount(t, base) {
|
|
171
|
+
if (t.asset === "native") return base.toString();
|
|
172
|
+
const parts = parseXrplAssetId(t.asset);
|
|
173
|
+
if (!parts) throw new Error(`XRPL: malformed asset id "${t.asset}".`);
|
|
174
|
+
return { currency: parts.currencyHex, issuer: parts.issuer, value: formatUnits(base, t.decimals) };
|
|
175
|
+
}
|
|
176
|
+
function parseCeil(value, decimals) {
|
|
177
|
+
const [whole = "0", frac = ""] = value.trim().split(".");
|
|
178
|
+
const kept = frac.slice(0, decimals).padEnd(decimals, "0");
|
|
179
|
+
const rest = frac.slice(decimals);
|
|
180
|
+
const base = BigInt(whole + kept);
|
|
181
|
+
return /[1-9]/.test(rest) ? base + 1n : base;
|
|
182
|
+
}
|
|
183
|
+
function fromAmount(t, a) {
|
|
184
|
+
if (a == null) return null;
|
|
185
|
+
try {
|
|
186
|
+
return typeof a === "string" ? BigInt(a) : parseCeil(a.value, t.decimals);
|
|
187
|
+
} catch {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function sourceCurrency(t) {
|
|
192
|
+
if (t.asset === "native") return { currency: "XRP" };
|
|
193
|
+
const parts = parseXrplAssetId(t.asset);
|
|
194
|
+
if (!parts) throw new Error(`XRPL: malformed asset id "${t.asset}".`);
|
|
195
|
+
return { currency: parts.currencyHex, issuer: parts.issuer };
|
|
196
|
+
}
|
|
197
|
+
function side(t, amount) {
|
|
198
|
+
return {
|
|
199
|
+
asset: t.asset,
|
|
200
|
+
symbol: t.symbol ?? (t.asset === "native" ? XRP_SYMBOL : t.asset),
|
|
201
|
+
decimals: t.asset === "native" ? XRP_DECIMALS : t.decimals,
|
|
202
|
+
amount: amount.toString(),
|
|
203
|
+
amountFormatted: formatUnits(amount, t.asset === "native" ? XRP_DECIMALS : t.decimals)
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
async function quoteXrplSwap(p) {
|
|
207
|
+
if (p.from.asset === p.to.asset) return null;
|
|
208
|
+
if (p.wantAmount <= 0n) return null;
|
|
209
|
+
let alternatives = [];
|
|
210
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
211
|
+
try {
|
|
212
|
+
const res = await p.client.pathFind({
|
|
213
|
+
source_account: p.owner,
|
|
214
|
+
destination_account: p.owner,
|
|
215
|
+
// ← ourselves: this is what makes it a swap
|
|
216
|
+
destination_amount: toAmount(p.to, p.wantAmount),
|
|
217
|
+
source_currencies: [sourceCurrency(p.from)]
|
|
218
|
+
});
|
|
219
|
+
alternatives = res.alternatives ?? [];
|
|
220
|
+
} catch {
|
|
221
|
+
alternatives = [];
|
|
222
|
+
}
|
|
223
|
+
if (alternatives.length) break;
|
|
224
|
+
if (attempt === 0) await new Promise((r) => setTimeout(r, 600));
|
|
225
|
+
}
|
|
226
|
+
if (!alternatives.length) return null;
|
|
227
|
+
let best = null;
|
|
228
|
+
let bestSpend = null;
|
|
229
|
+
for (const alt of alternatives) {
|
|
230
|
+
const spend = fromAmount(p.from, alt.source_amount);
|
|
231
|
+
if (spend == null || spend <= 0n) continue;
|
|
232
|
+
if (bestSpend == null || spend < bestSpend) {
|
|
233
|
+
bestSpend = spend;
|
|
234
|
+
best = alt;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (!best || bestSpend == null) return null;
|
|
238
|
+
const maxSpend = applySlippage(bestSpend, p.slippageBps);
|
|
239
|
+
return {
|
|
240
|
+
source: SOURCE,
|
|
241
|
+
network: p.network,
|
|
242
|
+
from: side(p.from, bestSpend),
|
|
243
|
+
to: side(p.to, p.wantAmount),
|
|
244
|
+
maxSpend: maxSpend.toString(),
|
|
245
|
+
maxSpendFormatted: formatUnits(maxSpend, p.from.asset === "native" ? XRP_DECIMALS : p.from.decimals),
|
|
246
|
+
slippageBps: p.slippageBps,
|
|
247
|
+
// `paths_computed` drops straight into the transaction's Paths field.
|
|
248
|
+
route: { paths: best.paths_computed ?? [], from: p.from, to: p.to }
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function sanitizePaths(paths) {
|
|
252
|
+
if (!Array.isArray(paths)) return [];
|
|
253
|
+
return paths.map(
|
|
254
|
+
(path) => (Array.isArray(path) ? path : []).map((raw) => {
|
|
255
|
+
const step = raw;
|
|
256
|
+
const clean = {};
|
|
257
|
+
if (typeof step.account === "string") clean.account = step.account;
|
|
258
|
+
if (typeof step.currency === "string") clean.currency = step.currency;
|
|
259
|
+
if (typeof step.issuer === "string") clean.issuer = step.issuer;
|
|
260
|
+
return clean;
|
|
261
|
+
}).filter((step) => Object.keys(step).length > 0)
|
|
262
|
+
).filter((path) => path.length > 0);
|
|
263
|
+
}
|
|
264
|
+
async function swapXrpl(p) {
|
|
265
|
+
const { client, wallet, quote } = p;
|
|
266
|
+
const route = quote.route;
|
|
267
|
+
if (!route?.from || !route?.to) {
|
|
268
|
+
throw new Error("XRPL: swap quote is missing its routing data \u2014 re-quote before swapping.");
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
const [sequence, feeDrops, ledgerIndex] = await Promise.all([
|
|
272
|
+
client.accountSequence(wallet.classicAddress),
|
|
273
|
+
client.feeDrops(),
|
|
274
|
+
client.currentLedgerIndex()
|
|
275
|
+
]);
|
|
276
|
+
const tx = {
|
|
277
|
+
TransactionType: "Payment",
|
|
278
|
+
Account: wallet.classicAddress,
|
|
279
|
+
Destination: wallet.classicAddress,
|
|
280
|
+
// ← ourselves: the ledger's own swap idiom
|
|
281
|
+
Amount: toAmount(route.to, BigInt(quote.to.amount)),
|
|
282
|
+
// exact output
|
|
283
|
+
SendMax: toAmount(route.from, BigInt(quote.maxSpend)),
|
|
284
|
+
// the on-chain slippage cap
|
|
285
|
+
// Sanitized: raw path steps from ripple_path_find can break xrpl.js encoding.
|
|
286
|
+
...sanitizePaths(route.paths).length ? { Paths: sanitizePaths(route.paths) } : {},
|
|
287
|
+
Sequence: sequence,
|
|
288
|
+
Fee: feeForSubmit2(feeDrops),
|
|
289
|
+
LastLedgerSequence: ledgerIndex + 20,
|
|
290
|
+
// NOT tfPartialPayment: we want the full `Amount` delivered or nothing at all.
|
|
291
|
+
Flags: 0
|
|
292
|
+
};
|
|
293
|
+
let signed;
|
|
294
|
+
try {
|
|
295
|
+
signed = wallet.sign(tx);
|
|
296
|
+
} catch (cause) {
|
|
297
|
+
const shape = Object.fromEntries(
|
|
298
|
+
Object.entries(tx).map(([k, v]) => [k, typeof v === "object" ? JSON.stringify(v) : `${typeof v}:${String(v)}`])
|
|
299
|
+
);
|
|
300
|
+
throw new Error(
|
|
301
|
+
`XRPL could not encode the swap transaction: ${String(cause?.message ?? cause)}. Transaction was ${JSON.stringify(shape)}`,
|
|
302
|
+
{ cause }
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
const res = await client.submit(signed.tx_blob);
|
|
306
|
+
const code = res.engine_result;
|
|
307
|
+
if (code.startsWith("tes")) {
|
|
308
|
+
return {
|
|
309
|
+
transaction: res.tx_json?.hash ?? signed.hash,
|
|
310
|
+
network: quote.network,
|
|
311
|
+
source: quote.source,
|
|
312
|
+
from: quote.from,
|
|
313
|
+
to: quote.to
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
throw mapEngineResult(code, res);
|
|
317
|
+
} catch (err) {
|
|
318
|
+
if (err instanceof InsufficientFundsError || err instanceof RecipientNotReadyError) throw err;
|
|
319
|
+
throw toInsufficientFundsError(err) ?? err;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function feeForSubmit2(openLedgerFee) {
|
|
323
|
+
try {
|
|
324
|
+
const f = BigInt(openLedgerFee);
|
|
325
|
+
const padded = f * 2n;
|
|
326
|
+
return (padded < 10n ? 10n : padded).toString();
|
|
327
|
+
} catch {
|
|
328
|
+
return "12";
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function mapEngineResult(code, res) {
|
|
332
|
+
const err = new Error(`XRPL swap rejected: ${code}`);
|
|
333
|
+
err.cause = res;
|
|
334
|
+
if (code === "tecPATH_PARTIAL" || code === "tecPATH_DRY") {
|
|
335
|
+
return new InsufficientFundsError(
|
|
336
|
+
`XRPL swap refused: the price moved past your slippage cap, or the path ran dry, so nothing was spent. Re-quote and retry, or raise slippageBps. (XRPL: ${code})`,
|
|
337
|
+
{ cause: res }
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
if (code === "tecNO_LINE" || code === "tecNO_AUTH" || code === "tecNO_ISSUER") {
|
|
341
|
+
return new RecipientNotReadyError(
|
|
342
|
+
`XRPL swap refused: your account needs a trustline for the currency you're swapping INTO before it can hold it. (XRPL: ${code})`,
|
|
343
|
+
{ cause: res }
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
if (code === "tecUNFUNDED_PAYMENT" || code === "terINSUF_FEE_B" || code === "tecINSUFF_FEE") {
|
|
347
|
+
return new InsufficientFundsError(
|
|
348
|
+
`XRPL swap failed: the account can't cover it \u2014 balance or the 1 XRP base reserve. (XRPL: ${code})`,
|
|
349
|
+
{ cause: res }
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
return err;
|
|
353
|
+
}
|
|
354
|
+
|
|
153
355
|
// src/drivers/xrpl/exact.ts
|
|
154
356
|
import { createHash } from "crypto";
|
|
155
357
|
var SECONDS_PER_LEDGER = 4;
|
|
@@ -170,7 +372,7 @@ function feesAreSponsored(accept) {
|
|
|
170
372
|
function invoiceIdHash(invoiceId) {
|
|
171
373
|
return createHash("sha256").update(invoiceId, "utf8").digest("hex").toUpperCase();
|
|
172
374
|
}
|
|
173
|
-
function
|
|
375
|
+
function feeForSubmit3(openLedgerFee) {
|
|
174
376
|
const fee = Number(openLedgerFee);
|
|
175
377
|
const chosen = Number.isFinite(fee) && fee > MIN_FEE_DROPS ? Math.ceil(fee) : MIN_FEE_DROPS;
|
|
176
378
|
return String(Math.min(chosen, MAX_FEE_DROPS));
|
|
@@ -224,7 +426,7 @@ async function payExactXrpl(input) {
|
|
|
224
426
|
Amount: accept.amount,
|
|
225
427
|
// integer drops, verbatim from the trusted accept
|
|
226
428
|
Sequence: sequence,
|
|
227
|
-
Fee:
|
|
429
|
+
Fee: feeForSubmit3(openLedgerFee),
|
|
228
430
|
LastLedgerSequence: ledgerIndex + ledgerWindowFor(accept.maxTimeoutSeconds),
|
|
229
431
|
// tfFullyCanonicalSig ON, tfPartialPayment OFF — the shape every other XRPL client sends.
|
|
230
432
|
Flags: TF_FULLY_CANONICAL_SIG
|
|
@@ -500,6 +702,8 @@ function resolveXrplWallet(config) {
|
|
|
500
702
|
function isXrplActNotFound(e) {
|
|
501
703
|
return /actNotFound/i.test(String(e?.message ?? e));
|
|
502
704
|
}
|
|
705
|
+
var XRPL_BASE_RESERVE_DROPS = 1000000n;
|
|
706
|
+
var XRPL_OWNER_RESERVE_DROPS = 200000n;
|
|
503
707
|
var xrplDriver = {
|
|
504
708
|
family: "xrpl",
|
|
505
709
|
resolve(opts) {
|
|
@@ -525,6 +729,9 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
525
729
|
return result;
|
|
526
730
|
}
|
|
527
731
|
const payClient = {
|
|
732
|
+
async pathFind(params) {
|
|
733
|
+
return rpc("ripple_path_find", { ...params, ledger_index: "current" });
|
|
734
|
+
},
|
|
528
735
|
async accountSequence(account) {
|
|
529
736
|
const r = await rpc("account_info", {
|
|
530
737
|
account,
|
|
@@ -713,6 +920,11 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
713
920
|
});
|
|
714
921
|
}
|
|
715
922
|
},
|
|
923
|
+
/** The bound wallet's own address — where THIS wallet gets paid. Derived from the key
|
|
924
|
+
* material only: no RPC, nothing moved. See {@link ResolvedNetwork.addressOf}. */
|
|
925
|
+
async addressOf(wallet) {
|
|
926
|
+
return resolveXrplWallet(wallet._native).classicAddress;
|
|
927
|
+
},
|
|
716
928
|
async balanceOf(wallet, asset) {
|
|
717
929
|
let owner;
|
|
718
930
|
try {
|
|
@@ -721,16 +933,22 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
721
933
|
return { token: null, native: null };
|
|
722
934
|
}
|
|
723
935
|
let native = null;
|
|
936
|
+
let ownerCount = 0n;
|
|
724
937
|
try {
|
|
725
938
|
const r = await rpc("account_info", {
|
|
726
939
|
account: owner,
|
|
727
940
|
ledger_index: "validated"
|
|
728
941
|
});
|
|
729
942
|
native = BigInt(r.account_data.Balance);
|
|
943
|
+
ownerCount = BigInt(r.account_data.OwnerCount ?? 0);
|
|
730
944
|
} catch (e) {
|
|
731
945
|
native = isXrplActNotFound(e) ? 0n : null;
|
|
732
946
|
}
|
|
733
|
-
if (isXrpNative(asset))
|
|
947
|
+
if (isXrpNative(asset)) {
|
|
948
|
+
if (native === null) return { token: null, native };
|
|
949
|
+
const reserve = XRPL_BASE_RESERVE_DROPS + XRPL_OWNER_RESERVE_DROPS * ownerCount;
|
|
950
|
+
return { token: native > reserve ? native - reserve : 0n, native };
|
|
951
|
+
}
|
|
734
952
|
let token = null;
|
|
735
953
|
try {
|
|
736
954
|
const [currencyHex, issuer] = asset.split(":");
|
|
@@ -769,6 +987,20 @@ function makeXrplNetwork(preset, rpcUrl) {
|
|
|
769
987
|
return { ready: "unknown" };
|
|
770
988
|
}
|
|
771
989
|
},
|
|
990
|
+
/* ---- swap (OPTIONAL, opt-in): a cross-currency payment to yourself. See ./swap.ts ---- */
|
|
991
|
+
async quoteSwap({ from, to, wantAmount, slippageBps, wallet }) {
|
|
992
|
+
let owner;
|
|
993
|
+
try {
|
|
994
|
+
owner = resolveXrplWallet(wallet._native).classicAddress;
|
|
995
|
+
} catch {
|
|
996
|
+
return null;
|
|
997
|
+
}
|
|
998
|
+
return quoteXrplSwap({ client: payClient, network, owner, from, to, wantAmount, slippageBps });
|
|
999
|
+
},
|
|
1000
|
+
async swap(wallet, quote) {
|
|
1001
|
+
const w = resolveXrplWallet(wallet._native);
|
|
1002
|
+
return swapXrpl({ client: payClient, wallet: w, quote });
|
|
1003
|
+
},
|
|
772
1004
|
async verify(_ref, accept) {
|
|
773
1005
|
return verifyXrpl({ reader, accept });
|
|
774
1006
|
}
|