@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,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
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
2
 
3
3
  var _chunkCQREG5LEcjs = require('./chunk-CQREG5LE.cjs');
4
4
 
@@ -9,7 +9,9 @@ var _chunkCQREG5LEcjs = require('./chunk-CQREG5LE.cjs');
9
9
 
10
10
 
11
11
 
12
- var _chunkQONQSZHJcjs = require('./chunk-QONQSZHJ.cjs');
12
+
13
+
14
+ var _chunk6XTNI2OQcjs = require('./chunk-6XTNI2OQ.cjs');
13
15
 
14
16
  // src/drivers/ton/index.ts
15
17
  var _core = require('@ton/core');
@@ -76,7 +78,7 @@ async function waitForSeqno(opened, from, { tries = 30, intervalMs = 2e3 } = {})
76
78
  }
77
79
  if (current > from) return;
78
80
  }
79
- throw new (0, _chunkQONQSZHJcjs.ConfirmationTimeoutError)(
81
+ throw new (0, _chunk6XTNI2OQcjs.ConfirmationTimeoutError)(
80
82
  `TON wallet seqno did not advance past ${from} \u2014 the payment may not have been accepted.`
81
83
  );
82
84
  }
@@ -202,18 +204,193 @@ function notFound(nonce) {
202
204
  };
203
205
  }
204
206
 
