How to Bridge & Swap Tokens

The full API reference for our /quote, /approve, /allowance and /send endpoints can be found in the API reference section.

In this section, we will discuss the process of moving tokens and exchanging them across one or multiple blockchain networks. Accomplishing a successful transfer necessitates making calls to our endpoints in order to move the transaction through its various stages.

Transfer Stages

The stages can be summarized as follows (and depicted in the diagram below):

  • Step 1. Retrieving a valid quote from a Swing-supported bridge
  • Step 2. (Optional) Getting approval for native token transfers/swaps
  • Step 3. Intiating transaction given the prior quote on a specific bridge
  • Step 4. Waiting for transaction to complete by polling for transaction status
  • Step 5. (Optional depending on bridge) Finalize transaction by claiming funds
  • Step 6. Transaction Complete!
Swing API Transfer Flow

Step 1: Get a quote

Retrieving a valid quote from a Swing supported bridge

The full API reference for our /quote endpoint can be found in the API reference section.

This is the first prerequisite step for any transaction on the Swing API. We want to determine which bridge provides us the best quote based on our needs (whether that’s price, speed or low fees).

Our quoting system requires all the following required information:

  1. Source chain
  2. Destination chain
  3. Source Token on the Source chain
  4. Destination Token on the Destination chain
  5. The amount you want to send
  6. Source Wallet Address to pull funds from

We can query our /quote endpoint in the following manner given all the necessary information:

Make sure to send your projectId to get quotes with project-specific configurations. Create you Swing Platform project to configure your integration, access transaction data and more.

const getQuote = async () => {
  const result = await axios.get(
    'https://swap.prod.swing.xyz/v0/transfer/quote',
    {
      //source chain parameters
      fromChain: 'ethereum',
      tokenSymbol: 'ETH',
      fromTokenAddress: '0x0000000000000000000000000000000000000000',
 
      //destination chain parameters
      toChain: 'arbitrum',
      toTokenSymbol: 'ETH',
      toTokenAddress: '0x0000000000000000000000000000000000000000',
 
      //transfer parameters
      tokenAmount: '1000000000000000000',
      projectId: 'replug', // create your project here: https://platform.swing.xyz/
    },
  );
  return result.data;
};

The results for our quote will look like this. The “routes” property will have all the possible bridges that can support the requested transfer. Each route also includes the fees associated with that route (learn more about fees). You can choose one based on either low fees, best price or speed.

{
  "fromChain": {
    "chainId": 1,
    "slug": "ethereum",
    "name": "Ethereum"
  },
  "fromToken": {
    "symbol": "USDC",
    "name": "USDC",
    "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
    "decimals": 6
  },
  "toChain": {
    "chainId": 137,
    "slug": "polygon",
    "name": "Polygon"
  },
  "toToken": {
    "symbol": "USDC",
    "name": "USDC",
    "decimals": 6
  },
  "routes": [
    {
      "distribution": {
        "hop": 1
      },
      "duration": 10,
      "quote": {
        "amount": "99991945",
        "amountUSD": "99.992",
        "decimals": 6
      },
      "fees": [
        {
          "type": "bridge",
          "amount": "60000",
          "amountUSD": "0.060",
          "chainSlug": "polygon",
          "tokenSymbol": "USDC",
          "tokenAddress": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
          "decimals": 6,
          "deductedFromSourceToken": "true"
        },
        {
          "type": "bridge",
          "amount": 38389893006871,
          "amountUSD": 0.072,
          "chainSlug": "ethereum",
          "tokenSymbol": "ETH",
          "tokenAddress": "0x0000000000000000000000000000000000000000",
          "decimals": 18,
          "deductedFromSourceToken": "false"
        },
        {
          "type": "gas",
          "amount": "11278145831044354",
          "amountUSD": "21.124",
          "chainSlug": "ethereum",
          "tokenSymbo": "ETH",
          "tokenAddress": "0x0000000000000000000000000000000000000000",
          "decimals": 18,
          "deductedFromSourceToken": "false"
        }
      ],
      "route": [
        {
          "bridge": "hop",
          "bridgeTokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
          "steps": ["allowance", "approve", "send"],
          "name": "USDC",
          "part": 100
        }
      ]
    },
    {
      "distribution": {
        "celer": 1
      },
      "duration": 10,
      "quote": {
        "amount": "99991945",
        "amountUSD": "99.992",
        "decimals": 6
      },
      "fees": [
        {
          "type": "bridge",
          "amount": "60000",
          "amountUSD": "0.060",
          "chainSlug": "polygon",
          "tokenSymbol": "USDC",
          "tokenAddress": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
          "decimals": 6,
          "deductedFromSourceToken": "true"
        },
        {
          "type": "bridge",
          "amount": 38389893006871,
          "amountUSD": 0.072,
          "chainSlug": "ethereum",
          "tokenSymbol": "ETH",
          "tokenAddress": "0x0000000000000000000000000000000000000000",
          "decimals": 18,
          "deductedFromSourceToken": "false"
        },
        {
          "type": "gas",
          "amount": "11278145831044354",
          "amountUSD": "21.124",
          "chainSlug": "ethereum",
          "tokenSymbo": "ETH",
          "tokenAddress": "0x0000000000000000000000000000000000000000",
          "decimals": 18,
          "deductedFromSourceToken": "false"
        }
      ],
      "route": [
        {
          "bridge": "celer",
          "bridgeTokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
          "steps": ["allowance", "approve", "send"],
          "name": "USDC",
          "part": 100
        }
      ]
    }
  ]
}

