πŸ”„Swaps (Solver)

Solver part of the SDK provides abstractions to assist you with interacting with the cross-chain Intent Smart Contracts, Solver and Relay API.

Using SDK Config and Constants

SDK includes predefined configurations of supported chains, tokens and other relevant information for the client to consume.

import { supportedSpokeChains, getSupportedSolverTokens, SpokeChainId, Token } from "@sodax/sdk"

// all supported spoke chains
export const spokeChains: SpokeChainId[] = supportedSpokeChains;

// using spoke chain id to retrieve supported tokens for solver (intent swaps)
const supportedSolverTokens: readonly Token[] = getSupportedSolverTokens(spokeChainId);

// check if token address for given spoke chain id is supported in solver
const isSolverSupportedToken: boolean = isSolverSupportedToken(spokeChainId, token)

Please refer to SDK constants.ts for more.

Initialising Spoke Provider

Refer to Initialising Spoke Provider section to see how BSC spoke provider used as bscSpokeProvider can be created.

Request a Quote

Requesting a quote should require you to just consume user input amount and converting it to the appropriate token amount (scaled by token decimals). All the required configurations (chain id [nid], token decimals and address) should be loaded as described in Load SDK Config.

Quoting API supports different types of quotes:

  • "exact_input": "amount" parameter is the amount the user want's to swap (e.g. the user is asking for a quote to swap 1 WETH to xxx SUI)

  • "exact_output": "amount" parameter is the final amount the user wants. (e.g. the user want's to swap WETH for SUI, but is asking how many WETH is going to cost to have 1 SUI)

import {
  Sodax,
  getHubChainConfig,
  BSC_MAINNET_CHAIN_ID,
  ARBITRUM_MAINNET_CHAIN_ID,
  IntentQuoteRequest,
  Result,
  IntentQuoteResponse,
  IntentErrorResponse
} from "@sodax/sdk";

const bscEthToken = '0x2170Ed0880ac9A755fd29B2688956BD959F933F8';  // Address of the ETH token on BSC (spoke chain)
const arbWbtcToken = '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f'; // Address of the wBTC token on ARB (spoke chain)

  const quoteRequest = {
    token_src: bscEthToken,
    token_dst: arbWbtcToken,
    token_src_blockchain_id: BSC_MAINNET_CHAIN_ID,
    token_dst_blockchain_id: ARBITRUM_MAINNET_CHAIN_ID,
    amount: 1000n,
    quote_type: 'exact_input',
  } satisfies IntentQuoteRequest;

  const result = await sodax.solver.getQuote(quoteRequest);

  if (result.ok) {
    // success
  } else {
    // handle error
  }

Token Approval Flow

Before creating an intent, you need to ensure that the Asset Manager contract has permission to spend your tokens. Here's how to handle the approval flow:

import {
  SolverService,
  BSC_MAINNET_CHAIN_ID,
  ARBITRUM_MAINNET_CHAIN_ID
} from "@sodax/sdk"

const bscEthToken = '0x2170Ed0880ac9A755fd29B2688956BD959F933F8';  // Address of the ETH token on BSC (spoke chain)
const arbWbtcToken = '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f'; // Address of the wBTC token on ARB (spoke chain)
const evmWalletAddressBytes = evmWalletProvider.getWalletAddressBytes();

// First check if approval is needed
const isApproved = await sodax.solver.isAllowanceValid(
  {
    inputToken: bscEthToken,  // The address of the input token on spoke chain
    outputToken: arbWbtcToken,  // The address of the output token on spoke chain
    inputAmount: BigInt(1000000), // The amount of input tokens
    minOutputAmount: BigInt(900000), // min amount you are expecting to receive
    deadline: BigInt(0), // Optional timestamp after which intent expires (0 = no deadline)
    allowPartialFill: false, // Whether the intent can be partially filled
    srcChain: BSC_MAINNET_CHAIN_ID, // Chain ID where input tokens originate
    dstChain: ARBITRUM_MAINNET_CHAIN_ID, // Chain ID where output tokens should be delivered
    srcAddress: evmWalletAddressBytes, // Source address in bytes (original address on spoke chain)
    dstAddress: evmWalletAddressBytes, // Destination address in bytes (original address on spoke chain)
    solver: '0x0000000000000000000000000000000000000000', // Optional specific solver address (address(0) = any solver)
    data: '0x', // Additional arbitrary data
  },
  bscSpokeProvider
);

if (!isApproved.ok) {
  // Handle error
  console.error('Failed to check allowance:', isApproved.error);
} else if (!isApproved.value) {
  // Approval needed - get the Asset Manager address from the chain config
  const assetManagerAddress = bscSpokeProvider.chainConfig.addresses.assetManager;
  
  // Approve the Asset Manager to spend your tokens
  const approveResult = await sodax.solver.approve(
    bscEthToken,
    BigInt(1000000), // Amount to approve
    assetManagerAddress,
    bscSpokeProvider
  );

  if (!approveResult.ok) {
    // Handle error
    console.error('Failed to approve tokens:', approveResult.error);
  } else {
    // Wait for approval transaction to be mined
    await approveResult.value.wait();
  }
}

// Now you can proceed with creating the intent
// ... continue with createIntent or createAndSubmitIntent ...

Create And Submit Intent Order

Creating Intent Order requires creating spoke provider for the chain that intent is going to be created on (token_src_blockchain_id).

Example for BSC -> ARB Intent Order:

  import {
    SolverService,
    SolverConfig,
    BSC_MAINNET_CHAIN_ID,
    ARBITRUM_MAINNET_CHAIN_ID
  } from "@sodax/sdk"

  const bscEthToken = '0x2170Ed0880ac9A755fd29B2688956BD959F933F8';  // Address of the ETH token on BSC (spoke chain)
  const arbWbtcToken = '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f'; // Address of the wBTC token on ARB (spoke chain)
  const evmWalletAddressBytes = evmWalletProvider.getWalletAddressBytes();

  const createIntentParams = {
    inputToken: bscEthToken,  // The address of the input token on spoke chain
    outputToken: arbWbtcToken,  // The address of the output token on spoke chain
    inputAmount: BigInt(1000000), // The amount of input tokens
    minOutputAmount: BigInt(900000), // min amount you are expecting to receive
    deadline: BigInt(0), // Optional timestamp after which intent expires (0 = no deadline)
    allowPartialFill: false, // Whether the intent can be partially filled
    srcChain: BSC_MAINNET_CHAIN_ID, // Chain ID where input tokens originate
    dstChain: ARBITRUM_MAINNET_CHAIN_ID, // Chain ID where output tokens should be delivered
    srcAddress: evmWalletAddressBytes, // Source address in bytes (original address on spoke chain)
    dstAddress: evmWalletAddressBytes, // Destination address in bytes (original address on spoke chain)
    solver: '0x0000000000000000000000000000000000000000', // Optional specific solver address (address(0) = any solver)
    data: '0x', // Additional arbitrary data
  } satisfies CreateIntentParams;

  // creates and submits on-chain transaction or returns raw transaction
  // NOTE: after intent is created on-chain it should also be posted to Solver API and submitted to Relay API
  // see below example of createAndSubmitIntent which does that for you
  const createIntentOnlyResult = await sodax.solver.createIntent(
    createIntentParams,
    bscSpokeProvider,
    partnerFeeAmount,
    true, // true = get raw transaction, false = execute and return tx hash
  );

  if (!createIntentResult.ok) {
    // handle error
  }

  // txHash and created Intent data as Intent & FeeAmount type
  const [rawTx, intent] = createIntentResult.value;

  // create on-chain intent, post to Solver API and Submit to Relay API
  // IMPORTANT: you should primarily use this one to create and submit intent
  const createAndSubmitIntentResult = await sodax.solver.createAndSubmitIntent(
    createIntentParams,
    bscSpokeProvider,
    partnerFeeAmount,
  );

    if (!createAndSubmitIntentResult.ok) {
    // handle error
  }

  // txHash and created Intent data as Intent & FeeAmount type
  const [txHash, intent] = createIntentResult.value;

Get Intent Order

Retrieve intent data using tx hash obtained from intent creation response.

const intent = await sodax.solver.getIntent(txHash, hubProvider);

Cancel Intent Order

Active Intent Order can be cancelled using Intent. See Get Intent Order on how to obtain intent.Note create intent functions also return intent data for convenience.


const result = await sodax.solver.cancelIntent(
  intent,
  bscSpokeProvider,
  hubProvider,
  false, // true = get raw transaction, false = execute and return tx hash
);

Get Intent Status

Retrieve status of intent.

const result = await sodax.solver.getStatus({
    intent_tx_hash: '0x...', // tx hash of create intent blockchain transaction
  } satisfies IntentStatusRequest);

Get Intent Hash

Get Intent Hash (keccak256) used as an ID of intent in smart contract.

const intentHash = sodax.solver.getIntentHash(intent);

Last updated