@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.
- package/CHANGELOG.md +343 -0
- package/README.md +1 -0
- package/dist/{algorand-25FMBCT3.js → algorand-HL57PQHE.js} +157 -2
- package/dist/{algorand-Q3TUQLPK.cjs → algorand-W776EEM2.cjs} +205 -50
- 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 +2929 -1444
- package/dist/index.d.cts +625 -13
- package/dist/index.d.ts +625 -13
- package/dist/index.js +2506 -1021
- package/dist/{near-OLKCMTBI.js → near-5X2CQGML.js} +197 -1
- package/dist/{near-5LTTDU6G.cjs → near-XSQXEIHT.cjs} +242 -46
- package/dist/{solana-AI2G7V33.cjs → solana-EBV6PUCU.cjs} +211 -52
- package/dist/{solana-HTKDRTD3.js → solana-O6Q6QILH.js} +167 -8
- package/dist/{stellar-E2KWEV2E.cjs → stellar-5C7FQLPS.cjs} +191 -29
- package/dist/{stellar-EUFZLX6J.js → stellar-YF5LOJEM.js} +163 -1
- 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-GOVHMYYK.js} +222 -5
- package/dist/{xrpl-SVVS445B.cjs → xrpl-YS3IXPLV.cjs} +273 -56
- package/package.json +12 -2
- 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-
|
|
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
|
},
|