@piprail/sdk 2.16.2 → 3.0.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.
Files changed (33) hide show
  1. package/CHANGELOG.md +343 -0
  2. package/README.md +1 -0
  3. package/dist/{algorand-25FMBCT3.js → algorand-HL57PQHE.js} +157 -2
  4. package/dist/{algorand-Q3TUQLPK.cjs → algorand-W776EEM2.cjs} +205 -50
  5. package/dist/{aptos-VBJONBFY.cjs → aptos-5QBXJ6NV.cjs} +255 -39
  6. package/dist/{aptos-SBV6SGDP.js → aptos-H5VLH2QL.js} +217 -1
  7. package/dist/{chunk-QONQSZHJ.cjs → chunk-6XTNI2OQ.cjs} +44 -44
  8. package/dist/{chunk-OXEFPLZA.cjs → chunk-6ZRAIQXF.cjs} +4 -4
  9. package/dist/chunk-GVCGUSTE.js +30 -0
  10. package/dist/{chunk-V2IJ5HUW.cjs → chunk-MZAJQYM3.cjs} +1 -0
  11. package/dist/chunk-MZXVXC3C.cjs +30 -0
  12. package/dist/{chunk-2CX7XRZK.js → chunk-TZDVZCTC.js} +1 -0
  13. package/dist/{chunk-C52H5TYB.js → chunk-YXBQKBDH.js} +2 -2
  14. package/dist/index.cjs +2929 -1444
  15. package/dist/index.d.cts +625 -13
  16. package/dist/index.d.ts +625 -13
  17. package/dist/index.js +2506 -1021
  18. package/dist/{near-OLKCMTBI.js → near-5X2CQGML.js} +197 -1
  19. package/dist/{near-5LTTDU6G.cjs → near-XSQXEIHT.cjs} +242 -46
  20. package/dist/{solana-AI2G7V33.cjs → solana-EBV6PUCU.cjs} +211 -52
  21. package/dist/{solana-HTKDRTD3.js → solana-O6Q6QILH.js} +167 -8
  22. package/dist/{stellar-E2KWEV2E.cjs → stellar-5C7FQLPS.cjs} +191 -29
  23. package/dist/{stellar-EUFZLX6J.js → stellar-YF5LOJEM.js} +163 -1
  24. package/dist/{sui-KCIITCYH.js → sui-JFQSDNSZ.js} +165 -3
  25. package/dist/{sui-Q3NJOJZS.cjs → sui-WIZEAO7E.cjs} +185 -23
  26. package/dist/{ton-J7TQWRN4.cjs → ton-AGTKLQA5.cjs} +264 -22
  27. package/dist/{ton-WH2JVQOO.js → ton-OZKFNRLT.js} +244 -2
  28. package/dist/{tron-FXBXDNEY.js → tron-HG3IOOCG.js} +283 -1
  29. package/dist/{tron-HIPMOX7S.cjs → tron-S4WX7OHY.cjs} +323 -41
  30. package/dist/{xrpl-RUOB37QH.js → xrpl-GOVHMYYK.js} +222 -5
  31. package/dist/{xrpl-SVVS445B.cjs → xrpl-YS3IXPLV.cjs} +273 -56
  32. package/package.json +12 -2
  33. package/dist/{chunk-QU25LSVS.js → chunk-5GRBEMCA.js} +44 -44
@@ -1,3 +1,6 @@
1
+ import {
2
+ applySlippage
3
+ } from "./chunk-GVCGUSTE.js";
1
4
  import {
2
5
  delay
3
6
  } from "./chunk-FURB5RP7.js";
@@ -7,10 +10,11 @@ import {
7
10
  UnknownTokenError,
8
11
  WrongFamilyError,
9
12
  assertNoLegacyWalletKey,
13
+ formatUnits,
10
14
  nativeCost,
11
15
  rejectForeignToken,
12
16
  toInsufficientFundsError
13
- } from "./chunk-QU25LSVS.js";
17
+ } from "./chunk-5GRBEMCA.js";
14
18
 
15
19
  // src/drivers/tron/index.ts
16
20
  import { TronWeb } from "tronweb";
@@ -121,6 +125,257 @@ function isTronAffordability(code, message) {
121
125
  return /balance is not sufficient|insufficient (funds|balance|energy)|not enough/i.test(decoded);
122
126
  }
123
127
 
