@sodax/sdk 1.0.4-beta-rc1 → 1.0.4-beta

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.cjs CHANGED
@@ -6871,6 +6871,7 @@ var getAllLegacybnUSDTokens = () => {
6871
6871
  var ConfigService = class {
6872
6872
  serviceConfig;
6873
6873
  backendApiService;
6874
+ sharedConfig;
6874
6875
  initialized = false;
6875
6876
  sodaxConfig;
6876
6877
  // data structures for quick lookup
@@ -6883,7 +6884,7 @@ var ConfigService = class {
6883
6884
  supportedTokensPerChain;
6884
6885
  moneyMarketReserveAssetsSet;
6885
6886
  spokeChainIdsSet;
6886
- constructor({ backendApiService, config }) {
6887
+ constructor({ backendApiService, config, sharedConfig }) {
6887
6888
  this.serviceConfig = {
6888
6889
  backendApiUrl: config?.backendApiUrl ?? DEFAULT_BACKEND_API_ENDPOINT,
6889
6890
  timeout: config?.timeout ?? DEFAULT_BACKEND_API_TIMEOUT
@@ -6891,6 +6892,10 @@ var ConfigService = class {
6891
6892
  this.backendApiService = backendApiService;
6892
6893
  this.sodaxConfig = types.defaultSodaxConfig;
6893
6894
  this.loadSodaxConfigDataStructures(this.sodaxConfig);
6895
+ this.sharedConfig = {
6896
+ ...types.defaultSharedConfig,
6897
+ ...sharedConfig
6898
+ };
6894
6899
  }
6895
6900
  async initialize() {
6896
6901
  try {
@@ -7507,6 +7512,30 @@ var EvmVaultTokenService = class {
7507
7512
  });
7508
7513
  return { decimals, depositFee, withdrawalFee, maxDeposit, isSupported };
7509
7514
  }
7515
+ /**
7516
+ * Fetches token information for a list of tokens in a vault using a multicall.
7517
+ * @param vault - The address of the vault contract.
7518
+ * @param tokens - An array of token addresses to fetch info for.
7519
+ * @param publicClient - The Viem PublicClient instance used to interact with the blockchain.
7520
+ * @returns A promise that resolves to an array of TokenInfo objects, one for each provided token.
7521
+ */
7522
+ static async getTokenInfos(vault, tokens, publicClient) {
7523
+ const infos = await publicClient.multicall({
7524
+ contracts: tokens.map(
7525
+ (token) => ({
7526
+ address: vault,
7527
+ abi: vaultTokenAbi,
7528
+ functionName: "tokenInfo",
7529
+ args: [token]
7530
+ })
7531
+ ),
7532
+ allowFailure: false
7533
+ });
7534
+ return infos.map((info) => {
7535
+ const [decimals, depositFee, withdrawalFee, maxDeposit, isSupported] = info;
7536
+ return { decimals, depositFee, withdrawalFee, maxDeposit, isSupported };
7537
+ });
7538
+ }
7510
7539
  /**
7511
7540
  * Retrieves the reserves of the vault.
7512
7541
  * @param vault - The address of the vault.
@@ -10460,6 +10489,38 @@ var StellarSpokeService = class _StellarSpokeService {
10460
10489
  const availableTrustAmount = limit - balance;
10461
10490
  return availableTrustAmount >= amount;
10462
10491
  }
10492
+ /**
10493
+ * Check if the user has sufficent trustline established for the token.
10494
+ * @param token - The token address to check the trustline for.
10495
+ * @param amount - The amount of tokens to check the trustline for.
10496
+ * @param spokeProvider - The Stellar spoke provider.
10497
+ * @returns True if the user has sufficent trustline established for the token, false otherwise.
10498
+ */
10499
+ static async walletHasSufficientTrustline(token, amount, walletAddress, horizonRpcUrl) {
10500
+ const stellarChainConfig = types.spokeChainConfig[types.STELLAR_MAINNET_CHAIN_ID];
10501
+ if (token.toLowerCase() === stellarChainConfig.nativeToken.toLowerCase() || token.toLowerCase() === stellarChainConfig.supportedTokens.legacybnUSD.address.toLowerCase()) {
10502
+ return true;
10503
+ }
10504
+ const trustlineConfig = stellarChainConfig.trustlineConfigs.find(
10505
+ (config) => config.contractId.toLowerCase() === token.toLowerCase()
10506
+ );
10507
+ if (!trustlineConfig) {
10508
+ throw new Error(`Trustline config not found for token: ${token}`);
10509
+ }
10510
+ const server = new stellarSdk.Horizon.Server(horizonRpcUrl, { allowHttp: true });
10511
+ const { balances } = await server.accounts().accountId(walletAddress).call();
10512
+ const tokenBalance = balances.find(
10513
+ (balance2) => "limit" in balance2 && "balance" in balance2 && "asset_code" in balance2 && trustlineConfig.assetCode.toLowerCase() === balance2.asset_code?.toLowerCase() && "asset_issuer" in balance2 && trustlineConfig.assetIssuer.toLowerCase() === balance2.asset_issuer?.toLowerCase()
10514
+ );
10515
+ if (!tokenBalance) {
10516
+ console.error(`No token balances found for token: ${token}`);
10517
+ return false;
10518
+ }
10519
+ const limit = parseToStroops(tokenBalance.limit);
10520
+ const balance = parseToStroops(tokenBalance.balance);
10521
+ const availableTrustAmount = limit - balance;
10522
+ return availableTrustAmount >= amount;
10523
+ }
10463
10524
  /**
10464
10525
  * Request a trustline for a given token and amount.
10465
10526
  * @param token - The token address to request the trustline for.
@@ -11878,7 +11939,7 @@ var SolanaSpokeService = class _SolanaSpokeService {
11878
11939
  }
11879
11940
  return spokeProvider.walletProvider.sendTransaction(serializedTransaction);
11880
11941
  }
11881
- static async waitForConfirmation(spokeProvider, signature, commitment = "finalized", timeoutMs = 12e4) {
11942
+ static async waitForConfirmation(spokeProvider, signature, commitment = "finalized", timeoutMs = 6e4) {
11882
11943
  try {
11883
11944
  const connection = new web3_js.Connection(spokeProvider.chainConfig.rpcUrl, commitment);
11884
11945
  const deadline = Date.now() + timeoutMs;
@@ -13919,7 +13980,27 @@ var MoneyMarketService = class _MoneyMarketService {
13919
13980
  );
13920
13981
  }
13921
13982
  const walletAddress = await spokeProvider.walletProvider.getWalletAddress();
13922
- if (isStellarSpokeProviderType(spokeProvider) && (params.action === "supply" || params.action === "repay")) {
13983
+ if (params.toChainId === types.STELLAR_MAINNET_CHAIN_ID && params.toAddress) {
13984
+ const targetHasTrustline = await StellarSpokeService.walletHasSufficientTrustline(
13985
+ params.token,
13986
+ params.amount,
13987
+ params.toAddress,
13988
+ this.configService.sharedConfig[types.STELLAR_MAINNET_CHAIN_ID].horizonRpcUrl
13989
+ );
13990
+ let srcHasTrustline = true;
13991
+ if (isStellarSpokeProviderType(spokeProvider)) {
13992
+ srcHasTrustline = await StellarSpokeService.hasSufficientTrustline(
13993
+ params.token,
13994
+ params.amount,
13995
+ spokeProvider
13996
+ );
13997
+ }
13998
+ return {
13999
+ ok: true,
14000
+ value: targetHasTrustline && srcHasTrustline
14001
+ };
14002
+ }
14003
+ if (isStellarSpokeProviderType(spokeProvider)) {
13923
14004
  return {
13924
14005
  ok: true,
13925
14006
  value: await StellarSpokeService.hasSufficientTrustline(params.token, params.amount, spokeProvider)
@@ -13953,6 +14034,9 @@ var MoneyMarketService = class _MoneyMarketService {
13953
14034
  * Approve amount spending if isAllowanceValid returns false.
13954
14035
  * For evm spoke chains, the spender is the asset manager contract while
13955
14036
  * for sonic spoke (hub) chain, the spender is the user router contract.
14037
+ * NOTE: Stellar requires trustline when being either sender or receiver. Thus
14038
+ * you should make sure that both src and destination wallets have sufficient trustlines.
14039
+ * Make sure to invoke this method using wallet address which is about to receive the tokens!
13956
14040
  * @param token - ERC20 token address
13957
14041
  * @param amount - Amount to approve
13958
14042
  * @param spender - Spender address
@@ -13994,10 +14078,6 @@ var MoneyMarketService = class _MoneyMarketService {
13994
14078
  }
13995
14079
  const walletAddress = await spokeProvider.walletProvider.getWalletAddress();
13996
14080
  if (isStellarSpokeProviderType(spokeProvider)) {
13997
- invariant6__default.default(
13998
- params.action === "supply" || params.action === "repay",
13999
- "Invalid action (only supply and repay are supported on stellar)"
14000
- );
14001
14081
  const result = await StellarSpokeService.requestTrustline(params.token, params.amount, spokeProvider, raw);
14002
14082
  return {
14003
14083
  ok: true,
@@ -14188,7 +14268,7 @@ var MoneyMarketService = class _MoneyMarketService {
14188
14268
  ok: true,
14189
14269
  value: txResult,
14190
14270
  data: {
14191
- address: toHubWallet,
14271
+ address: fromHubWallet,
14192
14272
  payload: data
14193
14273
  }
14194
14274
  };
@@ -14255,7 +14335,7 @@ var MoneyMarketService = class _MoneyMarketService {
14255
14335
  if (spokeProvider.chainConfig.chain.id !== this.hubProvider.chainConfig.chain.id || params.toChainId && params.toAddress && params.toChainId !== this.hubProvider.chainConfig.chain.id) {
14256
14336
  const packetResult = await relayTxAndWaitPacket(
14257
14337
  txResult.value,
14258
- isSolanaSpokeProviderType(spokeProvider) ? txResult.data : void 0,
14338
+ spokeProvider instanceof SolanaSpokeProvider ? txResult.data : void 0,
14259
14339
  spokeProvider,
14260
14340
  this.config.relayerApiEndpoint,
14261
14341
  timeout
@@ -14540,7 +14620,7 @@ var MoneyMarketService = class _MoneyMarketService {
14540
14620
  if (spokeProvider.chainConfig.chain.id !== this.hubProvider.chainConfig.chain.id) {
14541
14621
  const packetResult = await relayTxAndWaitPacket(
14542
14622
  txResult.value,
14543
- spokeProvider instanceof SolanaSpokeProvider ? txResult.data : void 0,
14623
+ isSolanaSpokeProviderType(spokeProvider) ? txResult.data : void 0,
14544
14624
  spokeProvider,
14545
14625
  this.config.relayerApiEndpoint,
14546
14626
  timeout
@@ -14640,7 +14720,7 @@ var MoneyMarketService = class _MoneyMarketService {
14640
14720
  ok: true,
14641
14721
  value: txResult,
14642
14722
  data: {
14643
- address: toHubWallet,
14723
+ address: fromHubWallet,
14644
14724
  payload: data
14645
14725
  }
14646
14726
  };
@@ -15042,7 +15122,8 @@ var Sodax = class {
15042
15122
  config: {
15043
15123
  backendApiUrl: config?.backendApiConfig?.baseURL,
15044
15124
  timeout: config?.backendApiConfig?.timeout
15045
- }
15125
+ },
15126
+ sharedConfig: config?.sharedConfig
15046
15127
  });
15047
15128
  this.hubProvider = new EvmHubProvider({ config: config?.hubProviderConfig, configService: this.config });
15048
15129
  this.swaps = config && config.swaps ? new SwapService({
@@ -16529,7 +16610,7 @@ var BridgeService = class {
16529
16610
  *
16530
16611
  * @param spokeProvider - The spoke provider instance
16531
16612
  * @param token - The token address to query the balance for
16532
- * @returns {Promise<bigint>} - The token balance as a bigint value
16613
+ * @returns {Promise<BridgeLimit>} - The max bridgeable amount with corresponding decimals
16533
16614
  */
16534
16615
  async getBridgeableAmount(from, to) {
16535
16616
  try {
@@ -16537,51 +16618,59 @@ var BridgeService = class {
16537
16618
  const toHubAsset = this.configService.getHubAssetInfo(to.xChainId, to.address);
16538
16619
  invariant6__default.default(fromHubAsset, `Hub asset not found for token ${from.address} on chain ${from.xChainId}`);
16539
16620
  invariant6__default.default(toHubAsset, `Hub asset not found for token ${to.address} on chain ${to.xChainId}`);
16540
- const [depositTokenInfo, reserves] = await Promise.all([
16541
- EvmVaultTokenService.getTokenInfo(fromHubAsset.vault, fromHubAsset.asset, this.hubProvider.publicClient),
16621
+ invariant6__default.default(this.isBridgeable({ from, to }), `Tokens ${from.address} and ${to.address} are not bridgeable`);
16622
+ const [tokenInfos, reserves] = await Promise.all([
16623
+ EvmVaultTokenService.getTokenInfos(
16624
+ fromHubAsset.vault,
16625
+ [fromHubAsset.asset, toHubAsset.asset],
16626
+ this.hubProvider.publicClient
16627
+ ),
16542
16628
  EvmVaultTokenService.getVaultReserves(toHubAsset.vault, this.hubProvider.publicClient)
16543
16629
  ]);
16544
- if (!this.configService.isValidVault(fromHubAsset.asset) && this.configService.isValidVault(toHubAsset.asset)) {
16545
- const fromTokenIndex2 = reserves.tokens.findIndex((t) => t.toLowerCase() === fromHubAsset.asset.toLowerCase());
16546
- invariant6__default.default(
16547
- fromTokenIndex2 !== -1,
16548
- `Token ${fromHubAsset.asset} not found in the vault reserves for chain ${from.xChainId}`
16549
- );
16550
- const fromTokenDepositedAmount2 = reserves.balances[fromTokenIndex2] ?? 0n;
16551
- const availableDeposit2 = depositTokenInfo.maxDeposit - fromTokenDepositedAmount2;
16630
+ invariant6__default.default(tokenInfos.length === 2, `Expected 2 token infos, got ${tokenInfos.length}`);
16631
+ const [fromTokenInfo, toTokenInfo] = tokenInfos;
16632
+ invariant6__default.default(fromTokenInfo, "From token info not found");
16633
+ invariant6__default.default(toTokenInfo, "To token info not found");
16634
+ if (!fromTokenInfo.isSupported) {
16552
16635
  return {
16553
16636
  ok: true,
16554
- value: availableDeposit2
16637
+ value: {
16638
+ amount: 0n,
16639
+ decimals: fromTokenInfo.decimals,
16640
+ type: "DEPOSIT_LIMIT"
16641
+ }
16555
16642
  };
16556
16643
  }
16557
- if (this.configService.isValidVault(fromHubAsset.asset)) {
16558
- const tokenIndex2 = reserves.tokens.findIndex((t) => t.toLowerCase() === toHubAsset.asset.toLowerCase());
16559
- invariant6__default.default(
16560
- tokenIndex2 !== -1,
16561
- `Token ${toHubAsset.asset} not found in the vault reserves for chain ${to.xChainId}`
16562
- );
16563
- const assetManagerBalance2 = reserves.balances[tokenIndex2] ?? 0n;
16644
+ if (from.xChainId !== this.hubProvider.chainConfig.chain.id && to.xChainId === this.hubProvider.chainConfig.chain.id) {
16645
+ const fromTokenDepositedAmount2 = this.findTokenBalanceInReserves(reserves, from);
16646
+ const availableDeposit2 = fromTokenInfo.maxDeposit - fromTokenDepositedAmount2;
16564
16647
  return {
16565
16648
  ok: true,
16566
- value: assetManagerBalance2
16649
+ value: {
16650
+ amount: availableDeposit2,
16651
+ decimals: fromTokenInfo.decimals,
16652
+ type: "DEPOSIT_LIMIT"
16653
+ }
16567
16654
  };
16568
16655
  }
16569
- const fromTokenIndex = reserves.tokens.findIndex((t) => t.toLowerCase() === fromHubAsset.asset.toLowerCase());
16570
- invariant6__default.default(
16571
- fromTokenIndex !== -1,
16572
- `Token ${fromHubAsset.asset} not found in the vault reserves for chain ${from.xChainId}`
16573
- );
16574
- const fromTokenDepositedAmount = reserves.balances[fromTokenIndex] ?? 0n;
16575
- const availableDeposit = depositTokenInfo.maxDeposit - fromTokenDepositedAmount;
16576
- const tokenIndex = reserves.tokens.findIndex((t) => t.toLowerCase() === toHubAsset.asset.toLowerCase());
16577
- invariant6__default.default(
16578
- tokenIndex !== -1,
16579
- `Token ${toHubAsset.asset} not found in the vault reserves for chain ${to.xChainId}`
16580
- );
16581
- const assetManagerBalance = reserves.balances[tokenIndex] ?? 0n;
16656
+ if (from.xChainId === this.hubProvider.chainConfig.chain.id && to.xChainId !== this.hubProvider.chainConfig.chain.id) {
16657
+ return {
16658
+ ok: true,
16659
+ value: {
16660
+ amount: this.findTokenBalanceInReserves(reserves, to),
16661
+ decimals: toTokenInfo.decimals,
16662
+ type: "WITHDRAWAL_LIMIT"
16663
+ }
16664
+ };
16665
+ }
16666
+ const fromTokenDepositedAmount = this.findTokenBalanceInReserves(reserves, from);
16667
+ const availableDeposit = fromTokenInfo.maxDeposit - fromTokenDepositedAmount;
16668
+ const assetManagerBalance = this.findTokenBalanceInReserves(reserves, to);
16669
+ const availableDepositNormalised = BigNumber4__default.default(availableDeposit).shiftedBy(-fromTokenInfo.decimals);
16670
+ const assetManagerBalanceNormalised = BigNumber4__default.default(assetManagerBalance).shiftedBy(-toTokenInfo.decimals);
16582
16671
  return {
16583
16672
  ok: true,
16584
- value: availableDeposit < assetManagerBalance ? availableDeposit : assetManagerBalance
16673
+ value: availableDepositNormalised.isLessThan(assetManagerBalanceNormalised) ? { amount: availableDeposit, decimals: fromTokenInfo.decimals, type: "DEPOSIT_LIMIT" } : { amount: assetManagerBalance, decimals: toTokenInfo.decimals, type: "WITHDRAWAL_LIMIT" }
16585
16674
  };
16586
16675
  } catch (error) {
16587
16676
  console.error(error);
@@ -16658,6 +16747,13 @@ var BridgeService = class {
16658
16747
  }
16659
16748
  return bridgeableTokens;
16660
16749
  }
16750
+ findTokenBalanceInReserves(reserves, token) {
16751
+ const hubAsset = this.configService.getHubAssetInfo(token.xChainId, token.address);
16752
+ invariant6__default.default(hubAsset, `Hub asset not found for token ${token.address} on chain ${token.xChainId}`);
16753
+ const tokenIndex = reserves.tokens.findIndex((t) => t.toLowerCase() === hubAsset.asset.toLowerCase());
16754
+ invariant6__default.default(tokenIndex !== -1, `Token ${hubAsset.asset} not found in the vault reserves for chain ${token.xChainId}`);
16755
+ return reserves.balances[tokenIndex] ?? 0n;
16756
+ }
16661
16757
  };
16662
16758
  var StakingLogic = class _StakingLogic {
16663
16759
  constructor() {