Bridging Assets via Calldata Execution

After getting a quote, you'll next have to send a transaction to Swing's Cross-Chain API.

This step involves using our wallet connector (xDefi) to send a transaction to the Bitcoin network or, to invoke a smart contract function on any EVM network which is necessary for approvals and swap transactions.

The steps for sending a transaction are as followed:

Making a Request

Navigating to our src/services/requests.ts, you'll find our request implemenation for the /send endpoint:

export const sendTransactionRequest = async (
  payload: SendTransactionPayload,
): Promise<SendTransactionApiResponse> => {
  try {
    const response = await axios.post<SendTransactionApiResponse>(
      `${baseUrl}/transfer/send`,
      { ...payload, projectId },
      {
        headers: {
          'Content-Type': 'application/json',
        },
      },
    );
    return response.data;
  } catch (error) {
    console.error('Error sending transaction:', error);
    throw error;
  }
};

The SendTransactionPayload body payload contains the source chain, destination chain, tokenAmount, and the desired route.

URL: https://swap.prod.swing.xyz/v0/transfer/send

Parameters:

KeyExampleDescription
fromChainbitcoinThe blockchain where the transaction originates.
fromTokenAddressbtcSource Token Address
fromUserAddressbc1qeegt8mserjpwmaylfmprfswcx6twa4psusas8xSender's wallet address
tokenSymbolBTCSource Token slug
toTokenAddress0x0000000000000000000000000000000000000000Destination Token Address.
toChainethereumDestination Source slug
toTokenAmount4376081Amount of the destination token being received.
toTokenSymbolETHDestination Chain slug
toUserAddress0x018c15DA1239B84b08283799B89045CD476BBbBbReceiver's wallet address
tokenAmount1000000000000000000Amount of the source token being sent (in wei for ETH).
typeswapType of transaction.
projectIdreplugYour project's ID
routeSee Getting BTC Routes pageSelected Route from /quote endpoint

Since executing a swap/bridge will change the state of a user's wallet, the next step of this transaction must be done via a Smart Contract Transaction on Ethereum or a send transaction on Bitcoin and not via Swing's Cross-Chain API. The response received from the sendTransactionRequest endpoint provides us with the necessary txData/callData needed to be passed on to a user's wallet to sign the transaction.

The txData from the sendTransactionRequest will look something like this:

{
   ....
    "tx": {
        "from": "bc1qeegt8mserjpwmaylfmprfswcx6twa4psusas8x",
        "to": "0x",
        "value": "0x",
        "data": "70736274ff0100d80200000002b05b268c9f39123623c19077fd8be9ea3eb4be2cc9bf2b9679a03eaef853e4de0200000000ffffffff5f1d0367c097ea1af3dfae2eeba4c66887b4b4fea3a4853e7fdb73d3dab59d280000000000ffffffff0330750000000000001600145c8e21c7446ac47b8537eeee77116d1bb33d82350000000000000000356a333d3a653a3078653165303939326265393930326539323436306163306666363235646363316334383566636636623a3a743a30f617000000000000160014ce50b3ee191c82edf49f4ec234c1d83696eed430000000000001011f3910000000000000160014ce50b3ee191c82edf49f4ec234c1d83696eed4300001011f499d000000000000160014ce50b3ee191c82edf49f4ec234c1d83696eed43000000000",
        "meta": {
            "from": "bc1qeegt8mserjpwmaylfmprfswcx6twa4psusas8x",
            "recipient": "bc1qtj8zr36ydtz8hpfhamh8wytdrwenmq34e0l6zc",
            "amount": {
                "amount": "30000",
                "decimals": 8
            },
            "memo": "=:e:0xe1e0992be9902e92460ac0ff625dcc1c485fcf6b::t:0",
            "slippage": 1146
        }
    }
   ....
}

Connecting to xDefi wallet

To connect to xDefi, we'll be using the xdefiWallet() and useConnect() hooks from Thirdweb's SDK. To interact with any EVM chain, we need a wallet signer which we also included in Thirdweb's SDK:

// src/components/Swaps.tsx
 
import { xdefiWallet, useConnect, useSigner } from '@thirdweb-dev/react';
 
const xDefiConfig = xdefiWallet(); //Wallet provider config.
const connect = useConnect(); //For connecting to a user's supported wallet.
const signer = useSigner(); //For interacting with EVM chains

Next, in our connect wallet function, we will connect to our wallet using the xDefiConfig:

// src/components/Swaps.tsx
 
async function connectWallet(chainId?: number) {
  try {
    // Connect to xDefi
    await connect(xDefiConfig, { chainId });
  } catch (error) {
    console.error('Connect Wallet Error:', error);
  }
}

