In this guide, we’ll show you how to do a cross-chain swap using USDT on the Ethereum chain for USDC on the Arbitrum chain as an example provided by OKX DEX. This process includes:
Import the necessary Node.js libraries and set your environment variables as well as define helper functions and assembly parameters Node.js Environment Settings.
This demo is in JavaScript
Connect to an Ethereum node
: You need to ensure that you have connected to an available Ethereum node. You can use web3.js or other Ethereum development libraries to connect to the node. In the code, you need to specify the node's HTTP or WebSocket endpoint.Obtain a token contract instance
: Using the contract address and ABI of the token, you need to create an instance of the token contract. You can use the web3.eth.Contract in web3.js to achieve this. Pass the contract address and ABI as arguments to the contract instance.Query the authorized amount
: Query the authorized allowance by calling the allowance function of the contract instance. This function requires two parameters: the owner's address and the authorized party's address (spenderAddress). You can query the authorized allowance by providing these two addresses during the call.const { Web3 } = require('web3');
// Connect to an Ethereum node
const web3 = new Web3('https://xxxxx');
// token address and ABI
const tokenAddress = '0xxxxxxxxx';
// user address
const ownerAddress = '0xxxxxxxx';
// ETH dex token approval address
const spenderAddress = '0x40aa958dd87fc8305b97f2ba922cddca374bcd7f';
const tokenABI = [
{
"constant": true,
"inputs": [
{
"name": "_owner",
"type": "address"
},
{
"name": "_spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
];
// Create token contract instance
const tokenContract = new web3.eth.Contract(tokenABI, tokenAddress);
// Query token approve allowance function
async function getAllowance(ownerAddress, spenderAddress) {
try {
const allowance = await tokenContract.methods.allowance(ownerAddress, spenderAddress).call();
console.log(`Allowance for ${ownerAddress} to ${spenderAddress}: ${allowance}`);
} catch (error) {
console.error('Failed to query allowance:', error);
}
}
getAllowance(ownerAddress, spenderAddress).then(r => console.log(r));
const { data: allowanceData } = await getAllowanceData();
const allowanceAmount = allowanceData?.[0]?.allowanceAmount;
Next, define the parameters for the transaction approval you want to perform.
const getApproveTransactionParams = {
chainId: fromChainId,
tokenContractAddress: fromTokenAddress,
userWalletAddress,
approveAmount: fromTokenAmount,
};
Define helper functions to interact with the DEX API.
const approveTransaction = async () => {
const { apiRequestUrl, path } = getAggregatorRequestUrl(
'/approve-transaction',
getApproveTransactionParams
);
return fetch(apiRequestUrl, {
method: 'get',
headers: headersParams,
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
if (parseFloat(allowanceAmount) < parseFloat(fromTokenAmount)) {
const { data } = await approveTransaction(allowanceAmount);
let allowanceParams = {
...{ data: data[0].data }, // You can modify the data content you want in accordance with the Web3 official website
};
const { rawTransaction } = await web3.eth.accounts.signTransaction(
allowanceParams,
privateKey
);
await web3.eth.sendSignedTransaction(rawTransaction);
}
Next, define the parameters to get the tradable toChainId list through fromChainId.
const toChainListParams = {
chainId: fromChainId,
};
Define helper functions to interact with the DEX API.
const getSupportedChain = async () => {
const { apiRequestUrl, path } = getCrossChainBaseUrl(
'/supported/chain',
toChainListParams
);
return fetch(apiRequestUrl, {
method: 'get',
headers: headersParams,
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
You can also select one of the other chains as the destination chain based on the list.
const { data: supportedChainList } = await getSupportedChain();
const selectChainItem = supportedChainList.find((item) => {
return item.chainName === 'Arbitrum';
});
toChainId = selectChainItem?.chainId;
Next, define the parameters to get a list of tradable tokens through toChainId.
const toChainTokenListParams = {
chainId: toChainId,
};
Define helper functions to interact with the DEX API.
const getToChainTokenList = async () => {
const { apiRequestUrl, path } = getAggregatorRequestUrl(
'/all-tokens',
toChainTokenListParams
);
return fetch(apiRequestUrl, {
method: 'get',
headers: headersParams,
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
You can also select another token as the destination token.
const { data: toChainTokenList } = await getToChainTokenList();
const selectToChainToken = toChainTokenList.find((item) => {
return item.tokenSymbol === 'USDC';
});
toTokenAddress = selectToChainToken?.tokenContractAddress;
Next, define the parameters to get basic information of the quote and the router list.
const quoteParams = {
fromChainId,
toChainId,
fromTokenAddress,
toTokenAddress,
amount: fromTokenAmount,
slippage,
};
Define helper functions to interact with the DEX API.
const getQuote = async () => {
const { apiRequestUrl, path } = getCrossChainBaseUrl('/quote', quoteParams);
return fetch(apiRequestUrl, {
method: 'get',
headers: headersParams,
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
const { data: quoteData } = await getQuote();
bridgeId = quoteData[0]?.routerList[0]?.router?.bridgeId;
Next, define the parameters to get the tx information of the cross-chain swap.
const swapParams = {
fromChainId: fromChainId,
toChainId: toChainId,
fromTokenAddress,
toTokenAddress,
amount: fromTokenAmount,
slippage,
userWalletAddress,
bridgeId,
};
Define helper functions to interact with the DEX API.
const getSwapData = async () => {
const { apiRequestUrl, path } = getCrossChainBaseUrl('/build-tx', swapParams);
return fetch(apiRequestUrl, {
method: 'get',
headers: headersParams,
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
const { data: swapData } = await getSwapData();
const swapDataTxInfo = swapData[0].tx;
const nonce = await web3.eth.getTransactionCount(userWalletAddress, 'latest');
// You can obtain the latest nonce and process the hexadecimal numbers starting with 0x according to your needs
let signTransactionParams = {
data: swapDataTxInfo.data,
gasPrice: swapDataTxInfo.gasPrice,
to: swapDataTxInfo.to,
value: swapDataTxInfo.value,
nonce,
};
const { rawTransaction } = await web3.eth.accounts.signTransaction(
signTransactionParams,
privateKey
);
const chainTxInfo = await web3.eth.sendSignedTransaction(rawTransaction);
transactionTx = chainTxInfo;
Next, define the parameters, mainly the source chain hash address.
const getCheckStatusParams = {
hash: transactionTx,
};
Define helper functions to interact with the DEX API.
const checkTransactionStatus = async () => {
const { apiRequestUrl, path } = getCrossChainBaseUrl(
'/status',
getCheckStatusParams
);
return fetch(apiRequestUrl, {
method: 'get',
headers: headersParams,
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
const { data: statusInfo } = await checkTransactionStatus();
console.log(statusInfo?.data[0]?.detailStatus);