Getting BTC Routes to EVM
To perform a swap between the Bitcoin network and any EVM compactible chain, we first have to get a quote from Swing's Cross-Chain API. The response from the /quote
endpoint contains the route information and the fees that will be paid by an interacting user.
Making a Request
URL: https://swap.prod.swing.xyz/v0/transfer/quote
Query Parameters:
Property | Example | Description |
---|---|---|
tokenAmount | 1000000000000000000 | Amount of the source token being sent (in wei for ETH). |
fromChain | bitcoin | Source Chain slug |
fromUserAddress | bc1qeegt8mserjpwmaylfmprfswcx6twa4psusas8x | Sender's wallet address |
fromTokenAddress | btc | Source Token Address |
tokenSymbol | BTC | Source Token slug |
toTokenAddress | 0x0000000000000000000000000000000000000000 | Destination Token Address. |
toTokenSymbol | ETH | Destination Token slug |
toChain | ethereum | Destination Chain slug |
toUserAddress | 0x018c15DA1239B84b08283799B89045CD476BBbBb | Receiver's wallet address |
projectId | replug | Your project's ID |
Navigating to our src/services/requests.ts
file, you will find our method for getting a quote from Swing's Cross-Chain API called getQuoteRequest()
.
export const getQuoteRequest = async (
queryParams: QuoteQueryParams,
): Promise<QuoteAPIResponse> => {
try {
const response = await axios.get<QuoteAPIResponse>(
`${baseUrl}/transfer/quote`,
{ params: { ...queryParams, projectId } },
);
return response.data;
} catch (error) {
console.error('Error fetching quote:', error);
throw error;
}
};
Navigating to our src/components/Swap.tsx
file, you'll find our defaultTransferParams
object which will store the default transaction config for our example:
const defaultTransferParams: TransferParams = {
tokenAmount: '1000000',
fromChain: 'bitcoin',
fromUserAddress: 'bc1qeegt8mserjpwmaylfmprfswcx6twa4psusas8x',
fromTokenAddress: 'btc',
fromTokenIconUrl:
'https://raw.githubusercontent.com/Pymmdrza/Cryptocurrency_Logos/mainx/PNG/eth.png',
tokenSymbol: 'BTC',
toTokenAddress: '0x0000000000000000000000000000000000000000',
toTokenSymbol: 'ETH',
toChain: 'ethereum',
toTokenIconUrl:
'https://raw.githubusercontent.com/Pymmdrza/Cryptocurrency_Logos/mainx/PNG/btc.png',
toUserAddress: '0xE1e0992Be9902E92460AC0Ff625Dcc1c485FCF6b', // enter your ethereum wallet here
};
The response received from the getQuoteRequest
endpoint provides us with the fees
a user will have to pay when executing a transaction, as well as a list of possible routes
for the user to choose from as seen in our bitcoin example project.