Bridging Assets from BTC to EVM

To perform a bridge from a Non-EVM chain like Bitcoin, you'll need to sign the transaction using a wallet provider that supports Bitcoin.

To brige our Bitcoin assets to any EVM chain, we have to set our source chain to bitcoin and our destination chain to any EVM chain like ethereum

Source Chain Config

const transferParams: TransferParams = {
  ...
  fromChain: 'bitcoin',
  fromUserAddress: 'bc1qeegt8mserjpwmaylfmprfswcx6twa4psusas8x',
  fromTokenAddress: 'btc',
  tokenSymbol: 'BTC',
  fromChainDecimal: 8,
  ...
};

Destination Chain Config

const transferParams: TransferParams = {
  ...
  toTokenAddress: '0x0000000000000000000000000000000000000000',
  toTokenSymbol: 'ETH',
  toChain: 'ethereum',
  toUserAddress: '0xE1e0992Be9902E92460AC0Ff625Dcc1c485FCF6b',
  toChainDecimal: 8,
  ...
};

putting our transfer config together and making a sendTransactionRequest:

const transferParams: TransferParams = {
  tokenAmount: '1000000',
 
  fromChain: 'bitcoin',
  fromUserAddress: 'bc1qeegt8mserjpwmaylfmprfswcx6twa4psusas8x',
  fromTokenAddress: 'btc',
  fromChainDecimal: 8,
  tokenSymbol: 'BTC',
 
  toTokenAddress: '0x0000000000000000000000000000000000000000',
  toTokenSymbol: 'ETH',
  toChain: 'ethereum',
  toUserAddress: '0xE1e0992Be9902E92460AC0Ff625Dcc1c485FCF6b', // enter your ethereum wallet here
  toChainDecimal: 18,
};
 
const transfer = await sendTransactionRequest({
  fromChain: transferParams.fromChain,
  fromTokenAddress: transferParams.fromTokenAddress,
  fromUserAddress: transferParams.fromUserAddress,
  tokenSymbol: transferParams.tokenSymbol,
 
  toTokenAddress: transferParams.toTokenAddress!,
  toChain: transferParams.toChain,
  toTokenAmount: transferRoute.quote.amount,
  toTokenSymbol: transferParams.toTokenSymbol!,
  toUserAddress: transferParams.toUserAddress!,
 
  tokenAmount: convertEthToWei(
    transferParams.tokenAmount,
    transferParams.fromChainDecimal,
  ),
  route: transferRoute.route, // response from the `/qoute` endpoint
  type: 'swap',
});

Here's a sample response from this request:

{
  "id": 258996,
  "fromToken": {
    "address": "btc",
    "symbol": "BTC",
    "name": "BTC",
    "decimals": 8,
    "logoURI": "https://raw.githubusercontent.com/polkaswitch/assets/master/blockchains/zksync-era/assets/0xBBeB516fb02a01611cBBE0453Fe3c580D7281011/logo.png"
  },
  "toToken": {
    "address": "0x0000000000000000000000000000000000000000",
    "symbol": "ETH",
    "name": "ETH",
    "decimals": 18,
    "logoURI": "https://raw.githubusercontent.com/polkaswitch/assets/master/blockchains/solana/assets/7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs/eth.jpg"
  },
  "fromChain": {
    "chainId": 0,
    "name": "Bitcoin",
    "slug": "bitcoin",
    "protocolType": "bitcoin"
  },
  "toChain": {
    "chainId": 1,
    "name": "Ethereum",
    "slug": "ethereum",
    "protocolType": "evm"
  },
  "route": [
    {
      "bridge": "thorswap",
      "bridgeTokenAddress": "btc",
      "steps": ["allowance", "approve", "send"],
      "name": "BTC",
      "part": 100
    }
  ],
  "tx": {
    "from": "bc1qeegt8mserjpwmaylfmprfswcx6twa4psusas8x",
    "to": "0x",
    "value": "0x",
    "data": "70736274ff0100d80200000002b05b268c9f39123623c19077fd8be9ea3eb4be2cc9bf2b9679a03eaef853e4de0200000000ffffffff5f1d0367c097ea1af3dfae2eeba4c66887b4b4fea3a4853e7fdb73d3dab59d280000000000ffffffff0330750000000000001600145c8e21c7446ac47b8537eeee77116d1bb33d82350000000000000000356a333d3a653a3078653165303939326265393930326539323436306163306666363235646363316334383566636636623a3a743a30f617000000000000160014ce50b3ee191c82edf49f4ec234c1d83696eed430000000000001011f3910000000000000160014ce50b3ee191c82edf49f4ec234c1d83696eed4300001011f499d000000000000160014ce50b3ee191c82edf49f4ec234c1d83696eed43000000000",
    "meta": {
      "from": "bc1qeegt8mserjpwmaylfmprfswcx6twa4psusas8x",
      "recipient": "bc1qtj8zr36ydtz8hpfhamh8wytdrwenmq34e0l6zc",
      "amount": {
        "amount": "30000",
        "decimals": 8
      },
      "memo": "=:e:0xe1e0992be9902e92460ac0ff625dcc1c485fcf6b::t:0",
      "slippage": 1146
    }
  }
}

