@t2000/sdk 1.4.0 → 1.6.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/dist/index.js CHANGED
@@ -143,6 +143,32 @@ var init_errors = __esm({
143
143
  });
144
144
 
145
145
  // src/token-registry.ts
146
+ var token_registry_exports = {};
147
+ __export(token_registry_exports, {
148
+ COIN_REGISTRY: () => COIN_REGISTRY,
149
+ ETH_TYPE: () => ETH_TYPE,
150
+ IKA_TYPE: () => IKA_TYPE,
151
+ LOFI_TYPE: () => LOFI_TYPE,
152
+ MANIFEST_TYPE: () => MANIFEST_TYPE,
153
+ NAVX_TYPE: () => NAVX_TYPE,
154
+ SUI_TYPE: () => SUI_TYPE,
155
+ TOKEN_MAP: () => TOKEN_MAP,
156
+ USDC_TYPE: () => USDC_TYPE,
157
+ USDE_TYPE: () => USDE_TYPE,
158
+ USDSUI_TYPE: () => USDSUI_TYPE,
159
+ USDT_TYPE: () => USDT_TYPE,
160
+ WAL_TYPE: () => WAL_TYPE,
161
+ WBTC_TYPE: () => WBTC_TYPE,
162
+ getCoinMeta: () => getCoinMeta,
163
+ getDecimalsForCoinType: () => getDecimalsForCoinType,
164
+ getTier: () => getTier,
165
+ isInRegistry: () => isInRegistry,
166
+ isSupported: () => isSupported,
167
+ isTier1: () => isTier1,
168
+ isTier2: () => isTier2,
169
+ resolveSymbol: () => resolveSymbol,
170
+ resolveTokenType: () => resolveTokenType
171
+ });
146
172
  function getCoinMeta(coinType) {
147
173
  return BY_TYPE.get(coinType);
148
174
  }
