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:
- First, we will make a request to
https://swap.prod.swing.xyz/v0/transfer/send
- Using the
txData
/callData
returned from the/send
request, sign the transaction with using a user's wallet
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:
Key | Example | Description |
---|---|---|
fromChain | bitcoin | The blockchain where the transaction originates. |
fromTokenAddress | btc | Source Token Address |
fromUserAddress | bc1qeegt8mserjpwmaylfmprfswcx6twa4psusas8x | Sender's wallet address |
tokenSymbol | BTC | Source Token slug |
toTokenAddress | 0x0000000000000000000000000000000000000000 | Destination Token Address. |
toChain | ethereum | Destination Source slug |
toTokenAmount | 4376081 | Amount of the destination token being received. |
toTokenSymbol | ETH | Destination Chain slug |
toUserAddress | 0x018c15DA1239B84b08283799B89045CD476BBbBb | Receiver's wallet address |
tokenAmount | 1000000000000000000 | Amount of the source token being sent (in wei for ETH). |
type | swap | Type of transaction. |
projectId | replug | Your project's ID |
route | See Getting BTC Routes page | Selected 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:

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);