128
+ // src/drivers/tron/swap.ts
129
+ var SUNSWAP_V2_ROUTER = "TKzxdSv2FZKQrEqkKVgp5DcwEXBEKMg2Ax";
130
+ var WTRX = "TNUC9Qb1rRpS5CbWLmNMxXBjyFoydXjWFR";
131
+ var TRX_SYMBOL = "TRX";
132
+ var TRON_SWAP_ENERGY = 230629;
133
+ var FEE_LIMIT_SUN = 4e7;
134
+ var APPROVE_FEE_LIMIT_SUN = 2e7;
135
+ var APPROVE_TIMEOUT_MS = 6e4;
136
+ var APPROVE_POLL_MS = 3e3;
137
+ var DEADLINE_SECONDS = 300;
138
+ function sourceFor(needsApproval) {
139
+ return {
140
+ kind: "provider",
141
+ name: "SunSwap V2",
142
+ note: "Third-party Tron DEX (SunSwap V2), called contract-to-contract with no API and no API key: the quote is a constant contract call and the swap is a router call your own key signs. Cost on this route: the pool's own 0.30% constant-product fee. PipRail adds nothing, and the router has no fee parameter to add. Note Tron charges roughly 23 TRX of ENERGY per swap regardless of size, which is usually the dominant cost" + (needsApproval ? ", and selling a TRC-20 costs a second transaction on top: an approve to the router, about another 10 TRX the first time, so budget roughly 33 TRX for this direction." : "; selling native TRX needs no approval, so this is a single transaction.")
143
+ };
144
+ }
145
+ function pathFor(from, to) {
146
+ const a = from.asset === "native" ? WTRX : from.asset;
147
+ const b = to.asset === "native" ? WTRX : to.asset;
148
+ return [a, b];
149
+ }
150
+ function side(t, amount) {
151
+ const decimals = t.asset === "native" ? TRX_DECIMALS : t.decimals;
152
+ return {
153
+ asset: t.asset,
154
+ symbol: t.symbol ?? (t.asset === "native" ? TRX_SYMBOL : t.asset),
155
+ decimals,
156
+ amount: amount.toString(),
157
+ amountFormatted: formatUnits(amount, decimals)
158
+ };
159
+ }
160
+ function decodeUintArray(hex) {
161
+ try {
162
+ if (!hex) return null;
163
+ const words = hex.match(/.{64}/g);
164
+ if (!words || words.length < 3) return null;
165
+ const len = Number(BigInt("0x" + words[1]));
166
+ if (!Number.isSafeInteger(len) || len < 2) return null;
167
+ const vals = words.slice(2, 2 + len).map((w) => BigInt("0x" + w));
168
+ return vals.length === len ? vals : null;
169
+ } catch {
170
+ return null;
171
+ }
172
+ }
173
+ async function quoteTronSwap(p) {
174
+ if (p.wantAmount <= 0n) return null;
175
+ const path = pathFor(p.from, p.to);
176
+ if (path[0] === path[1]) return null;
177
+ let amounts = null;
178
+ try {
179
+ const res = await p.client.transactionBuilder.triggerConstantContract(
180
+ SUNSWAP_V2_ROUTER,
181
+ "getAmountsIn(uint256,address[])",
182
+ {},
183
+ [
184
+ { type: "uint256", value: p.wantAmount.toString() },
185
+ { type: "address[]", value: path }
186
+ ],
187
+ p.owner
188
+ );
189
+ if (res?.result?.result !== true) return null;
190
+ amounts = decodeUintArray(res.constant_result?.[0]);
191
+ } catch {
192
+ return null;
193
+ }
194
+ if (!amounts?.length) return null;
195
+ const amountIn = amounts[0];
196
+ if (amountIn === void 0 || amountIn <= 0n) return null;
197
+ const amountInMax = applySlippage(amountIn, p.slippageBps);
198
+ return {
199
+ source: sourceFor(p.from.asset !== "native"),
200
+ network: p.network,
201
+ from: side(p.from, amountIn),
202
+ to: side(p.to, p.wantAmount),
203
+ maxSpend: amountInMax.toString(),
204
+ maxSpendFormatted: formatUnits(amountInMax, p.from.asset === "native" ? TRX_DECIMALS : p.from.decimals),
205
+ slippageBps: p.slippageBps,
206
+ route: {
207
+ router: SUNSWAP_V2_ROUTER,
208
+ path,
209
+ amountInMax: amountInMax.toString(),
210
+ fromIsNative: p.from.asset === "native",
211
+ toIsNative: p.to.asset === "native",
212
+ needsApproval: p.from.asset !== "native",
213
+ tokenIn: p.from.asset === "native" ? "" : p.from.asset
214
+ }
215
+ };
216
+ }
217
+ async function readAllowance(client, token, owner, spender) {
218
+ try {
219
+ const res = await client.transactionBuilder.triggerConstantContract(
220
+ token,
221
+ "allowance(address,address)",
222
+ {},
223
+ [
224
+ { type: "address", value: owner },
225
+ { type: "address", value: spender }
226
+ ],
227
+ owner
228
+ );
229
+ if (res?.result?.result !== true) return 0n;
230
+ const raw = res.constant_result?.[0];
231
+ return raw ? BigInt("0x" + raw.slice(0, 64)) : 0n;
232
+ } catch {
233
+ return 0n;
234
+ }
235
+ }
236
+ async function sendAndConfirm(client, contract, selector, params, owner, privateKey, feeLimit) {
237
+ const built = await client.transactionBuilder.triggerSmartContract(
238
+ contract,
239
+ selector,
240
+ { feeLimit },
241
+ params,
242
+ owner
243
+ );
244
+ if (built?.result?.result !== true) {
245
+ throw new Error(built?.result?.message ?? `Tron: ${selector} could not be built`);
246
+ }
247
+ const signed = await client.trx.sign(built.transaction, privateKey);
248
+ const broadcast = await client.trx.sendRawTransaction(signed);
249
+ if (broadcast?.result === false || broadcast?.code) {
250
+ throw new Error(`${broadcast.code ?? "broadcast failed"}: ${broadcast.message ?? ""}`);
251
+ }
252
+ const txid = broadcast?.txid ?? broadcast?.transaction?.txID ?? signed.txID;
253
+ const deadline = Date.now() + APPROVE_TIMEOUT_MS;
254
+ while (Date.now() < deadline) {
255
+ await new Promise((r) => setTimeout(r, APPROVE_POLL_MS));
256
+ let info = null;
257
+ try {
258
+ info = await client.trx.getTransactionInfo(txid);
259
+ } catch {
260
+ continue;
261
+ }
262
+ const result = info?.receipt?.result;
263
+ if (!result) continue;
264
+ if (result !== "SUCCESS") {
265
+ throw new Error(`Tron: ${selector} failed on-chain (${result}, tx ${txid})`);
266
+ }
267
+ return txid;
268
+ }
269
+ throw new Error(`Tron: ${selector} was not confirmed within ${APPROVE_TIMEOUT_MS / 1e3}s (tx ${txid})`);
270
+ }
271
+ function safeCallValue(sun) {
272
+ const n = Number(sun);
273
+ if (!Number.isSafeInteger(n)) {
274
+ throw new InsufficientFundsError(
275
+ `Tron: a native input of ${sun} sun exceeds the largest amount that can be sent without rounding. Split the swap into smaller amounts.`
276
+ );
277
+ }
278
+ return n;
279
+ }
280
+ async function swapTron(p) {
281
+ const route = p.quote.route;
282
+ if (!route?.router || !route.path?.length) {
283
+ throw new Error("Tron: swap quote is missing its SunSwap routing data \u2014 re-quote before swapping.");
284
+ }
285
+ const amountOut = BigInt(p.quote.to.amount);
286
+ const amountInMax = BigInt(route.amountInMax);
287
+ const deadline = Math.floor(Date.now() / 1e3) + DEADLINE_SECONDS;
288
+ const selector = route.fromIsNative ? "swapETHForExactTokens(uint256,address[],address,uint256)" : route.toIsNative ? "swapTokensForExactETH(uint256,uint256,address[],address,uint256)" : "swapTokensForExactTokens(uint256,uint256,address[],address,uint256)";
289
+ const params = route.fromIsNative ? [
290
+ { type: "uint256", value: amountOut.toString() },
291
+ { type: "address[]", value: route.path },
292
+ { type: "address", value: p.owner },
293
+ { type: "uint256", value: deadline }
294
+ ] : [
295
+ { type: "uint256", value: amountOut.toString() },
296
+ { type: "uint256", value: amountInMax.toString() },
297
+ { type: "address[]", value: route.path },
298
+ { type: "address", value: p.owner },
299
+ { type: "uint256", value: deadline }
300
+ ];
301
+ try {
302
+ if (route.needsApproval && route.tokenIn) {
303
+ const allowance = await readAllowance(p.client, route.tokenIn, p.owner, route.router);
304
+ if (allowance < amountInMax) {
305
+ if (allowance > 0n) {
306
+ await sendAndConfirm(
307
+ p.client,
308
+ route.tokenIn,
309
+ "approve(address,uint256)",
310
+ [
311
+ { type: "address", value: route.router },
312
+ { type: "uint256", value: "0" }
313
+ ],
314
+ p.owner,
315
+ p.privateKey,
316
+ APPROVE_FEE_LIMIT_SUN
317
+ );
318
+ }
319
+ await sendAndConfirm(
320
+ p.client,
321
+ route.tokenIn,
322
+ "approve(address,uint256)",
323
+ [
324
+ { type: "address", value: route.router },
325
+ { type: "uint256", value: amountInMax.toString() }
326
+ ],
327
+ p.owner,
328
+ p.privateKey,
329
+ APPROVE_FEE_LIMIT_SUN
330
+ );
331
+ }
332
+ }
333
+ const built = await p.client.transactionBuilder.triggerSmartContract(
334
+ route.router,
335
+ selector,
336
+ {
337
+ feeLimit: FEE_LIMIT_SUN,
338
+ /*
339
+ * A native input is sent as callValue, capped at the ceiling: the router refunds
340
+ * whatever the exact-output trade did not need.
341
+ *
342
+ * TronWeb's callValue is a JS number, so a value past 2^53 would be silently rounded
343
+ * and the swap would spend an amount nobody chose. That is ~9 billion TRX, far more
344
+ * than anyone will ever swap, but a payment library must never round money quietly.
345
+ */
346
+ ...route.fromIsNative ? { callValue: safeCallValue(amountInMax) } : {}
347
+ },
348
+ params,
349
+ p.owner
350
+ );
351
+ if (built?.result?.result !== true) {
352
+ throw new Error(built?.result?.message ?? "SunSwap router refused to build the swap");
353
+ }
354
+ const signed = await p.client.trx.sign(built.transaction, p.privateKey);
355
+ const broadcast = await p.client.trx.sendRawTransaction(signed);
356
+ if (broadcast?.result === false || broadcast?.code) {
357
+ throw new Error(`${broadcast.code ?? "broadcast failed"}: ${broadcast.message ?? ""}`);
358
+ }
359
+ const txid = broadcast?.txid ?? broadcast?.transaction?.txID ?? signed.txID;
360
+ return {
361
+ transaction: txid,
362
+ network: p.quote.network,
363
+ source: p.quote.source,
364
+ from: p.quote.from,
365
+ to: p.quote.to
366
+ };
367
+ } catch (err) {
368
+ const msg = String(err?.message ?? err);
369
+ if (/energy|bandwidth|balance|insufficient|OUT_OF_ENERGY/i.test(msg)) {
370
+ throw new InsufficientFundsError(
371
+ `Tron swap failed: the account cannot cover it. Note a SunSwap swap burns about ${TRON_SWAP_ENERGY.toLocaleString("en-US")} energy, which without staked energy is charged as roughly 23 TRX regardless of trade size. (${msg.slice(0, 140)})`,
372
+ { cause: err }
373
+ );
374
+ }
375
+ throw err;
376
+ }
377
+ }
378
+
124
379
  // src/drivers/tron/verify.ts
