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

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/README.md CHANGED
@@ -7,21 +7,21 @@ The Sodax SDK provides a comprehensive interface for interacting with the Sodax
7
7
  ### Features
8
8
 
9
9
  - [Swaps (Solver / Intents)](./docs/SWAPS.md) - Cross-chain intent-based swaps
10
- - EVM (Arbitrum, Avalanche, Base, BSC, Optimism, Polygon, Sonic, HyperEVM, Lightlink) ✅
10
+ - EVM (Arbitrum, Avalanche, Base, BSC, Optimism, Polygon, Sonic, HyperEVM, Lightlink, Kaia) ✅
11
11
  - Sui ✅
12
12
  - Stellar ✅
13
13
  - ICON ✅
14
14
  - Solana ✅
15
15
  - Injective ✅
16
16
  - [Money Market](./docs/MONEY_MARKET.md) - Cross-chain lending and borrowing
17
- - EVM (Arbitrum, Avalanche, Base, BSC, Optimism, Polygon, Sonic, HyperEVM, Lightlink) ✅
17
+ - EVM (Arbitrum, Avalanche, Base, BSC, Optimism, Polygon, Sonic, HyperEVM, Lightlink, Kaia) ✅
18
18
  - Sui ✅
19
19
  - Stellar ✅
20
20
  - ICON ✅
21
21
  - Solana ✅
22
22
  - Injective ✅
23
23
  - [Bridge](./docs/BRIDGE.md) - Cross-chain token bridging
24
- - EVM (Arbitrum, Avalanche, Base, BSC, Optimism, Polygon, Sonic, HyperEVM, Lightlink) ✅
24
+ - EVM (Arbitrum, Avalanche, Base, BSC, Optimism, Polygon, Sonic, HyperEVM, Lightlink, Kaia) ✅
25
25
  - Sui ✅
26
26
  - Stellar ✅
27
27
  - ICON ✅
@@ -29,7 +29,7 @@ The Sodax SDK provides a comprehensive interface for interacting with the Sodax
29
29
  - Injective ✅
30
30
  - [Migration](./docs/MIGRATION.md) - Token migration (ICX, bnUSD, BALN)
31
31
  - [Staking](./docs/STAKING.md) - SODA token staking
32
- - EVM (Arbitrum, Avalanche, Base, BSC, Optimism, Polygon, Sonic, HyperEVM, Lightlink) ✅
32
+ - EVM (Arbitrum, Avalanche, Base, BSC, Optimism, Polygon, Sonic, HyperEVM, Lightlink, Kaia) ✅
33
33
  - Sui ✅
34
34
  - Stellar ✅
35
35
  - ICON ✅
package/dist/index.cjs CHANGED
@@ -6826,6 +6826,8 @@ function getEvmViemChain(id) {
6826
6826
  return chains.lightlinkPhoenix;
6827
6827
  case types.ETHEREUM_MAINNET_CHAIN_ID:
6828
6828
  return chains.mainnet;
6829
+ case types.KAIA_MAINNET_CHAIN_ID:
6830
+ return chains.kaia;
6829
6831
  default:
6830
6832
  throw new Error(`Unsupported EVM chain ID: ${id}`);
6831
6833
  }