Step 2: Get token approval

Getting approval for non-native token transfers & swaps

The full API reference for our /approve endpoint can be found in the API reference section.

This section is only applicable to transfers of non-native tokens. Non-native tokens are governance tokens, wrapped tokens, stablecoins, and oracle tokens.

If you are performing a transfer with a non-native token, our Swing Contract requires the right allowance limit to spend/withdraw funds (denominated in non-native tokens) from the designated wallet address. Therefore, we need to invoke our /approve endpoint to set with the right allowance limit that is greater than the amount you want to transfer. This is a well-known standard step.

In practice, depending on the non-native token amount you are looking to transfer, you want to call our /approve endpoint with a limit greater than or equal to the amount you are transferring.

You can use the /allowance endpoint to determine if you have already approved a sufficient limit to allow your non-native token transfer. Importantly, the allowance limit is specific to the token, chain and bridge. So you must approve an allowance limit every time for every unique transfer combination of token, chain or bridge.

Allowance Endpoint

We can query our /allowance endpoint in the following manner to get the allowance limit:

const getAllowance = async () => {
  const result = await axios.get(
    'https://swap.prod.swing.xyz/v0/transfer/allowance',
    {
      fromChain: 'ethereum',
      tokenSymbol: 'ETH',
      tokenAddress: '0x0000000000000000000000000000000000000000',
      bridge: 'openocean',
      fromAddress: '0x018c15DA1239B84b08283799B89045CD476BBbBb',
      toChain: 'polygon',
      toTokenSymbol: 'USDC',
      toTokenAddress: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359',
      contractCall: true,
    },
  );
  return result.data.allowance;
};

This will always only return the allowance limit for that specific token, on the specific chain for that specific bridge:

{
  "allowance": "1000000"
}

Approve Endpoint

If you need to approve a higher allowance limit, you can query our /approve endpoint in the following manner:

const getApprovalCallData = async () => {
  const result = await axios.get(
    'https://swap.prod.swing.xyz/v0/transfer/approve',
    {
      fromChain: 'ethereum',
      tokenSymbol: 'ETH',
      tokenAddress: '0x0000000000000000000000000000000000000000',
      bridge: 'openocean',
      fromAddress: '0x018c15DA1239B84b08283799B89045CD476BBbBb',
      toChain: 'polygon',
      toTokenSymbol: 'USDC',
      toTokenAddress: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359',
      tokenAmount: 100000000,
      contractCall: true,
    },
  );
  return result.data;
};

Since approve is a contract-level function, Swing only returns the necessary call-data that will need to be signed and executed by the local wallet (ie. Metamask, Coinbase Wallet, WalletConnect, etc).