207
+ // src/drivers/ton/swap.ts
208
+ var STONFI_API = "https://api.ston.fi";
209
+ var PTON_MASTER_V2_1 = "EQBnGWMCf3-FZZq1W4IWcWiGAc3PHuZ0_H-7sad2oY00o83S";
210
+ function sourceFor(feePercent) {
211
+ const pct = feePercent ? `${(Number(feePercent) * 100).toFixed(4)}%` : "the underlying pool fee";
212
+ return {
213
+ kind: "provider",
214
+ name: "STON.fi",
215
+ note: `Third-party TON DEX (ston.fi), used keyless. It prices the route and returns the router to message; your own wallet signs and sends, and STON.fi never holds your funds. Cost on this route: ${pct}, which is the pool fee. PipRail adds nothing.`
216
+ };
217
+ }
218
+ function masterFor(t) {
219
+ return t.asset === "native" ? PTON_MASTER_V2_1 : t.asset;
220
+ }
221
+ function side(t, amount) {
222
+ const decimals = t.asset === "native" ? TON_DECIMALS : t.decimals;
223
+ return {
224
+ asset: t.asset,
225
+ symbol: _nullishCoalesce(t.symbol, () => ( (t.asset === "native" ? TON_NATIVE_SYMBOL : t.asset))),
226
+ decimals,
227
+ amount: amount.toString(),
228
+ amountFormatted: _chunk6XTNI2OQcjs.formatUnits.call(void 0, amount, decimals)
229
+ };
230
+ }
231
+ async function postJson(url) {
232
+ try {
233
+ const res = await fetch(url, { method: "POST" });
234
+ if (!res.ok) return null;
235
+ return await res.json();
236
+ } catch (e6) {
237
+ return null;
238
+ }
239
+ }
240
+ async function quoteTonSwap(p) {
241
+ if (p.wantAmount <= 0n) return null;
242
+ const offerMaster = masterFor(p.from);
243
+ const askMaster = masterFor(p.to);
244
+ if (offerMaster === askMaster) return null;
245
+ const tolerance = (p.slippageBps / 1e4).toFixed(6);
246
+ const bps = BigInt(Math.max(0, Math.min(p.slippageBps, 9999)));
247
+ const askUnits = (p.wantAmount * 10000n + (10000n - bps) - 1n) / (10000n - bps);
248
+ const url = `${STONFI_API}/v1/reverse_swap/simulate?offer_address=${encodeURIComponent(offerMaster)}&ask_address=${encodeURIComponent(askMaster)}&units=${askUnits.toString()}&slippage_tolerance=${tolerance}`;
249
+ const sim = await postJson(url);
250
+ if (!_optionalChain([sim, 'optionalAccess', _ => _.offer_units]) || !sim.router_address) return null;
251
+ const num = (v) => {
252
+ if (typeof v !== "string" && typeof v !== "number") return null;
253
+ try {
254
+ const n = BigInt(v);
255
+ return n >= 0n ? n : null;
256
+ } catch (e7) {
257
+ return null;
258
+ }
259
+ };
260
+ const offerUnits = num(sim.offer_units);
261
+ if (offerUnits === null || offerUnits <= 0n) return null;
262
+ const minAsk = num(sim.min_ask_units);
263
+ if (minAsk === null || minAsk < p.wantAmount) return null;
264
+ const forwardGas = _nullishCoalesce(num(_optionalChain([sim, 'access', _2 => _2.gas_params, 'optionalAccess', _3 => _3.forward_gas])), () => ( 300000000n));
265
+ const major = _nullishCoalesce(_optionalChain([sim, 'access', _4 => _4.router, 'optionalAccess', _5 => _5.major_version]), () => ( 2));
266
+ const minor = _nullishCoalesce(_optionalChain([sim, 'access', _6 => _6.router, 'optionalAccess', _7 => _7.minor_version]), () => ( 1));
267
+ const routerVersion = major === 1 ? "v1" : `v${major}_${minor}`;
268
+ return {
269
+ source: sourceFor(sim.fee_percent),
270
+ network: p.network,
271
+ from: side(p.from, offerUnits),
272
+ to: side(p.to, p.wantAmount),
273
+ // The offer side is fixed by the simulation, so it IS the ceiling.
274
+ maxSpend: offerUnits.toString(),
275
+ maxSpendFormatted: _chunk6XTNI2OQcjs.formatUnits.call(void 0, offerUnits, p.from.asset === "native" ? TON_DECIMALS : p.from.decimals),
276
+ slippageBps: p.slippageBps,
277
+ route: {
278
+ routerAddress: sim.router_address,
279
+ routerVersion,
280
+ ptonMaster: _nullishCoalesce(_optionalChain([sim, 'access', _8 => _8.router, 'optionalAccess', _9 => _9.pton_master_address]), () => ( PTON_MASTER_V2_1)),
281
+ ptonVersion: _nullishCoalesce(_optionalChain([sim, 'access', _10 => _10.router, 'optionalAccess', _11 => _11.pton_version]), () => ( "2.1")),
282
+ offerMaster,
283
+ askMaster,
284
+ askJettonWallet: sim.ask_jetton_wallet,
285
+ // 🔴 The on-chain floor, checked above to be ≥ the invoice. The router refuses the swap
286
+ // below it, so "at least the invoice" is enforced by the contract, not by this quote.
287
+ minAskUnits: minAsk.toString(),
288
+ forwardGas: forwardGas.toString(),
289
+ fromIsNative: p.from.asset === "native",
290
+ toIsNative: p.to.asset === "native"
291
+ }
292
+ };
293
+ }
294
+ async function swapTon(p) {
295
+ const route = p.quote.route;
296
+ if (!_optionalChain([route, 'optionalAccess', _12 => _12.routerAddress]) || !route.offerMaster || !route.askMaster) {
297
+ throw new Error("TON: swap quote is missing its STON.fi routing data \u2014 re-quote before swapping.");
298
+ }
299
+ const [{ DEX, pTON }, { Address: Address3 }] = await Promise.all([
300
+ Promise.resolve().then(() => _interopRequireWildcard(require("@ston-fi/sdk"))),
301
+ Promise.resolve().then(() => _interopRequireWildcard(require("@ton/core")))
302
+ ]);
303
+ const dex = DEX[route.routerVersion];
304
+ if (!_optionalChain([dex, 'optionalAccess', _13 => _13.Router])) {
305
+ throw new Error(
306
+ `TON: STON.fi router version ${route.routerVersion} is not supported by the installed @ston-fi/sdk.`
307
+ );
308
+ }
309
+ const ptonNs = pTON[route.ptonVersion.startsWith("2") ? "v2_1" : "v1"];
310
+ if (!_optionalChain([ptonNs, 'optionalAccess', _14 => _14.create])) {
311
+ throw new Error(`TON: STON.fi pTON version ${route.ptonVersion} is not supported by the installed SDK.`);
312
+ }
313
+ const router = p.client.open(dex.Router.create(Address3.parse(route.routerAddress)));
314
+ const call = (name, args) => {
315
+ const fn = router[name];
316
+ if (typeof fn !== "function") {
317
+ throw new Error(`TON: the installed @ston-fi/sdk router has no ${name} \u2014 is it a supported version?`);
318
+ }
319
+ return fn.call(router, args);
320
+ };
321
+ const proxyTon = ptonNs.create(Address3.parse(route.ptonMaster));
322
+ const userWalletAddress = p.wallet.contract.address;
323
+ const offerAmount = BigInt(p.quote.from.amount);
324
+ const minAskAmount = BigInt(route.minAskUnits);
325
+ let txParams;
326
+ try {
327
+ if (route.fromIsNative) {
328
+ txParams = await call("getSwapTonToJettonTxParams", {
329
+ userWalletAddress,
330
+ proxyTon,
331
+ offerAmount,
332
+ askJettonAddress: Address3.parse(route.askMaster),
333
+ minAskAmount
334
+ });
335
+ } else if (route.toIsNative) {
336
+ txParams = await call("getSwapJettonToTonTxParams", {
337
+ userWalletAddress,
338
+ proxyTon,
339
+ offerJettonAddress: Address3.parse(route.offerMaster),
340
+ offerAmount,
341
+ minAskAmount
342
+ });
343
+ } else {
344
+ txParams = await call("getSwapJettonToJettonTxParams", {
345
+ userWalletAddress,
346
+ offerJettonAddress: Address3.parse(route.offerMaster),
347
+ askJettonAddress: Address3.parse(route.askMaster),
348
+ offerAmount,
349
+ minAskAmount
350
+ });
351
+ }
352
+ } catch (err) {
353
+ throw new Error(`TON: STON.fi could not build this swap message. Nothing was sent. (${String(err).slice(0, 160)})`, {
354
+ cause: err
355
+ });
356
+ }
357
+ try {
358
+ const hash = await p.send({
359
+ to: String(txParams.to),
360
+ value: BigInt(txParams.value),
361
+ body: txParams.body
362
+ });
363
+ return {
364
+ transaction: hash,
365
+ network: p.quote.network,
366
+ source: p.quote.source,
367
+ from: p.quote.from,
368
+ to: p.quote.to
369
+ };
370
+ } catch (err) {
371
+ const msg = String(_nullishCoalesce(_optionalChain([err, 'optionalAccess', _15 => _15.message]), () => ( err)));
372
+ if (/not enough|insufficient|balance|inbound message/i.test(msg)) {
373
+ throw new (0, _chunk6XTNI2OQcjs.InsufficientFundsError)(
374
+ `TON swap could not be sent: the wallet cannot cover the offer plus the attached forward gas (${_chunk6XTNI2OQcjs.formatUnits.call(void 0, BigInt(route.forwardGas), TON_DECIMALS)} TON is attached, most of which bounces back). (${msg.slice(0, 140)})`,
375
+ { cause: err }
376
+ );
377
+ }
378
+ throw err;
379
+ }
380
+ }
381
+
205
382
  // src/drivers/ton/wallet.ts