@@ -6871,6 +6873,7 @@ var getAllLegacybnUSDTokens = () => {
6871
6873
  var ConfigService = class {
6872
6874
  serviceConfig;
6873
6875
  backendApiService;
6876
+ sharedConfig;
6874
6877
  initialized = false;
6875
6878
  sodaxConfig;
6876
6879
  // data structures for quick lookup
@@ -6883,7 +6886,7 @@ var ConfigService = class {
6883
6886
  supportedTokensPerChain;
6884
6887
  moneyMarketReserveAssetsSet;
6885
6888
  spokeChainIdsSet;
6886
- constructor({ backendApiService, config }) {
6889
+ constructor({ backendApiService, config, sharedConfig }) {
6887
6890
  this.serviceConfig = {
6888
6891
  backendApiUrl: config?.backendApiUrl ?? DEFAULT_BACKEND_API_ENDPOINT,
6889
6892
  timeout: config?.timeout ?? DEFAULT_BACKEND_API_TIMEOUT
@@ -6891,6 +6894,10 @@ var ConfigService = class {
6891
6894
  this.backendApiService = backendApiService;
6892
6895
  this.sodaxConfig = types.defaultSodaxConfig;
6893
6896
  this.loadSodaxConfigDataStructures(this.sodaxConfig);
6897
+ this.sharedConfig = {
6898
+ ...types.defaultSharedConfig,
6899
+ ...sharedConfig
6900
+ };
6894
6901
  }
6895
6902
  async initialize() {
6896
6903
  try {
@@ -7507,6 +7514,30 @@ var EvmVaultTokenService = class {
7507
7514
  });
7508
7515
  return { decimals, depositFee, withdrawalFee, maxDeposit, isSupported };
7509
7516
  }
7517
+ /**
7518
+ * Fetches token information for a list of tokens in a vault using a multicall.
7519
+ * @param vault - The address of the vault contract.
7520
+ * @param tokens - An array of token addresses to fetch info for.
7521
+ * @param publicClient - The Viem PublicClient instance used to interact with the blockchain.
7522
+ * @returns A promise that resolves to an array of TokenInfo objects, one for each provided token.
7523
+ */
7524
+ static async getTokenInfos(vault, tokens, publicClient) {
7525
+ const infos = await publicClient.multicall({
7526
+ contracts: tokens.map(
7527
+ (token) => ({
7528
+ address: vault,
7529
+ abi: vaultTokenAbi,
7530
+ functionName: "tokenInfo",
7531
+ args: [token]
7532
+ })
7533
+ ),
7534
+ allowFailure: false
7535
+ });
7536
+ return infos.map((info) => {
7537
+ const [decimals, depositFee, withdrawalFee, maxDeposit, isSupported] = info;
7538
+ return { decimals, depositFee, withdrawalFee, maxDeposit, isSupported };
7539
+ });
7540
+ }
7510
7541
  /**
7511
7542
  * Retrieves the reserves of the vault.
7512
7543
  * @param vault - The address of the vault.
@@ -10460,6 +10491,38 @@ var StellarSpokeService = class _StellarSpokeService {
10460
10491
  const availableTrustAmount = limit - balance;
10461
10492
  return availableTrustAmount >= amount;
10462
10493
  }
10494
+ /**
10495
+ * Check if the user has sufficent trustline established for the token.
10496
+ * @param token - The token address to check the trustline for.
10497
+ * @param amount - The amount of tokens to check the trustline for.
10498
+ * @param spokeProvider - The Stellar spoke provider.
10499
+ * @returns True if the user has sufficent trustline established for the token, false otherwise.
10500
+ */
10501
+ static async walletHasSufficientTrustline(token, amount, walletAddress, horizonRpcUrl) {
10502
+ const stellarChainConfig = types.spokeChainConfig[types.STELLAR_MAINNET_CHAIN_ID];
10503
+ if (token.toLowerCase() === stellarChainConfig.nativeToken.toLowerCase() || token.toLowerCase() === stellarChainConfig.supportedTokens.legacybnUSD.address.toLowerCase()) {
10504
+ return true;
10505
+ }
10506
+ const trustlineConfig = stellarChainConfig.trustlineConfigs.find(
10507
+ (config) => config.contractId.toLowerCase() === token.toLowerCase()
10508
+ );
10509
+ if (!trustlineConfig) {
10510
+ throw new Error(`Trustline config not found for token: ${token}`);
10511
+ }
10512
+ const server = new stellarSdk.Horizon.Server(horizonRpcUrl, { allowHttp: true });
10513
+ const { balances } = await server.accounts().accountId(walletAddress).call();
10514
+ const tokenBalance = balances.find(
10515
+ (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()
10516
+ );
10517
+ if (!tokenBalance) {
10518
+ console.error(`No token balances found for token: ${token}`);
10519
+ return false;
10520
+ }
10521
+ const limit = parseToStroops(tokenBalance.limit);
10522
+ const balance = parseToStroops(tokenBalance.balance);
10523
+ const availableTrustAmount = limit - balance;
10524
+ return availableTrustAmount >= amount;
10525
+ }
10463
10526
  /**
10464
10527
  * Request a trustline for a given token and amount.
10465
10528
  * @param token - The token address to request the trustline for.
@@ -13919,7 +13982,27 @@ var MoneyMarketService = class _MoneyMarketService {
13919
13982
  );
13920
13983
  }
13921
13984
  const walletAddress = await spokeProvider.walletProvider.getWalletAddress();
13922
- if (isStellarSpokeProviderType(spokeProvider) && (params.action === "supply" || params.action === "repay")) {
13985
+ if (params.toChainId === types.STELLAR_MAINNET_CHAIN_ID && params.toAddress) {
13986
+ const targetHasTrustline = await StellarSpokeService.walletHasSufficientTrustline(
13987
+ params.token,
13988
+ params.amount,
13989
+ params.toAddress,
13990
+ this.configService.sharedConfig[types.STELLAR_MAINNET_CHAIN_ID].horizonRpcUrl
13991
+ );
13992
+ let srcHasTrustline = true;
13993
+ if (isStellarSpokeProviderType(spokeProvider)) {
13994
+ srcHasTrustline = await StellarSpokeService.hasSufficientTrustline(
13995
+ params.token,
13996
+ params.amount,
13997
+ spokeProvider
13998
+ );
13999
+ }
14000
+ return {
14001
+ ok: true,
14002
+ value: targetHasTrustline && srcHasTrustline
14003
+ };
14004
+ }
14005
+ if (isStellarSpokeProviderType(spokeProvider)) {
13923
14006
  return {
13924
14007
  ok: true,
13925
14008
  value: await StellarSpokeService.hasSufficientTrustline(params.token, params.amount, spokeProvider)
@@ -13953,6 +14036,9 @@ var MoneyMarketService = class _MoneyMarketService {
13953
14036
  * Approve amount spending if isAllowanceValid returns false.
13954
14037
  * For evm spoke chains, the spender is the asset manager contract while
13955
14038
  * for sonic spoke (hub) chain, the spender is the user router contract.
14039
+ * NOTE: Stellar requires trustline when being either sender or receiver. Thus
14040
+ * you should make sure that both src and destination wallets have sufficient trustlines.
14041
+ * Make sure to invoke this method using wallet address which is about to receive the tokens!
13956
14042
  * @param token - ERC20 token address
13957
14043
  * @param amount - Amount to approve
13958
14044
  * @param spender - Spender address
@@ -13994,10 +14080,6 @@ var MoneyMarketService = class _MoneyMarketService {
13994
14080
  }
13995
14081
  const walletAddress = await spokeProvider.walletProvider.getWalletAddress();
13996
14082
  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
14083
  const result = await StellarSpokeService.requestTrustline(params.token, params.amount, spokeProvider, raw);
14002
14084
  return {
14003
14085
  ok: true,
@@ -15042,7 +15124,8 @@ var Sodax = class {
15042
15124
  config: {
15043
15125
  backendApiUrl: config?.backendApiConfig?.baseURL,
15044
15126
  timeout: config?.backendApiConfig?.timeout
15045
- }
15127
+ },
15128
+ sharedConfig: config?.sharedConfig
15046
15129
  });
15047
15130
  this.hubProvider = new EvmHubProvider({ config: config?.hubProviderConfig, configService: this.config });
15048
15131
  this.swaps = config && config.swaps ? new SwapService({
@@ -16529,7 +16612,7 @@ var BridgeService = class {
16529
16612
  *
16530
16613
  * @param spokeProvider - The spoke provider instance
16531
16614
  * @param token - The token address to query the balance for
16532
- * @returns {Promise<bigint>} - The token balance as a bigint value
16615
+ * @returns {Promise<BridgeLimit>} - The max bridgeable amount with corresponding decimals
16533
16616
  */
16534
16617
  async getBridgeableAmount(from, to) {
16535
16618
  try {
@@ -16537,51 +16620,59 @@ var BridgeService = class {
16537
16620
  const toHubAsset = this.configService.getHubAssetInfo(to.xChainId, to.address);
16538
16621
  invariant6__default.default(fromHubAsset, `Hub asset not found for token ${from.address} on chain ${from.xChainId}`);
16539
16622
  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),
16623
+ invariant6__default.default(this.isBridgeable({ from, to }), `Tokens ${from.address} and ${to.address} are not bridgeable`);
16624
+ const [tokenInfos, reserves] = await Promise.all([
16625
+ EvmVaultTokenService.getTokenInfos(
16626
+ fromHubAsset.vault,
16627
+ [fromHubAsset.asset, toHubAsset.asset],
16628
+ this.hubProvider.publicClient
16629
+ ),
16542
16630
  EvmVaultTokenService.getVaultReserves(toHubAsset.vault, this.hubProvider.publicClient)
16543
16631
  ]);
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;
16632
+ invariant6__default.default(tokenInfos.length === 2, `Expected 2 token infos, got ${tokenInfos.length}`);
16633
+ const [fromTokenInfo, toTokenInfo] = tokenInfos;
16634
+ invariant6__default.default(fromTokenInfo, "From token info not found");
16635
+ invariant6__default.default(toTokenInfo, "To token info not found");
16636
+ if (!fromTokenInfo.isSupported) {
16552
16637
  return {
16553
16638
  ok: true,
16554
- value: availableDeposit2
16639
+ value: {
16640
+ amount: 0n,
16641
+ decimals: fromTokenInfo.decimals,
16642
+ type: "DEPOSIT_LIMIT"
16643
+ }
16555
16644
  };
16556
16645
  }
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;
16646
+ if (from.xChainId !== this.hubProvider.chainConfig.chain.id && to.xChainId === this.hubProvider.chainConfig.chain.id) {
16647
+ const fromTokenDepositedAmount2 = this.findTokenBalanceInReserves(reserves, from);
16648
+ const availableDeposit2 = fromTokenInfo.maxDeposit - fromTokenDepositedAmount2;
16564
16649
  return {
16565
16650
  ok: true,
16566
- value: assetManagerBalance2
16651
+ value: {
16652
+ amount: availableDeposit2,
16653
+ decimals: fromTokenInfo.decimals,
16654
+ type: "DEPOSIT_LIMIT"
16655
+ }
16567
16656
  };
16568
16657
  }
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;
16658
+ if (from.xChainId === this.hubProvider.chainConfig.chain.id && to.xChainId !== this.hubProvider.chainConfig.chain.id) {
16659
+ return {
16660
+ ok: true,
16661
+ value: {
16662
+ amount: this.findTokenBalanceInReserves(reserves, to),
16663
+ decimals: toTokenInfo.decimals,
16664
+ type: "WITHDRAWAL_LIMIT"
16665
+ }
16666
+ };
16667
+ }
16668
+ const fromTokenDepositedAmount = this.findTokenBalanceInReserves(reserves, from);
16669
+ const availableDeposit = fromTokenInfo.maxDeposit - fromTokenDepositedAmount;
16670
+ const assetManagerBalance = this.findTokenBalanceInReserves(reserves, to);
16671
+ const availableDepositNormalised = BigNumber4__default.default(availableDeposit).shiftedBy(-fromTokenInfo.decimals);
16672
+ const assetManagerBalanceNormalised = BigNumber4__default.default(assetManagerBalance).shiftedBy(-toTokenInfo.decimals);
16582
16673
  return {
16583
16674
  ok: true,
16584
- value: availableDeposit < assetManagerBalance ? availableDeposit : assetManagerBalance
16675
+ value: availableDepositNormalised.isLessThan(assetManagerBalanceNormalised) ? { amount: availableDeposit, decimals: fromTokenInfo.decimals, type: "DEPOSIT_LIMIT" } : { amount: assetManagerBalance, decimals: toTokenInfo.decimals, type: "WITHDRAWAL_LIMIT" }
16585
16676
  };
16586
16677
  } catch (error) {
16587
16678
  console.error(error);
@@ -16658,6 +16749,13 @@ var BridgeService = class {
16658
16749
  }
16659
16750
  return bridgeableTokens;
16660
16751
  }
16752
+ findTokenBalanceInReserves(reserves, token) {
16753
+ const hubAsset = this.configService.getHubAssetInfo(token.xChainId, token.address);
16754
+ invariant6__default.default(hubAsset, `Hub asset not found for token ${token.address} on chain ${token.xChainId}`);
16755
+ const tokenIndex = reserves.tokens.findIndex((t) => t.toLowerCase() === hubAsset.asset.toLowerCase());
16756
+ invariant6__default.default(tokenIndex !== -1, `Token ${hubAsset.asset} not found in the vault reserves for chain ${token.xChainId}`);
16757
+ return reserves.balances[tokenIndex] ?? 0n;
16758
+ }
16661
16759
  };
16662
16760
  var StakingLogic = class _StakingLogic {
16663
16761
  constructor() {