The definition for the sendTransactionRequest response can be found in src/interfaces/send.interface.ts.

export interface SendTransactionApiResponse {
  id: number;
  fromToken: Token;
  toToken: Token;
  fromChain: Chain;
  toChain: Chain;
  route: Route[];
  tx: TransactionDetails;
}

Since we're only interested in the tx object which contains our callData in the response, we'll extract it:

// src/components/Swaps.tsx
 
const { from, recipient, amount, memo } = transfer.tx.meta;

Using our wallet provider, we will send a transaction to the user's wallet using xDefi's Injected Browser SDK which should be availabe to you if you have xDefi installed in your browser:

(window.xfi as any)?.bitcoin.request(
  {
    method: 'transfer',
    params: [
      {
        from,
        recipient,
        amount,
        memo,
      },
    ],
  },
  (error: any, result: any) => {
    console.log(error, result);
 
    if (error) {
      toast({
        variant: 'destructive',
        title: 'Something went wrong!',
        description: 'Swap error, please check your balance or swap config',
      });
 
      setIsLoading(false);
      setTransStatus(null);
    }
    const txHash = result;
    pollTransactionStatus(transfer.id.toString(), txHash);
    console.log(txHash);
  },
);

Executing this request, will bring up an xDefi prompt in your browser:

btc project swap prompt

Bridging Assets from EVM to BTC

To Bridge assets from any EVM chain to the Bitcoin network, we essentially only have to reverse our transactionParams and perform a sendTransactionRequest:

const transferParams: TransferParams = {
  tokenAmount: '1',
 
  fromChain: 'ethereum',
  fromUserAddress: '',
  fromTokenAddress: '0x0000000000000000000000000000000000000000',
  fromChainDecimal: 18,
  tokenSymbol: 'ETH',
 
  toTokenAddress: 'btc',
  toTokenSymbol: 'BTC',
  toChain: 'bitcoin',
  toUserAddress: 'bc1qeegt8mserjpwmaylfmprfswcx6twa4psusas8x', // enter your bitcoin wallet here
  toChainDecimal: 8,
};
 
const transfer = await sendTransactionRequest({
  fromChain: transferParams.fromChain,
  fromTokenAddress: transferParams.fromTokenAddress,
  fromUserAddress: transferParams.fromUserAddress,
  tokenSymbol: transferParams.tokenSymbol,
 
  toTokenAddress: transferParams.toTokenAddress!,
  toChain: transferParams.toChain,
  toTokenAmount: transferRoute.quote.amount,
  toTokenSymbol: transferParams.toTokenSymbol!,
  toUserAddress: transferParams.toUserAddress!,
 
  tokenAmount: convertEthToWei(
    transferParams.tokenAmount,
    transferParams.fromChainDecimal,
  ),
  route: transferRoute.route, // response from the `/qoute` endpoint
  type: 'swap',
});

The tx object in our response from our sendTransactionRequest for an EVM to BTC bridge will look something like this:

{
   ....
    "tx": {
        "from": "0x018c15DA1239B84b08283799B89045CD476BBbBb",
        "to": "0x39E3e49C99834C9573c9FC7Ff5A4B226cD7B0E63",
        "data": "0x301a3720000000000000000000000000eeeeeeeeeeee........",
        "value": "0x0e35fa931a0000",
        "gas": "0x06a02f"
    }
   ....
}

Next, we will use our signer object to execute our callData transaction via xDefi or any Ethereum supported wallet:

First, let's extract the txData:

// src/components/Swaps.tsx
 
let txData: any = {
  data: transfer.tx.data,
  from: transfer.tx.from,
  to: transfer.tx.to,
  value: transfer.tx.value,
  gasLimit: transfer.tx.gas,
};

Using our signer, we will send a transaction to the user's wallet using the sendTransaction function from our signer:

// src/components/Swaps.tsx
 
const txResponse = await signer?.sendTransaction(txData); // <- `txResponse` contains the `txHash` of our transaction. You will need this later for getting a transaction's status.
 
const receipt = await txResponse?.wait();
 
console.log('Transaction receipt:', receipt);