The definition for the getQuoteRequest
response can be found in src/interfaces/quote.interface.ts.
export interface QuoteAPIResponse {
routes: Route[];
fromToken: Token;
fromChain: Chain;
toToken: Token;
toChain: Chain;
}
Each Route
contains a gasFee
, bridgeFee
and the amount of tokens the destination wallet will receive.
Here's an example response that contains the route data:
"routes": [
{
"duration": 10,
"gas": "439824241499248",
"quote": {
"integration": "thorswap",
"type": "swap",
"bridgeFee": "343736",
"bridgeFeeInNativeToken": "0",
"amount": "9360457",
"decimals": 8,
"amountUSD": "6303.051",
"bridgeFeeUSD": "231.462",
"bridgeFeeInNativeTokenUSD": "0",
"fees": [
{
"type": "bridge",
"amount": "343736",
"amountUSD": "231.462",
"chainSlug": "bitcoin",
"tokenSymbol": "BTC",
"tokenAddress": "btc",
"decimals": 8,
"deductedFromSourceToken": true
},
{
"type": "gas",
"amount": "439824241499248",
"amountUSD": "1.439",
"chainSlug": "ethereum",
"tokenSymbol": "ETH",
"tokenAddress": "0x0000000000000000000000000000000000000000",
"decimals": 18,
"deductedFromSourceToken": false
}
]
},
"route": [
{
"bridge": "thorswap",
"bridgeTokenAddress": "0x0000000000000000000000000000000000000000",
"steps": [
"allowance",
"approve",
"send"
],
"name": "ETH",
"part": 100
}
],
"distribution": {
"thorswap": 1
},
"gasUSD": "1.439"
}
],
Getting BTC to EVM Quote
To get a quote for a bridge transaction between Bitcoin and 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,
...
};
To calculate the tokenAmount
, which is the amount of assets being bridged, you need to convert the amount entered by a user to the smallest units for both Bitcoin and Ethereum. This requires storing the decimal places that represent these smallest units for both chains.
The decimal places for both our source and destination chains are stored in the fromChainDecimal
and toChainDecimal
properties respectively.
Here’s how you can covert the tokenAmout
:
For Ethereum:
- The smallest unit is
Wei
. - Decimal places: 18 (1 ETH = 10^18 Wei).
For Bitcoin:
- The smallest unit is
Satoshi
. - Decimal places: 8 (1 BTC = 10^8 Satoshis).
Navigating to our src/utils
folder, you'll find a file ethToWei
that contains a function to convert our tokenAmount to either Satoshis or Wei:
export const convertEthToWei = (ethAmount: string, decimals = 18) => {
// Convert the amount to a string to handle both string and number inputs
const weiAmount = ethers.utils.parseUnits(ethAmount.toString(), decimals);
return weiAmount.toString();
};
Selecting The Best Route
By default, the /quote
endpoint will return a list of possible routes and will sort them from the cheapest to the most expensive fees that a user will have to pay.
In our example project, we're safely assigning the first route to our transferRoute
state variable as its guaranteed to be the cheapest route.
const [transferRoute, setTransferRoute] = useState<Route | null>(null);
const transferParams: TransferParams = {
tokenAmount: '1',
// source chain config
fromChain: 'bitcoin',
fromUserAddress: 'bc1qeegt8mserjpwmaylfmprfswcx6twa4psusas8x',
fromTokenAddress: 'btc',
fromChainDecimal: 8,
tokenSymbol: 'BTC',
// destination chain config
toTokenAddress: '0x0000000000000000000000000000000000000000',
toTokenSymbol: 'ETH',
toChain: 'ethereum',
toUserAddress: '0xE1e0992Be9902E92460AC0Ff625Dcc1c485FCF6b',
toChainDecimal: 18,
};
async function getQuote() {
// Call `/quote` endpoint via `getQuoteRequest()`
const quotes = await getQuoteRequest({
fromChain: transferParams.fromChain,
fromTokenAddress: transferParams.fromTokenAddress,
fromUserAddress: transferParams.fromUserAddress,
toChain: transferParams.toChain,
tokenSymbol: transferParams.tokenSymbol,
toTokenAddress: transferParams.toTokenAddress,
toTokenSymbol: transferParams.toTokenSymbol,
toUserAddress: transferParams.toUserAddress,
// tokenAmount conversion
tokenAmount: convertEthToWei(
transferParams.tokenAmount,
transferParams.fromChainDecimal,
),
});
// Check if we got back any routes
if (!quotes?.routes.length) {
console.log('No routes available. Try increasing the send amount.');
return;
}
// If we got back any route, set our current route to the first route as it's the cheapest
setTransferRoute(quotes.routes[0]!);
}
Getting EVM to BTC Quote
To get a quote for a bridge transaction between any EVM chain and the Bitcoin network, we essentially only have to reverse our transactionParams
and perform a getQuoteRequest
:
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 quotes = await getQuoteRequest({
fromChain: transferParams.fromChain,
fromTokenAddress: transferParams.fromTokenAddress,
fromUserAddress: transferParams.fromUserAddress,
toChain: transferParams.toChain,
tokenSymbol: transferParams.tokenSymbol,
toTokenAddress: transferParams.toTokenAddress,
toTokenSymbol: transferParams.toTokenSymbol,
toUserAddress: transferParams.toUserAddress,
tokenAmount: convertEthToWei(
transferParams.tokenAmount,
transferParams.fromChainDecimal,
),
});