206
383
  var _crypto = require('@ton/crypto');
207
384
 
208
385
  function assertTonWallet(wallet, network) {
209
386
  if (typeof wallet !== "object" || wallet === null) {
210
- throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
387
+ throw new (0, _chunk6XTNI2OQcjs.WrongFamilyError)(
211
388
  `chain ${network} is TON; wallet must be { key } (24-word mnemonic) or { keyPair }.`
212
389
  );
213
390
  }
214
- _chunkQONQSZHJcjs.assertNoLegacyWalletKey.call(void 0, wallet, "TON");
391
+ _chunk6XTNI2OQcjs.assertNoLegacyWalletKey.call(void 0, wallet, "TON");
215
392
  if (!("key" in wallet) && !("keyPair" in wallet)) {
216
- throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
393
+ throw new (0, _chunk6XTNI2OQcjs.WrongFamilyError)(
217
394
  `chain ${network} is TON; wallet must be { key } (24-word mnemonic) or { keyPair }.`
218
395
  );
219
396
  }
@@ -224,13 +401,13 @@ async function resolveTonWallet(config) {
224
401
  let keyPair;
225
402
  if (config.key) {
226
403
  if (typeof config.key !== "string" && !Array.isArray(config.key)) {
227
- throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
404
+ throw new (0, _chunk6XTNI2OQcjs.WrongFamilyError)(
228
405
  "chain is TON; the wallet { key } must be a 24-word mnemonic (a string or string[])."
229
406
  );
230
407
  }
231
408
  const words = Array.isArray(config.key) ? config.key : config.key.trim().split(/\s+/);
232
409
  if (!await _crypto.mnemonicValidate.call(void 0, words)) {
233
- throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
410
+ throw new (0, _chunk6XTNI2OQcjs.WrongFamilyError)(
234
411
  "chain is TON; the wallet { key } is not a valid TON mnemonic (expected 24 words from the TON/BIP-39 wordlist)."
235
412
  );
236
413
  }
@@ -238,7 +415,7 @@ async function resolveTonWallet(config) {
238
415
  } else if (config.keyPair) {
239
416
  keyPair = config.keyPair;
240
417
  } else {
241
- throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)("TON wallet needs { key } (24-word mnemonic) or { keyPair }.");
418
+ throw new (0, _chunk6XTNI2OQcjs.WrongFamilyError)("TON wallet needs { key } (24-word mnemonic) or { keyPair }.");
242
419
  }
243
420
  const contract = version === "v5r1" ? _ton.WalletContractV5R1.create({ workchain: 0, publicKey: keyPair.publicKey }) : _ton.WalletContractV4.create({ workchain: 0, publicKey: keyPair.publicKey });
244
421
  return { keyPair, contract };
@@ -287,15 +464,15 @@ function makeTonNetwork(preset, rpcUrl) {
287
464
  const info = preset.tokens[token.toUpperCase()];
288
465
  if (!info) {
289
466
  const known = Object.keys(preset.tokens).join(", ") || "(none built in)";
290
- throw new (0, _chunkQONQSZHJcjs.UnknownTokenError)(
467
+ throw new (0, _chunk6XTNI2OQcjs.UnknownTokenError)(
291
468
  `token "${token}" isn't built in for TON (known: ${known}). Note: native USDC doesn't exist on TON. Pass { master, decimals } for a custom jetton, or use 'native'.`
292
469
  );
293
470
  }
294
471
  return { asset: info.master, decimals: info.decimals, symbol: info.symbol };
295
472
  }
296
- _chunkQONQSZHJcjs.rejectForeignToken.call(void 0, token, "ton", network);
473
+ _chunk6XTNI2OQcjs.rejectForeignToken.call(void 0, token, "ton", network);
297
474
  if (!("master" in token)) {
298
- throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
475
+ throw new (0, _chunk6XTNI2OQcjs.WrongFamilyError)(
299
476
  `chain ${network} is TON; a custom token must be { master, decimals }.`
300
477
  );
301
478
  }
@@ -314,14 +491,14 @@ function makeTonNetwork(preset, rpcUrl) {
314
491
  },
315
492
  assertValidPayTo(payTo) {
316
493
  if (payTo.startsWith("0x")) {
317
- throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
494
+ throw new (0, _chunk6XTNI2OQcjs.WrongFamilyError)(
318
495
  `chain ${network} is TON, but payTo "${payTo}" looks like an EVM address.`
319
496
  );
320
497
  }
321
498
  try {
322
499
  _core.Address.parse(payTo);
323
- } catch (e6) {
324
- throw new (0, _chunkQONQSZHJcjs.WrongFamilyError)(
500
+ } catch (e8) {
501
+ throw new (0, _chunk6XTNI2OQcjs.WrongFamilyError)(
325
502
  `chain ${network} is TON, but payTo "${payTo}" is not a valid TON address.`
326
503
  );
327
504
  }
@@ -337,7 +514,7 @@ function makeTonNetwork(preset, rpcUrl) {
337
514
  const watch = await watchAccountFor(accept);
338
515
  return encodeRef(watch, accept.extra.nonce);
339
516
  } catch (err) {
340
- throw _nullishCoalesce(_chunkQONQSZHJcjs.toInsufficientFundsError.call(void 0, err), () => ( err));
517
+ throw _nullishCoalesce(_chunk6XTNI2OQcjs.toInsufficientFundsError.call(void 0, err), () => ( err));
341
518
  }
342
519
  },
343
520
  async confirm(ref) {
@@ -348,7 +525,7 @@ function makeTonNetwork(preset, rpcUrl) {
348
525
  let txs;
349
526
  try {
350
527
  txs = await client.getTransactions(account, { limit: 16, archival: true });
351
- } catch (e7) {
528
+ } catch (e9) {
352
529
  continue;
353
530
  }
354
531
  for (const tx of txs) {
@@ -356,18 +533,38 @@ function makeTonNetwork(preset, rpcUrl) {
356
533
  if (inc && inc.comment === nonce) return { height: tx.lt.toString() };
357
534
  }
358
535
  }
359
- throw new (0, _chunkQONQSZHJcjs.ConfirmationTimeoutError)(`TON payment for nonce ${nonce} did not settle in time.`);
536
+ throw new (0, _chunk6XTNI2OQcjs.ConfirmationTimeoutError)(`TON payment for nonce ${nonce} did not settle in time.`);
360
537
  },
361
538
  async estimateCost(accept) {
362
539
  const fee = accept.asset === "native" ? 10000000n : 50000000n;
363
540
  const detail = accept.asset === "native" ? `~0.01 ${TON_NATIVE_SYMBOL} network fee` : `~0.05 ${TON_NATIVE_SYMBOL} attached for the jetton transfer (leftover refunded)`;
364
- return _chunkQONQSZHJcjs.nativeCost.call(void 0, { symbol: TON_NATIVE_SYMBOL, decimals: TON_DECIMALS, fee, basis: "heuristic", detail });
541
+ return _chunk6XTNI2OQcjs.nativeCost.call(void 0, { symbol: TON_NATIVE_SYMBOL, decimals: TON_DECIMALS, fee, basis: "heuristic", detail });
542
+ },
543
+ /**
544
+ * The bound wallet's own address — where THIS wallet gets paid. Derived from the key
545
+ * material only: no RPC, nothing moved. See {@link ResolvedNetwork.addressOf}.
546
+ *
547
+ * 🔴 NON-BOUNCEABLE (`UQ…`), which is not the default `toString()` gives.
548
+ *
549
+ * TON encodes one account two ways. A bounceable `EQ…` address returns the funds to the
550
+ * sender if the destination contract is not initialised, and a wallet contract is not
551
+ * initialised until it has sent its first transaction. So a freshly generated agent wallet
552
+ * advertising `EQ…` would have its FIRST payment bounce, which is exactly the payment a new
553
+ * seller most needs to land. `UQ…` is also what every TON wallet shows you as your receive
554
+ * address, so this matches what the operator sees and what the wallet file records.
555
+ *
556
+ * Verification is unaffected either way: `Address.parse` accepts both forms and they are the
557
+ * same account.
558
+ */
559
+ async addressOf(wallet) {
560
+ const { contract } = await resolveTonWallet(wallet._native);
561
+ return contract.address.toString({ bounceable: false });
365
562
  },
366
563
  async balanceOf(wallet, asset) {
367
564
  let owner;
368
565
  try {
369
566
  owner = (await resolveTonWallet(wallet._native)).contract.address.toString();
370
- } catch (e8) {
567
+ } catch (e10) {
371
568
  return { token: null, native: null };
372
569
  }
373
570
  const native = await client.getBalance(_core.Address.parse(owner)).then((b) => BigInt(b)).catch(() => null);
@@ -377,7 +574,7 @@ function makeTonNetwork(preset, rpcUrl) {
377
574
  const jw = await jettonWalletFor(asset, owner);
378
575
  const { stack } = await client.runMethod(jw, "get_wallet_data");
379
576
  token = stack.readBigNumber();
380
- } catch (e9) {
577
+ } catch (e11) {
381
578
  token = null;
382
579
  }
383
580
  return { token, native };
@@ -386,11 +583,56 @@ function makeTonNetwork(preset, rpcUrl) {
386
583
  async recipientReady() {
387
584
  return { ready: "n/a" };
388
585
  },
586
+ /* ---- swap (OPTIONAL, opt-in): via STON.fi, keyless + no added fee. See ./swap.ts ---- */
587
+ async quoteSwap({ from, to, wantAmount, slippageBps }) {
588
+ return quoteTonSwap({ network, from, to, wantAmount, slippageBps });
589
+ },
590
+ async swap(wallet, quote) {
591
+ const tw = await resolveTonWallet(wallet._native);
592
+ return swapTon({
593
+ client,
594
+ wallet: tw,
595
+ quote,
596
+ /*
597
+ * TON settles asynchronously, so "sent" and "arrived" are different questions. This
598
+ * waits for the WALLET's seqno to advance (its external message was accepted and
599
+ * sent), then reports the resulting wallet transaction hash so the caller has a real,
600
+ * checkable reference. Whether the ask side landed is a balance question, exactly as
601
+ * it is on the payment path.
602
+ */
603
+ send: async ({ to, value, body }) => {
604
+ const opened = client.open(tw.contract);
605
+ const seqno = await opened.getSeqno();
606
+ await opened.sendTransfer({
607
+ seqno,
608
+ secretKey: tw.keyPair.secretKey,
609
+ sendMode: _core.SendMode.PAY_GAS_SEPARATELY | _core.SendMode.IGNORE_ERRORS,
610
+ messages: [_core.internal.call(void 0, { to: _core.Address.parse(to), value, bounce: true, body })]
611
+ });
612
+ for (let i = 0; i < 30; i += 1) {
613
+ await new Promise((r) => setTimeout(r, 2e3));
614
+ try {
615
+ if (await opened.getSeqno() > seqno) break;
616
+ } catch (e12) {
617
+ continue;
618
+ }
619
+ }
620
+ try {
621
+ const txs = await client.getTransactions(tw.contract.address, { limit: 10 });
622
+ const mine = _optionalChain([txs, 'optionalAccess', _16 => _16.find, 'call', _17 => _17((t) => _optionalChain([t, 'optionalAccess', _18 => _18.inMessage, 'optionalAccess', _19 => _19.info, 'optionalAccess', _20 => _20.type]) === "external-in")]);
623
+ const h = _optionalChain([mine, 'optionalAccess', _21 => _21.hash, 'optionalCall', _22 => _22()]);
624
+ if (h) return Buffer.from(h).toString("hex");
625
+ } catch (e13) {
626
+ }
627
+ return `ton:${tw.contract.address.toString()}|seqno:${seqno}`;
628
+ }
629
+ });
630
+ },
389
631
  async verify(_ref, accept) {
390
632
  let watch;
391
633
  try {
392
634
  watch = await watchAccountFor(accept);
393
- } catch (e10) {
635
+ } catch (e14) {
394
636
  return { ok: false, error: "tx_not_found", detail: `Could not derive/read the TON jetton wallet (transient RPC failure) \u2014 retry.` };
395
637
  }
396
638
  return verifyTon({ client, watch, accept });
@@ -3,16 +3,18 @@ import {
3
3
  } from "./chunk-FURB5RP7.js";
4
4
  import {
5
5
  ConfirmationTimeoutError,
6
+ InsufficientFundsError,
6
7
  UnknownTokenError,
7
8
  WrongFamilyError,
8
9
  assertNoLegacyWalletKey,
10
+ formatUnits,
9
11
  nativeCost,
10
12
  rejectForeignToken,
11
13
  toInsufficientFundsError
12
- } from "./chunk-QU25LSVS.js";
14
+ } from "./chunk-5GRBEMCA.js";
13
15
 
14
16
  // src/drivers/ton/index.ts
15
- import { Address as Address2 } from "@ton/core";
17
+ import { Address as Address2, internal as internal2, SendMode as SendMode2 } from "@ton/core";
16
18
  import { JettonMaster, TonClient } from "@ton/ton";
17
19
 
18
20
  // src/drivers/ton/chains.ts
@@ -202,6 +204,181 @@ function notFound(nonce) {
202
204
  };
203
205
  }
204
206
 
207
+ // src/drivers/ton/swap.ts
208
+ var STONFI_API = "https://api.ston.fi";
209
+ var PTON_MASTER_V2_1 = "EQBnGWMCf3-FZZq1W4IWcWiGAc3PHuZ0_H-7sad2oY00o83S";
210
+ function sourceFor(feePercent) {
211
+ const pct = feePercent ? `${(Number(feePercent) * 100).toFixed(4)}%` : "the underlying pool fee";
212
+ return {
213
+ kind: "provider",
214
+ name: "STON.fi",
215
+ note: `Third-party TON DEX (ston.fi), used keyless. It prices the route and returns the router to message; your own wallet signs and sends, and STON.fi never holds your funds. Cost on this route: ${pct}, which is the pool fee. PipRail adds nothing.`
216
+ };
217
+ }
218
+ function masterFor(t) {
219
+ return t.asset === "native" ? PTON_MASTER_V2_1 : t.asset;
220
+ }
221
+ function side(t, amount) {
222
+ const decimals = t.asset === "native" ? TON_DECIMALS : t.decimals;
223
+ return {
224
+ asset: t.asset,
225
+ symbol: t.symbol ?? (t.asset === "native" ? TON_NATIVE_SYMBOL : t.asset),
226
+ decimals,
227
+ amount: amount.toString(),
228
+ amountFormatted: formatUnits(amount, decimals)
229
+ };
230
+ }
231
+ async function postJson(url) {
232
+ try {
233
+ const res = await fetch(url, { method: "POST" });
234
+ if (!res.ok) return null;
235
+ return await res.json();
236
+ } catch {
237
+ return null;
238
+ }
239
+ }
240
+ async function quoteTonSwap(p) {
241
+ if (p.wantAmount <= 0n) return null;
242
+ const offerMaster = masterFor(p.from);
243
+ const askMaster = masterFor(p.to);
244
+ if (offerMaster === askMaster) return null;
245
+ const tolerance = (p.slippageBps / 1e4).toFixed(6);
246
+ const bps = BigInt(Math.max(0, Math.min(p.slippageBps, 9999)));
247
+ const askUnits = (p.wantAmount * 10000n + (10000n - bps) - 1n) / (10000n - bps);
248
+ const url = `${STONFI_API}/v1/reverse_swap/simulate?offer_address=${encodeURIComponent(offerMaster)}&ask_address=${encodeURIComponent(askMaster)}&units=${askUnits.toString()}&slippage_tolerance=${tolerance}`;
249
+ const sim = await postJson(url);
250
+ if (!sim?.offer_units || !sim.router_address) return null;
251
+ const num = (v) => {
252
+ if (typeof v !== "string" && typeof v !== "number") return null;
253
+ try {
254
+ const n = BigInt(v);
255
+ return n >= 0n ? n : null;
256
+ } catch {
257
+ return null;
258
+ }
259
+ };
260
+ const offerUnits = num(sim.offer_units);
261
+ if (offerUnits === null || offerUnits <= 0n) return null;
262
+ const minAsk = num(sim.min_ask_units);
263
+ if (minAsk === null || minAsk < p.wantAmount) return null;
264
+ const forwardGas = num(sim.gas_params?.forward_gas) ?? 300000000n;
265
+ const major = sim.router?.major_version ?? 2;
266
+ const minor = sim.router?.minor_version ?? 1;
267
+ const routerVersion = major === 1 ? "v1" : `v${major}_${minor}`;
268
+ return {
269
+ source: sourceFor(sim.fee_percent),
270
+ network: p.network,
271
+ from: side(p.from, offerUnits),
272
+ to: side(p.to, p.wantAmount),
273
+ // The offer side is fixed by the simulation, so it IS the ceiling.
274
+ maxSpend: offerUnits.toString(),
275
+ maxSpendFormatted: formatUnits(offerUnits, p.from.asset === "native" ? TON_DECIMALS : p.from.decimals),
276
+ slippageBps: p.slippageBps,
277
+ route: {
278
+ routerAddress: sim.router_address,
279
+ routerVersion,
280
+ ptonMaster: sim.router?.pton_master_address ?? PTON_MASTER_V2_1,
281
+ ptonVersion: sim.router?.pton_version ?? "2.1",
282
+ offerMaster,
283
+ askMaster,
284
+ askJettonWallet: sim.ask_jetton_wallet,
285
+ // 🔴 The on-chain floor, checked above to be ≥ the invoice. The router refuses the swap
286
+ // below it, so "at least the invoice" is enforced by the contract, not by this quote.
287
+ minAskUnits: minAsk.toString(),
288
+ forwardGas: forwardGas.toString(),
289
+ fromIsNative: p.from.asset === "native",
290
+ toIsNative: p.to.asset === "native"
291
+ }
292
+ };
293
+ }
294
+ async function swapTon(p) {
295
+ const route = p.quote.route;
296
+ if (!route?.routerAddress || !route.offerMaster || !route.askMaster) {
297
+ throw new Error("TON: swap quote is missing its STON.fi routing data \u2014 re-quote before swapping.");
298
+ }
299
+ const [{ DEX, pTON }, { Address: Address3 }] = await Promise.all([
300
+ import("@ston-fi/sdk"),
301
+ import("@ton/core")
302
+ ]);
303
+ const dex = DEX[route.routerVersion];
304
+ if (!dex?.Router) {
305
+ throw new Error(
306
+ `TON: STON.fi router version ${route.routerVersion} is not supported by the installed @ston-fi/sdk.`
307
+ );
308
+ }
309
+ const ptonNs = pTON[route.ptonVersion.startsWith("2") ? "v2_1" : "v1"];
310
+ if (!ptonNs?.create) {
311
+ throw new Error(`TON: STON.fi pTON version ${route.ptonVersion} is not supported by the installed SDK.`);
312
+ }
313
+ const router = p.client.open(dex.Router.create(Address3.parse(route.routerAddress)));
314
+ const call = (name, args) => {
315
+ const fn = router[name];
316
+ if (typeof fn !== "function") {
317
+ throw new Error(`TON: the installed @ston-fi/sdk router has no ${name} \u2014 is it a supported version?`);
318
+ }
319
+ return fn.call(router, args);
320
+ };
321
+ const proxyTon = ptonNs.create(Address3.parse(route.ptonMaster));
322
+ const userWalletAddress = p.wallet.contract.address;
323
+ const offerAmount = BigInt(p.quote.from.amount);
324
+ const minAskAmount = BigInt(route.minAskUnits);
325
+ let txParams;
326
+ try {
327
+ if (route.fromIsNative) {
328
+ txParams = await call("getSwapTonToJettonTxParams", {
329
+ userWalletAddress,
330
+ proxyTon,
331
+ offerAmount,
332
+ askJettonAddress: Address3.parse(route.askMaster),
333
+ minAskAmount
334
+ });
335
+ } else if (route.toIsNative) {
336
+ txParams = await call("getSwapJettonToTonTxParams", {
337
+ userWalletAddress,
338
+ proxyTon,
339
+ offerJettonAddress: Address3.parse(route.offerMaster),
340
+ offerAmount,
341
+ minAskAmount
342
+ });
343
+ } else {
344
+ txParams = await call("getSwapJettonToJettonTxParams", {
345
+ userWalletAddress,
346
+ offerJettonAddress: Address3.parse(route.offerMaster),
347
+ askJettonAddress: Address3.parse(route.askMaster),
348
+ offerAmount,
349
+ minAskAmount
350
+ });
351
+ }
352
+ } catch (err) {
353
+ throw new Error(`TON: STON.fi could not build this swap message. Nothing was sent. (${String(err).slice(0, 160)})`, {
354
+ cause: err
355
+ });
356
+ }
357
+ try {
358
+ const hash = await p.send({
359
+ to: String(txParams.to),
360
+ value: BigInt(txParams.value),
361
+ body: txParams.body
362
+ });
363
+ return {
364
+ transaction: hash,
365
+ network: p.quote.network,
366
+ source: p.quote.source,
367
+ from: p.quote.from,
368
+ to: p.quote.to
369
+ };
370
+ } catch (err) {
371
+ const msg = String(err?.message ?? err);
372
+ if (/not enough|insufficient|balance|inbound message/i.test(msg)) {
373
+ throw new InsufficientFundsError(
374
+ `TON swap could not be sent: the wallet cannot cover the offer plus the attached forward gas (${formatUnits(BigInt(route.forwardGas), TON_DECIMALS)} TON is attached, most of which bounces back). (${msg.slice(0, 140)})`,
375
+ { cause: err }
376
+ );
377
+ }
378
+ throw err;
379
+ }
380
+ }
381
+
205
382
  // src/drivers/ton/wallet.ts
206
383
  import { mnemonicToWalletKey, mnemonicValidate } from "@ton/crypto";
207
384
  import { WalletContractV4, WalletContractV5R1 } from "@ton/ton";
@@ -363,6 +540,26 @@ function makeTonNetwork(preset, rpcUrl) {
363
540
  const detail = accept.asset === "native" ? `~0.01 ${TON_NATIVE_SYMBOL} network fee` : `~0.05 ${TON_NATIVE_SYMBOL} attached for the jetton transfer (leftover refunded)`;
364
541
  return nativeCost({ symbol: TON_NATIVE_SYMBOL, decimals: TON_DECIMALS, fee, basis: "heuristic", detail });
365
542
  },
543
+ /**
544
+ * The bound wallet's own address — where THIS wallet gets paid. Derived from the key
545
+ * material only: no RPC, nothing moved. See {@link ResolvedNetwork.addressOf}.
546
+ *
547
+ * 🔴 NON-BOUNCEABLE (`UQ…`), which is not the default `toString()` gives.
548
+ *
549
+ * TON encodes one account two ways. A bounceable `EQ…` address returns the funds to the
550
+ * sender if the destination contract is not initialised, and a wallet contract is not
551
+ * initialised until it has sent its first transaction. So a freshly generated agent wallet
552
+ * advertising `EQ…` would have its FIRST payment bounce, which is exactly the payment a new
553
+ * seller most needs to land. `UQ…` is also what every TON wallet shows you as your receive
554
+ * address, so this matches what the operator sees and what the wallet file records.
555
+ *
556
+ * Verification is unaffected either way: `Address.parse` accepts both forms and they are the
557
+ * same account.
558
+ */
559
+ async addressOf(wallet) {
560
+ const { contract } = await resolveTonWallet(wallet._native);
561
+ return contract.address.toString({ bounceable: false });
562
+ },
366
563
  async balanceOf(wallet, asset) {
367
564
  let owner;
368
565
  try {
@@ -386,6 +583,51 @@ function makeTonNetwork(preset, rpcUrl) {
386
583
  async recipientReady() {
387
584
  return { ready: "n/a" };
388
585
  },
586
+ /* ---- swap (OPTIONAL, opt-in): via STON.fi, keyless + no added fee. See ./swap.ts ---- */
587
+ async quoteSwap({ from, to, wantAmount, slippageBps }) {
588
+ return quoteTonSwap({ network, from, to, wantAmount, slippageBps });
589
+ },
590
+ async swap(wallet, quote) {
591
+ const tw = await resolveTonWallet(wallet._native);
592
+ return swapTon({
593
+ client,
594
+ wallet: tw,
595
+ quote,
596
+ /*
597
+ * TON settles asynchronously, so "sent" and "arrived" are different questions. This
598
+ * waits for the WALLET's seqno to advance (its external message was accepted and
599
+ * sent), then reports the resulting wallet transaction hash so the caller has a real,
600
+ * checkable reference. Whether the ask side landed is a balance question, exactly as
601
+ * it is on the payment path.
602
+ */
603
+ send: async ({ to, value, body }) => {
604
+ const opened = client.open(tw.contract);
605
+ const seqno = await opened.getSeqno();
606
+ await opened.sendTransfer({
607
+ seqno,
608
+ secretKey: tw.keyPair.secretKey,
609
+ sendMode: SendMode2.PAY_GAS_SEPARATELY | SendMode2.IGNORE_ERRORS,
610
+ messages: [internal2({ to: Address2.parse(to), value, bounce: true, body })]
611
+ });
612
+ for (let i = 0; i < 30; i += 1) {
613
+ await new Promise((r) => setTimeout(r, 2e3));
614
+ try {
615
+ if (await opened.getSeqno() > seqno) break;
616
+ } catch {
617
+ continue;
618
+ }
619
+ }
620
+ try {
621
+ const txs = await client.getTransactions(tw.contract.address, { limit: 10 });
622
+ const mine = txs?.find((t) => t?.inMessage?.info?.type === "external-in");
623
+ const h = mine?.hash?.();
624
+ if (h) return Buffer.from(h).toString("hex");
625
+ } catch {
626
+ }
627
+ return `ton:${tw.contract.address.toString()}|seqno:${seqno}`;
628
+ }
629
+ });
630
+ },
389
631
  async verify(_ref, accept) {
390
632
  let watch;
391
633
  try {