125
380
  var TRANSFER_TOPIC = "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
126
381
  async function verifyTron(params) {
@@ -464,6 +719,13 @@ function makeTronNetwork(preset, rpcUrl) {
464
719
  detail: "~30k energy + bandwidth (sender not staked; pass { from } for a precise estimate)"
465
720
  });
466
721
  },
722
+ /** The bound wallet's own address — where THIS wallet gets paid. Derived from the key
723
+ * material only: no RPC, nothing moved. See {@link ResolvedNetwork.addressOf}. */
724
+ async addressOf(wallet) {
725
+ const addr = tronWeb.address.fromPrivateKey(resolveTronPrivateKey(wallet._native));
726
+ if (!addr) throw new WrongFamilyError(`chain ${network}: the Tron wallet key is not a valid private key.`);
727
+ return addr;
728
+ },
467
729
  async balanceOf(wallet, asset) {
468
730
  const owner = tronWeb.address.fromPrivateKey(
469
731
  resolveTronPrivateKey(wallet._native)
@@ -489,6 +751,26 @@ function makeTronNetwork(preset, rpcUrl) {
489
751
  return { token, native };
490
752
  },
491
753
  // No receive prerequisite — any Tron account receives TRX/TRC-20 immediately.
754
+ /* ---- swap (OPTIONAL, opt-in): via SunSwap V2, no API, no key, no added fee. See ./swap.ts ---- */
755
+ async quoteSwap({ from, to, wantAmount, slippageBps, wallet }) {
756
+ const owner = tronWeb.address.fromPrivateKey(resolveTronPrivateKey(wallet._native));
757
+ if (!owner) return null;
758
+ return quoteTronSwap({
759
+ client: tronWeb,
760
+ network,
761
+ from,
762
+ to,
763
+ wantAmount,
764
+ slippageBps,
765
+ owner
766
+ });
767
+ },
768
+ async swap(wallet, quote) {
769
+ const privateKey = resolveTronPrivateKey(wallet._native);
770
+ const owner = tronWeb.address.fromPrivateKey(privateKey);
771
+ if (!owner) throw new Error("Tron: could not derive the payer address from the wallet key.");
772
+ return swapTron({ client: tronWeb, owner, privateKey, quote });
773
+ },
492
774
  async recipientReady() {
493
775
  return { ready: "n/a" };
494
776
  },