{
  "data": "0x095ea7b3000000000000000000000000114bac0bed2...",
  "to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
  "fromChain": {
    "chainId": 1,
    "slug": "ethereum",
    "name": "Ethereum"
  }
}

Step 3: Initiate transfer transaction

Initiate transaction given the prior quote on a specific bridge

The full API reference for our /send endpoint can be found in the API reference section.

Similar to our quoting system, to initiate a transaction it requires all the following required information:

  1. Bridge
  2. Source chain
  3. Destination chain
  4. Source Token on the Source chain
  5. Destination Token on the Destination chain
  6. The amount you want to send
  7. Source Wallet Address to pull funds from

We can query our /send endpoint in the following manner given all the necessary information:

Make sure to send your projectId to start tracking your transactions. Create you Swing Platform project to configure your integration, access transaction data and more.

const sendTransaction = async () => {
  const result = await axios.post(
    'https://swap.prod.swing.xyz/v0/transfer/send',
    {
      //source chain parameters
      fromChain: 'ethereum',
      tokenSymbol: 'ETH',
      fromTokenAddress: '0x0000000000000000000000000000000000000000',
 
      //destination chain parameters
      toChain: 'polygon',
      toTokenSymbol: 'USDC',
      toTokenAddress: '0xcbe56b00d173a26a5978ce90db2e33622fd95a28',
 
      //transfer parameters
      fromUserAddress: '0x018c15DA1239B84b08283799B89045CD476BBbBb',
      toUserAddress: '0x018c15DA1239B84b08283799B89045CD476BBbBb',
      tokenAmount: '1000000000',
      projectId: 'replug', // create your project here: https://platform.swing.xyz/
      integration: 'openocean',
      type: 'swap',
      route: [
        {
          bridge: 'openocean',
          bridgeTokenAddress: '0x0000000000000000000000000000000000000000',
          steps: ['allowance', 'approve', 'send'],
          name: 'ETH',
          part: 100,
        },
      ],
    },
  );
  return result.data;
};

Since initiating a transaction requires invoking a contract-level function on your supplied wallet address, Swing only returns the necessary call-data that will need to be signed and executed by the local wallet (i.e Metamask, WalletConnect, Coinbase Wallet). The /send endpoint returns the following example call-data:

{
  "id": 233742,
  "fromToken": {
    "address": "0x0000000000000000000000000000000000001010",
    "symbol": "MATIC",
    "name": "MATIC",
    "decimals": 18,
    "logoURI": "https://s3.openocean.finance/token_logos/logos/1637561049975_1903381661429342.png"
  },
  "toToken": {
    "address": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
    "symbol": "USDC",
    "name": "USDC",
    "decimals": 6,
    "logoURI": "https://raw.githubusercontent.com/multiversx/mx-assets/master/tokens/USDC-c76f1f/logo.png"
  },
  "fromChain": {
    "chainId": 137,
    "name": "Ethereum",
    "slug": "ethereum",
    "protocolType": "evm"
  },
  "toChain": {
    "chainId": 137,
    "name": "Polygon",
    "slug": "polygon",
    "protocolType": "evm"
  },
  "route": [
    {
      "bridge": "openocean",
      "bridgeTokenAddress": "0x0000000000000000000000000000000000001010",
      "steps": ["allowance", "approve", "send"],
      "name": "MATIC",
      "part": 100
    }
  ],
  "tx": {
    "from": "0x018c15DA1239B84b08283799B89045CD476BBbBb",
    "to": "0x6352a56caadC4F1E25CD6c75970Fa768A3304e64",
    "data": "0x90411a320000000000000000000000001e82ad8a12068a85...",
    "value": "0x06f05b59d3b20000",
    "gas": "0x04647b"
  }
}

Step 4: Watch transaction status

Waiting for transaction to complete by polling for transaction status

The full API reference for our /status endpoint can be found in the API reference section.

Once you have a transaction submitted and pending, you will need to monitor the status of the transaction as you wait. This involves polling our /status endpoint to actively watch the status updates. For more information on fetching transaction status, refer to our guide on "How to Check Status of a Transfer".

Congrats! Your transaction is now complete 🥳

Next, lets learn how to request all supported chains and known tokens. Navigate to the next section 👈