@@ -626,6 +652,8 @@ __export(volo_exports, {
626
652
  VOLO_PKG: () => VOLO_PKG,
627
653
  VOLO_POOL: () => VOLO_POOL,
628
654
  VSUI_TYPE: () => VSUI_TYPE,
655
+ addStakeVSuiToTx: () => addStakeVSuiToTx,
656
+ addUnstakeVSuiToTx: () => addUnstakeVSuiToTx,
629
657
  buildStakeVSuiTx: () => buildStakeVSuiTx,
630
658
  buildUnstakeVSuiTx: () => buildUnstakeVSuiTx,
631
659
  getVoloStats: () => getVoloStats
@@ -665,7 +693,7 @@ async function buildStakeVSuiTx(_client, address, amountMist) {
665
693
  return tx;
666
694
  }
667
695
  async function buildUnstakeVSuiTx(client, address, amountMist) {
668
- const coins = await fetchVSuiCoins(client, address);
696
+ const coins = await fetchCoinsByType(client, address, VSUI_TYPE);
669
697
  if (coins.length === 0) {
670
698
  throw new Error("No vSUI found in wallet.");
671
699
  }
@@ -693,14 +721,81 @@ async function buildUnstakeVSuiTx(client, address, amountMist) {
693
721
  tx.transferObjects([suiCoin], address);
694
722
  return tx;
695
723
  }
696
- async function fetchVSuiCoins(client, address) {
724
+ async function addStakeVSuiToTx(tx, client, address, input) {
725
+ if (input.amountMist < MIN_STAKE_MIST) {
726
+ throw new Error(`Minimum stake is 1 SUI (${MIN_STAKE_MIST} MIST). Got: ${input.amountMist}`);
727
+ }
728
+ let suiCoin;
729
+ if (input.inputCoin) {
730
+ suiCoin = input.inputCoin;
731
+ } else {
732
+ const coins = await fetchCoinsByType(client, address, SUI_TYPE);
733
+ if (coins.length === 0) {
734
+ throw new Error("No SUI coins found in wallet");
735
+ }
736
+ const totalBalance = coins.reduce((sum, c) => sum + BigInt(c.balance), 0n);
737
+ if (totalBalance < input.amountMist) {
738
+ throw new Error(`Insufficient SUI: need ${input.amountMist} MIST, have ${totalBalance}`);
739
+ }
740
+ const primary = tx.object(coins[0].coinObjectId);
741
+ if (coins.length > 1) {
742
+ tx.mergeCoins(primary, coins.slice(1).map((c) => tx.object(c.coinObjectId)));
743
+ }
744
+ [suiCoin] = tx.splitCoins(primary, [input.amountMist]);
745
+ }
746
+ const [vSuiCoin] = tx.moveCall({
747
+ target: `${VOLO_PKG}::stake_pool::stake`,
748
+ arguments: [
749
+ tx.object(VOLO_POOL),
750
+ tx.object(VOLO_METADATA),
751
+ tx.object(SUI_SYSTEM_STATE),
752
+ suiCoin
753
+ ]
754
+ });
755
+ return { coin: vSuiCoin, effectiveAmountMist: input.amountMist };
756
+ }
757
+ async function addUnstakeVSuiToTx(tx, client, address, input) {
758
+ let vSuiCoin;
759
+ if (input.inputCoin) {
760
+ if (input.amountMist === "all") {
761
+ vSuiCoin = input.inputCoin;
762
+ } else {
763
+ [vSuiCoin] = tx.splitCoins(input.inputCoin, [input.amountMist]);
764
+ }
765
+ } else {
766
+ const coins = await fetchCoinsByType(client, address, VSUI_TYPE);
767
+ if (coins.length === 0) {
768
+ throw new Error("No vSUI found in wallet.");
769
+ }
770
+ const primary = tx.object(coins[0].coinObjectId);
771
+ if (coins.length > 1) {
772
+ tx.mergeCoins(primary, coins.slice(1).map((c) => tx.object(c.coinObjectId)));
773
+ }
774
+ if (input.amountMist === "all") {
775
+ vSuiCoin = primary;
776
+ } else {
777
+ [vSuiCoin] = tx.splitCoins(primary, [input.amountMist]);
778
+ }
779
+ }
780
+ const [suiCoin] = tx.moveCall({
781
+ target: `${VOLO_PKG}::stake_pool::unstake`,
782
+ arguments: [
783
+ tx.object(VOLO_POOL),
784
+ tx.object(VOLO_METADATA),
785
+ tx.object(SUI_SYSTEM_STATE),
786
+ vSuiCoin
787
+ ]
788
+ });
789
+ return { coin: suiCoin, effectiveAmountMist: input.amountMist };
790
+ }
791
+ async function fetchCoinsByType(client, owner, coinType) {
697
792
  const all = [];
698
793
  let cursor;
699
794
  let hasNext = true;
700
795
  while (hasNext) {
701
796
  const page = await client.getCoins({
702
- owner: address,
703
- coinType: VSUI_TYPE,
797
+ owner,
798
+ coinType,
704
799
  cursor: cursor ?? void 0
705
800
  });
706
801
  all.push(...page.data.map((c) => ({ coinObjectId: c.coinObjectId, balance: c.balance })));
@@ -712,6 +807,7 @@ async function fetchVSuiCoins(client, address) {
712
807
  var VOLO_PKG, VOLO_POOL, VOLO_METADATA, VSUI_TYPE, SUI_SYSTEM_STATE, MIN_STAKE_MIST, VOLO_STATS_URL;
713
808
  var init_volo = __esm({
714
809
  "src/protocols/volo.ts"() {
810
+ init_token_registry();
715
811
  VOLO_PKG = "0x68d22cf8bdbcd11ecba1e094922873e4080d4d11133e2443fddda0bfd11dae20";
716
812
  VOLO_POOL = "0x2d914e23d82fedef1b5f56a32d5c64bdcc3087ccfea2b4d6ea51a71f587840e5";
717
813
  VOLO_METADATA = "0x680cd26af32b2bde8d3361e804c53ec1d1cfe24c7f039eb7f549e8dfde389a60";
@@ -727,6 +823,7 @@ var cetus_swap_exports = {};
727
823
  __export(cetus_swap_exports, {
728
824
  OVERLAY_FEE_RATE: () => OVERLAY_FEE_RATE,
729
825
  TOKEN_MAP: () => TOKEN_MAP,
826
+ addSwapToTx: () => addSwapToTx,
730
827
  buildSwapTx: () => buildSwapTx,
731
828
  findSwapRoute: () => findSwapRoute,
732
829
  resolveTokenType: () => resolveTokenType,
@@ -752,7 +849,8 @@ async function findSwapRoute(params) {
752
849
  from: params.from,
753
850
  target: params.to,
754
851
  amount: params.amount.toString(),
755
- byAmountIn: params.byAmountIn
852
+ byAmountIn: params.byAmountIn,
853
+ ...params.providers ? { providers: params.providers } : {}
756
854
  };
757
855
  const routerData = await client.findRouters(findParams);
758
856
  if (!routerData) return null;
@@ -794,6 +892,85 @@ async function buildSwapTx(params) {
794
892
  });
795
893
  return outputCoin;
796
894
  }
895
+ async function addSwapToTx(tx, client, address, input) {
896
+ const { T2000Error: T2000Error2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
897
+ const fromType = resolveTokenType(input.from);
898
+ const toType = resolveTokenType(input.to);
899
+ if (!fromType) throw new T2000Error2("ASSET_NOT_SUPPORTED", `Unknown token: ${input.from}. Provide the symbol (USDC, SUI, ...) or full coin type.`);
900
+ if (!toType) throw new T2000Error2("ASSET_NOT_SUPPORTED", `Unknown token: ${input.to}. Provide the symbol (USDC, SUI, ...) or full coin type.`);
901
+ if (fromType === toType) throw new T2000Error2("SWAP_FAILED", "Cannot swap a token to itself");
902
+ if (!Number.isFinite(input.amount) || input.amount <= 0) {
903
+ throw new T2000Error2("INVALID_AMOUNT", "Amount must be greater than zero");
904
+ }
905
+ const fromDecimals = getDecimalsForCoinType(fromType);
906
+ const toDecimals = getDecimalsForCoinType(toType);
907
+ const requestedRaw = BigInt(Math.floor(input.amount * 10 ** fromDecimals));
908
+ const slippage = Math.max(1e-3, Math.min(input.slippage ?? 0.01, 0.05));
909
+ const byAmountIn = input.byAmountIn ?? true;
910
+ let inputCoin;
911
+ let effectiveRaw;
912
+ if (input.inputCoin) {
913
+ inputCoin = input.inputCoin;
914
+ effectiveRaw = requestedRaw;
915
+ } else {
916
+ const { ids, totalBalance } = await fetchAllCoinsForSwap(client, address, fromType);
917
+ if (ids.length === 0) {
918
+ throw new T2000Error2("INSUFFICIENT_BALANCE", `No ${input.from} coins found in wallet`);
919
+ }
920
+ const swapAll = requestedRaw >= totalBalance;
921
+ effectiveRaw = swapAll ? totalBalance : requestedRaw;
922
+ const primary = tx.object(ids[0]);
923
+ if (ids.length > 1) {
924
+ tx.mergeCoins(primary, ids.slice(1).map((id) => tx.object(id)));
925
+ }
926
+ inputCoin = swapAll ? primary : tx.splitCoins(primary, [effectiveRaw])[0];
927
+ }
928
+ const route = await findSwapRoute({
929
+ walletAddress: address,
930
+ from: fromType,
931
+ to: toType,
932
+ amount: effectiveRaw,
933
+ byAmountIn,
934
+ overlayFee: input.overlayFee,
935
+ providers: input.providers
936
+ });
937
+ if (!route) {
938
+ throw new T2000Error2("SWAP_NO_ROUTE", `No swap route found for ${input.from} \u2192 ${input.to}`);
939
+ }
940
+ if (route.insufficientLiquidity) {
941
+ throw new T2000Error2("SWAP_NO_ROUTE", `Insufficient liquidity for ${input.from} \u2192 ${input.to}`);
942
+ }
943
+ const outputCoin = await buildSwapTx({
944
+ walletAddress: address,
945
+ route,
946
+ tx,
947
+ inputCoin,
948
+ slippage,
949
+ overlayFee: input.overlayFee
950
+ });
951
+ return {
952
+ coin: outputCoin,
953
+ effectiveAmountIn: Number(effectiveRaw) / 10 ** fromDecimals,
954
+ expectedAmountOut: Number(route.amountOut) / 10 ** toDecimals,
955
+ route
956
+ };
957
+ }
958
+ async function fetchAllCoinsForSwap(client, owner, coinType) {
959
+ const ids = [];
960
+ let totalBalance = 0n;
961
+ let cursor;
962
+ let hasNext = true;
963
+ while (hasNext) {
964
+ const page = await client.getCoins({ owner, coinType, cursor: cursor ?? void 0 });
965
+ for (const c of page.data) {
966
+ ids.push(c.coinObjectId);
967
+ totalBalance += BigInt(c.balance);
968
+ }
969
+ cursor = page.nextCursor;
970
+ hasNext = page.hasNextPage;
971
+ }
972
+ return { ids, totalBalance };
973
+ }
797
974
  async function simulateSwap(params) {
798
975
  const client = getClient(params.walletAddress, params.overlayFee);
799
976
  try {
@@ -806,6 +983,7 @@ async function simulateSwap(params) {
806
983
  var OVERLAY_FEE_RATE, clientCache;
807
984
  var init_cetus_swap = __esm({
808
985
  "src/protocols/cetus-swap.ts"() {
986
+ init_token_registry();
809
987
  init_token_registry();
810
988
  OVERLAY_FEE_RATE = 1e-3;
811
989
  clientCache = /* @__PURE__ */ new Map();
@@ -1171,6 +1349,10 @@ async function buildSendTx({
1171
1349
  }
1172
1350
  return tx;
1173
1351
  }
1352
+ function addSendToTx(tx, coin, recipient) {
1353
+ const validRecipient = validateAddress(recipient);
1354
+ tx.transferObjects([coin], validRecipient);
1355
+ }
1174
1356
 
1175
1357
  // src/wallet/balance.ts
1176
1358
  var SUI_PRICE_FALLBACK = 1;
@@ -5313,7 +5495,10 @@ async function addSaveToTx(tx, _client, _address, coin, options = {}) {
5313
5495
  async function addRepayToTx(tx, client, address, coin, options = {}) {
5314
5496
  const asset = options.asset ?? "USDC";
5315
5497
  const assetInfo = resolveAssetInfo(asset);
5316
- await refreshOracle(tx, client, address, { skipPythUpdate: options.skipPythUpdate });
5498
+ await refreshOracle(tx, client, address, {
5499
+ skipPythUpdate: options.skipPythUpdate,
5500
+ skipOracle: options.skipOracle
5501
+ });
5317
5502
  try {
5318
5503
  await xe(tx, assetInfo.type, coin, { env: "prod" });
5319
5504
  } catch (err) {
@@ -5457,6 +5642,12 @@ async function addClaimRewardsToTx(tx, client, address) {
5457
5642
  return [];
5458
5643
  }
5459
5644
  }
5645
+ async function buildClaimRewardsTx(client, address) {
5646
+ const tx = new Transaction();
5647
+ tx.setSender(address);
5648
+ const rewards = await addClaimRewardsToTx(tx, client, address);
5649
+ return { tx, rewards };
5650
+ }
5460
5651
  function aggregateClaimableRewards(claimable) {
5461
5652
  const aggregated = /* @__PURE__ */ new Map();
5462
5653
  for (const c of claimable) {
@@ -7188,6 +7379,298 @@ var T2000 = class _T2000 extends EventEmitter {
7188
7379
  // src/index.ts
7189
7380
  init_errors();
7190
7381
 
7382
+ // src/wallet/coinSelection.ts
7383
+ init_errors();
7384
+ async function fetchAllCoins(client, owner, coinType) {
7385
+ const ids = [];
7386
+ let totalBalance = 0n;
7387
+ let cursor;
7388
+ let hasNext = true;
7389
+ while (hasNext) {
7390
+ const page = await client.getCoins({ owner, coinType, cursor: cursor ?? void 0 });
7391
+ for (const c of page.data) {
7392
+ ids.push(c.coinObjectId);
7393
+ totalBalance += BigInt(c.balance);
7394
+ }
7395
+ cursor = page.nextCursor;
7396
+ hasNext = page.hasNextPage;
7397
+ }
7398
+ return { ids, totalBalance };
7399
+ }
7400
+ async function selectAndSplitCoin(tx, client, owner, coinType, amount, options = {}) {
7401
+ const { ids, totalBalance } = await fetchAllCoins(client, owner, coinType);
7402
+ if (ids.length === 0) {
7403
+ throw new T2000Error("INSUFFICIENT_BALANCE", `No coins found for ${coinType}`);
7404
+ }
7405
+ const allowSwapAll = options.allowSwapAll ?? true;
7406
+ if (amount !== "all" && amount > totalBalance && !allowSwapAll) {
7407
+ throw new T2000Error("INSUFFICIENT_BALANCE", `Insufficient balance for ${coinType}`, {
7408
+ available: totalBalance.toString(),
7409
+ required: amount.toString()
7410
+ });
7411
+ }
7412
+ const requested = amount === "all" ? totalBalance : amount;
7413
+ const swapAll = amount === "all" || requested >= totalBalance;
7414
+ const effectiveAmount = swapAll ? totalBalance : requested;
7415
+ const primary = tx.object(ids[0]);
7416
+ if (ids.length > 1) {
7417
+ tx.mergeCoins(primary, ids.slice(1).map((id) => tx.object(id)));
7418
+ }
7419
+ const coin = swapAll ? primary : tx.splitCoins(primary, [effectiveAmount])[0];
7420
+ return { coin, effectiveAmount, swapAll };
7421
+ }
7422
+ async function selectSuiCoin(tx, client, owner, amountMist, sponsoredContext) {
7423
+ if (sponsoredContext) {
7424
+ const { SUI_TYPE: SUI_TYPE2 } = await Promise.resolve().then(() => (init_token_registry(), token_registry_exports));
7425
+ return selectAndSplitCoin(tx, client, owner, SUI_TYPE2, amountMist);
7426
+ }
7427
+ const [coin] = tx.splitCoins(tx.gas, [amountMist]);
7428
+ return { coin, effectiveAmount: amountMist, swapAll: false };
7429
+ }
7430
+ init_cetus_swap();
7431
+ init_volo();
7432
+ init_token_registry();
7433
+ init_errors();
7434
+ var SPONSORED_PYTH_DEPENDENT_PROVIDERS = [
7435
+ "HAEDALPMM",
7436
+ "METASTABLE",
7437
+ "OBRIC",
7438
+ "STEAMM_OMM",
7439
+ "STEAMM_OMM_V2",
7440
+ "SEVENK",
7441
+ "HAEDALHMMV2"
7442
+ ];
7443
+ async function getSponsoredSwapProviders() {
7444
+ const { getProvidersExcluding } = await import('@cetusprotocol/aggregator-sdk');
7445
+ return getProvidersExcluding([...SPONSORED_PYTH_DEPENDENT_PROVIDERS]);
7446
+ }
7447
+ function resolveSaveableAsset(asset) {
7448
+ if (!asset) return "USDC";
7449
+ if (asset !== "USDC" && asset !== "USDsui") {
7450
+ throw new T2000Error("ASSET_NOT_SUPPORTED", `Saveable asset must be USDC or USDsui, got ${asset}`);
7451
+ }
7452
+ return asset;
7453
+ }
7454
+ var WRITE_APPENDER_REGISTRY = {
7455
+ save_deposit: async (tx, input, ctx) => {
7456
+ const asset = resolveSaveableAsset(input.asset);
7457
+ const assetInfo = SUPPORTED_ASSETS[asset];
7458
+ if (input.amount <= 0) {
7459
+ throw new T2000Error("INVALID_AMOUNT", "Save amount must be greater than zero");
7460
+ }
7461
+ const rawAmount = BigInt(Math.floor(input.amount * 10 ** assetInfo.decimals));
7462
+ const { coin, effectiveAmount } = await selectAndSplitCoin(
7463
+ tx,
7464
+ ctx.client,
7465
+ ctx.sender,
7466
+ assetInfo.type,
7467
+ rawAmount
7468
+ );
7469
+ await addSaveToTx(tx, ctx.client, ctx.sender, coin, { asset });
7470
+ return {
7471
+ toolName: "save_deposit",
7472
+ effectiveAmount: Number(effectiveAmount) / 10 ** assetInfo.decimals,
7473
+ asset
7474
+ };
7475
+ },
7476
+ withdraw: async (tx, input, ctx) => {
7477
+ const asset = resolveSaveableAsset(input.asset);
7478
+ if (input.amount <= 0) {
7479
+ throw new T2000Error("INVALID_AMOUNT", "Withdraw amount must be greater than zero");
7480
+ }
7481
+ const { coin, effectiveAmount } = await addWithdrawToTx(
7482
+ tx,
7483
+ ctx.client,
7484
+ ctx.sender,
7485
+ input.amount,
7486
+ { asset, skipPythUpdate: ctx.sponsoredContext }
7487
+ );
7488
+ tx.transferObjects([coin], ctx.sender);
7489
+ return { toolName: "withdraw", effectiveAmount, asset };
7490
+ },
7491
+ borrow: async (tx, input, ctx) => {
7492
+ const asset = resolveSaveableAsset(input.asset);
7493
+ if (input.amount <= 0) {
7494
+ throw new T2000Error("INVALID_AMOUNT", "Borrow amount must be greater than zero");
7495
+ }
7496
+ const coin = await addBorrowToTx(
7497
+ tx,
7498
+ ctx.client,
7499
+ ctx.sender,
7500
+ input.amount,
7501
+ { asset, skipPythUpdate: ctx.sponsoredContext }
7502
+ );
7503
+ tx.transferObjects([coin], ctx.sender);
7504
+ return { toolName: "borrow", effectiveAmount: input.amount, asset };
7505
+ },
7506
+ repay_debt: async (tx, input, ctx) => {
7507
+ const asset = resolveSaveableAsset(input.asset);
7508
+ const assetInfo = SUPPORTED_ASSETS[asset];
7509
+ if (input.amount <= 0) {
7510
+ throw new T2000Error("INVALID_AMOUNT", "Repay amount must be greater than zero");
7511
+ }
7512
+ const rawAmount = BigInt(Math.floor(input.amount * 10 ** assetInfo.decimals));
7513
+ const { coin, effectiveAmount } = await selectAndSplitCoin(
7514
+ tx,
7515
+ ctx.client,
7516
+ ctx.sender,
7517
+ assetInfo.type,
7518
+ rawAmount
7519
+ );
7520
+ await addRepayToTx(tx, ctx.client, ctx.sender, coin, {
7521
+ asset,
7522
+ skipOracle: ctx.sponsoredContext
7523
+ });
7524
+ return {
7525
+ toolName: "repay_debt",
7526
+ effectiveAmount: Number(effectiveAmount) / 10 ** assetInfo.decimals,
7527
+ asset
7528
+ };
7529
+ },
7530
+ send_transfer: async (tx, input, ctx) => {
7531
+ const recipient = validateAddress(input.to);
7532
+ const asset = input.asset ?? "USDC";
7533
+ const assetInfo = SUPPORTED_ASSETS[asset];
7534
+ if (!assetInfo) {
7535
+ throw new T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
7536
+ }
7537
+ if (input.amount <= 0) {
7538
+ throw new T2000Error("INVALID_AMOUNT", "Send amount must be greater than zero");
7539
+ }
7540
+ const rawAmount = BigInt(Math.floor(input.amount * 10 ** assetInfo.decimals));
7541
+ let coin;
7542
+ let effectiveRaw;
7543
+ if (asset === "SUI") {
7544
+ const result = await selectSuiCoin(tx, ctx.client, ctx.sender, rawAmount, ctx.sponsoredContext);
7545
+ coin = result.coin;
7546
+ effectiveRaw = result.effectiveAmount;
7547
+ } else {
7548
+ const result = await selectAndSplitCoin(tx, ctx.client, ctx.sender, assetInfo.type, rawAmount);
7549
+ coin = result.coin;
7550
+ effectiveRaw = result.effectiveAmount;
7551
+ }
7552
+ addSendToTx(tx, coin, recipient);
7553
+ return {
7554
+ toolName: "send_transfer",
7555
+ effectiveAmount: Number(effectiveRaw) / 10 ** assetInfo.decimals,
7556
+ recipient,
7557
+ asset
7558
+ };
7559
+ },
7560
+ swap_execute: async (tx, input, ctx) => {
7561
+ const fromType = resolveTokenType(input.from);
7562
+ const toType = resolveTokenType(input.to);
7563
+ if (!fromType || !toType) {
7564
+ throw new T2000Error(
7565
+ "ASSET_NOT_SUPPORTED",
7566
+ `Unknown token in swap: from=${input.from}, to=${input.to}`
7567
+ );
7568
+ }
7569
+ const providers = input.providers ?? (ctx.sponsoredContext ? await getSponsoredSwapProviders() : void 0);
7570
+ const result = await addSwapToTx(tx, ctx.client, ctx.sender, {
7571
+ from: input.from,
7572
+ to: input.to,
7573
+ amount: input.amount,
7574
+ slippage: input.slippage,
7575
+ byAmountIn: input.byAmountIn,
7576
+ overlayFee: ctx.overlayFee,
7577
+ providers
7578
+ });
7579
+ tx.transferObjects([result.coin], ctx.sender);
7580
+ return {
7581
+ toolName: "swap_execute",
7582
+ effectiveAmountIn: result.effectiveAmountIn,
7583
+ expectedAmountOut: result.expectedAmountOut,
7584
+ route: result.route
7585
+ };
7586
+ },
7587
+ claim_rewards: async (tx, _input, ctx) => {
7588
+ const rewards = await addClaimRewardsToTx(tx, ctx.client, ctx.sender);
7589
+ return { toolName: "claim_rewards", rewards };
7590
+ },
7591
+ volo_stake: async (tx, input, ctx) => {
7592
+ if (input.amountSui <= 0) {
7593
+ throw new T2000Error("INVALID_AMOUNT", "Stake amount must be greater than zero");
7594
+ }
7595
+ const amountMist = BigInt(Math.floor(input.amountSui * 1e9));
7596
+ const result = await addStakeVSuiToTx(tx, ctx.client, ctx.sender, { amountMist });
7597
+ tx.transferObjects([result.coin], ctx.sender);
7598
+ return { toolName: "volo_stake", effectiveAmountMist: result.effectiveAmountMist };
7599
+ },
7600
+ volo_unstake: async (tx, input, ctx) => {
7601
+ const amountMist = input.amountVSui === "all" ? "all" : BigInt(Math.floor(input.amountVSui * 1e9));
7602
+ if (amountMist !== "all" && amountMist <= 0n) {
7603
+ throw new T2000Error("INVALID_AMOUNT", "Unstake amount must be greater than zero");
7604
+ }
7605
+ const result = await addUnstakeVSuiToTx(tx, ctx.client, ctx.sender, { amountMist });
7606
+ tx.transferObjects([result.coin], ctx.sender);
7607
+ return { toolName: "volo_unstake", effectiveAmountMist: result.effectiveAmountMist };
7608
+ }
7609
+ };
7610
+ function deriveAllowedAddressesFromPtb(tx) {
7611
+ const addresses = /* @__PURE__ */ new Set();
7612
+ const data = tx.getData();
7613
+ for (const cmd of data.commands) {
7614
+ const transferCmd = cmd.TransferObjects;
7615
+ if (!transferCmd) continue;
7616
+ const addressArg = transferCmd.address;
7617
+ if (!addressArg) continue;
7618
+ const addressInputIndex = addressArg.Input;
7619
+ if (addressInputIndex === void 0) continue;
7620
+ const input = data.inputs[addressInputIndex];
7621
+ if (!input) continue;
7622
+ const pureBytes = input.Pure?.bytes;
7623
+ if (!pureBytes) continue;
7624
+ try {
7625
+ const bytes = base64ToBytes(pureBytes);
7626
+ if (bytes.length !== 32) continue;
7627
+ const hex = "0x" + Array.from(bytes).map((b2) => b2.toString(16).padStart(2, "0")).join("");
7628
+ addresses.add(hex);
7629
+ } catch {
7630
+ }
7631
+ }
7632
+ return Array.from(addresses);
7633
+ }
7634
+ function base64ToBytes(b64) {
7635
+ if (typeof Buffer !== "undefined") {
7636
+ return Uint8Array.from(Buffer.from(b64, "base64"));
7637
+ }
7638
+ const binary = atob(b64);
7639
+ const bytes = new Uint8Array(binary.length);
7640
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
7641
+ return bytes;
7642
+ }
7643
+ async function composeTx(opts) {
7644
+ const tx = new Transaction();
7645
+ tx.setSender(opts.sender);
7646
+ const ctx = {
7647
+ client: opts.client,
7648
+ sender: opts.sender,
7649
+ sponsoredContext: opts.sponsoredContext ?? false,
7650
+ overlayFee: opts.overlayFee
7651
+ };
7652
+ const previews = [];
7653
+ for (const step of opts.steps) {
7654
+ const appender = WRITE_APPENDER_REGISTRY[step.toolName];
7655
+ if (!appender) {
7656
+ throw new T2000Error(
7657
+ "UNKNOWN",
7658
+ `No fragment appender registered for tool '${step.toolName}'. Allowed: ${Object.keys(WRITE_APPENDER_REGISTRY).join(", ")}`
7659
+ );
7660
+ }
7661
+ const preview = await appender(tx, step.input, ctx);
7662
+ previews.push(preview);
7663
+ }
7664
+ const txKindBytes = await tx.build({ client: opts.client, onlyTransactionKind: true });
7665
+ const derivedAllowedAddresses = deriveAllowedAddressesFromPtb(tx);
7666
+ return {
7667
+ tx,
7668
+ txKindBytes,
7669
+ derivedAllowedAddresses,
7670
+ perStepPreviews: previews
7671
+ };
7672
+ }
7673
+
7191
7674
  // src/protocols/protocolFee.ts
7192
7675
  var FEE_RATES = {
7193
7676
  save: SAVE_FEE_BPS,
@@ -7401,6 +7884,6 @@ init_volo();
7401
7884
  (*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
7402
7885
  */
7403
7886
 
7404
- export { ALL_NAVI_ASSETS, BORROW_FEE_BPS, BPS_DENOMINATOR, CETUS_USDC_SUI_POOL, CLOCK_ID, COIN_REGISTRY, ContactManager, DEFAULT_NETWORK, DEFAULT_SAFEGUARD_CONFIG, ETH_TYPE, GAS_RESERVE_MIN, HF_CRITICAL_THRESHOLD, HF_WARN_THRESHOLD, IKA_TYPE, KNOWN_TARGETS, KeypairSigner, LABEL_PATTERNS, LOFI_TYPE, MANIFEST_TYPE, MIST_PER_SUI, NAVX_TYPE, NaviAdapter, OPERATION_ASSETS, OUTBOUND_OPS, OVERLAY_FEE_RATE, ProtocolRegistry, SAVE_FEE_BPS, STABLE_ASSETS, SUI_DECIMALS, SUI_TYPE, SUPPORTED_ASSETS, SafeguardEnforcer, SafeguardError, T2000, T2000Error, T2000_OVERLAY_FEE_WALLET, TOKEN_MAP, USDC_DECIMALS, USDC_TYPE, USDE_TYPE, USDSUI_TYPE, USDT_TYPE, VOLO_METADATA, VOLO_PKG, VOLO_POOL, VSUI_TYPE, WAL_TYPE, WBTC_TYPE, ZkLoginSigner, addFeeTransfer, allDescriptors, assertAllowedAsset, buildStakeVSuiTx, buildSwapTx, buildUnstakeVSuiTx, calculateFee, classifyAction, classifyLabel, classifyTransaction, exportPrivateKey, extractTransferDetails, extractTxCommands, extractTxSender, fallbackLabel, findSwapRoute, formatAssetAmount, formatSui, formatUsd, generateKeypair, getAddress, getCoinMeta, getDecimals, getDecimalsForCoinType, getFinancialSummary, getPendingRewards, getRates, getSwapQuote, getTier, getVoloStats, isAllowedAsset, isInRegistry, isSupported, isTier1, isTier2, keypairFromPrivateKey, loadKey, mapMoveAbortCode, mapWalletError, mistToSui, naviDescriptor, normalizeCoinType, parseSuiRpcTx, queryHistory, queryTransaction, rawToStable, rawToUsdc, refineLendingLabel, resolveSymbol, resolveTokenType, saveKey, simulateTransaction, stableToRaw, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, walletExists };
7887
+ export { ALL_NAVI_ASSETS, BORROW_FEE_BPS, BPS_DENOMINATOR, CETUS_USDC_SUI_POOL, CLOCK_ID, COIN_REGISTRY, ContactManager, DEFAULT_NETWORK, DEFAULT_SAFEGUARD_CONFIG, ETH_TYPE, GAS_RESERVE_MIN, HF_CRITICAL_THRESHOLD, HF_WARN_THRESHOLD, IKA_TYPE, KNOWN_TARGETS, KeypairSigner, LABEL_PATTERNS, LOFI_TYPE, MANIFEST_TYPE, MIST_PER_SUI, NAVX_TYPE, NaviAdapter, OPERATION_ASSETS, OUTBOUND_OPS, OVERLAY_FEE_RATE, ProtocolRegistry, SAVE_FEE_BPS, STABLE_ASSETS, SUI_DECIMALS, SUI_TYPE, SUPPORTED_ASSETS, SafeguardEnforcer, SafeguardError, T2000, T2000Error, T2000_OVERLAY_FEE_WALLET, TOKEN_MAP, USDC_DECIMALS, USDC_TYPE, USDE_TYPE, USDSUI_TYPE, USDT_TYPE, VOLO_METADATA, VOLO_PKG, VOLO_POOL, VSUI_TYPE, WAL_TYPE, WBTC_TYPE, WRITE_APPENDER_REGISTRY, ZkLoginSigner, addFeeTransfer, addSendToTx, addStakeVSuiToTx, addSwapToTx, addUnstakeVSuiToTx, allDescriptors, assertAllowedAsset, buildClaimRewardsTx, buildSendTx, buildStakeVSuiTx, buildSwapTx, buildUnstakeVSuiTx, calculateFee, classifyAction, classifyLabel, classifyTransaction, composeTx, deriveAllowedAddressesFromPtb, exportPrivateKey, extractTransferDetails, extractTxCommands, extractTxSender, fallbackLabel, fetchAllCoins, findSwapRoute, formatAssetAmount, formatSui, formatUsd, generateKeypair, getAddress, getCoinMeta, getDecimals, getDecimalsForCoinType, getFinancialSummary, getPendingRewards, getRates, getSwapQuote, getTier, getVoloStats, isAllowedAsset, isInRegistry, isSupported, isTier1, isTier2, keypairFromPrivateKey, loadKey, mapMoveAbortCode, mapWalletError, mistToSui, naviDescriptor, normalizeCoinType, parseSuiRpcTx, queryHistory, queryTransaction, rawToStable, rawToUsdc, refineLendingLabel, resolveSymbol, resolveTokenType, saveKey, selectAndSplitCoin, selectSuiCoin, simulateTransaction, stableToRaw, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, walletExists };
7405
7888
  //# sourceMappingURL=index.js.map
7406
7889
  //# sourceMappingURL=index.js.map