Build swap applications on the EVM network#
Build single-chain applications#
In this guide, we’ll provide an example token swap through OKX DEX, using ETH from the Ethereum network to purchase USDC. The process is as follows:
- Set up your environment
- Check allowance
- Check approval parameters and initiate the approval
- Request the /quote endpoint and get the quote data
- Request the /swap endpoint send the swap transaction
1. Set up your environment#
// --------------------- npm package ---------------------
const { Web3} = require('web3');
const cryptoJS = require('crypto-js');
// The URL for the Ethereum node you want to connect to
const web3 = new Web3('https://......com');
const apiBaseUrl = 'https://www.okx.com/api/v5/dex/aggregator';
// --------------------- environment variable ---------------------
const chainId = '1';
// usdc contract address
const fromTokenAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
// Native token contract address
const toTokenAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
// gasPrice or GasLimit ratio
const ratio = BigInt(3) / BigInt(2);
// your wallet address
const user = '0x6f9fxxxxxxxxxxxxxxxxxxxx61059dcfd9'
const fromAmount = '1000000'
// user wallet private key
const privateKey = 'xxxxx';
// open api Secret key
const secretkey = 'xxxxx'
// Get the current time
const date = new Date();
// --------------------- util function ---------------------
function getAggregatorRequestUrl(methodName, queryParams) {
return apiBaseUrl + methodName + '?' + (new URLSearchParams(queryParams)).toString();
}
// Check https://www.okx.com/zh-hans/web3/build/docs/waas/rest-authentication for api-key
const headersParams = {
'Content-Type': 'application/json',
// The api Key obtained from the previous application
'OK-ACCESS-KEY': 'xxxxx',
'OK-ACCESS-SIGN': cryptoJS.enc.Base64.stringify(
// The field order of headersParams should be consistent with the order of quoteParams.
// example : quote ==> cryptoJS.HmacSHA256(timestamp + 'GET' + '/api/v5/dex/aggregator/quote?amount=1000000&chainId=1&toTokenAddress=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE&fromTokenAddress=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', secretKey)
cryptoJS.HmacSHA256(date.toISOString() + 'GET' + '/api/v5/dex/aggregator/xxx/xxx/xxx', secretKey)
),
// Convert the current time to the desired format
'OK-ACCESS-TIMESTAMP': date.toISOString(),
// The password created when applying for the key
'OK-ACCESS-PASSPHRASE': 'xxxxxxx',
};
2.Check allowance#
Taking ETH chain as an example#
- This demo is in JavaScript
- Connect to an Ethereum node: You need to ensure that you’ve 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: To use the contract address and ABI of the token, you need to create an instance of the token contract. You can use web3.eth.Contract in web3.js to achieve this, and pass the contract address and ABI as arguments to the contract instance. a. 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.
- For spenderAddress, refer to dexTokenApproveAddress from here in the Response interface.
const tokenAddress = fromTokenAddress;
// user address
const ownerAddress = user;
// 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();
return parseFloat(allowance);
} catch (error) {
console.error('Failed to query allowance:', error);
}
}
- The variable allowanceAmount in the following text represents the actual allowance amount on the blockchain.
3. Check the approval parameters and initiate the approval#
Note
As allowanceAmount is lower than fromTokenAmount, you need to approve this token.
3.1 Define your transaction approval parameters#
- Next, define the parameters for the transaction approval you want to perform.
const getApproveTransactionParams = {
chainId: chainId,
tokenContractAddress: fromTokenAddress,
approveAmount: fromAmount,
};
3.2 Define helper functions#
- Define helper functions that will be used to interact with the DEX transaction approval API.
const approveTransaction = async () => {
const apiRequestUrl = getAggregatorRequestUrl(
'/approve-transaction',
getApproveTransactionParams
);
console.log('apiRequestUrl:', apiRequestUrl)
return fetch(apiRequestUrl, {
method: 'get',
headers: headersParams,
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
3.3 Get transaction information and send approveTransaction#
async function sendApproveTx() {
const allowanceAmount = await getAllowance(ownerAddress, spenderAddress);
if (allowanceAmount < parseFloat(fromAmount)) {
let gasPrice = await web3.eth.getGasPrice();
let nonce = await web3.eth.getTransactionCount(user)
const {data} = await approveTransaction();
const txObject = {
nonce: nonce,
to: getApproveTransactionParams.tokenContractAddress, // approve token address
gasLimit: data[0].gasLimit * 2, // avoid GasLimit too low
gasPrice: gasPrice * BigInt(3) / BigInt(2), // avoid GasPrice too low
data: data[0].data, // approve callData
value: 0 // approve value fix 0
};
const {rawTransaction} = await web3.eth.accounts.signTransaction(
txObject,
privateKey
);
await web3.eth.sendSignedTransaction(rawTransaction);
}
}
4. Request the /quote endpoint and get the quote data#
4.1 Define quote parameters#
- Next, define the parameters to get basic information of the quote and the router list.
const quoteParams = {
amount: fromAmount,
chainId: chainId,
toTokenAddress: toTokenAddress,
fromTokenAddress: fromTokenAddress,
};
4.2 Define helper functions#
- Define helper functions to interact with the DEX API.
const getQuote = async () => {
const apiRequestUrl = getAggregatorRequestUrl('/quote', quoteParams);
return fetch(apiRequestUrl, {
method: 'get',
headers: headersParams,
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
5. Request the /swap endpoint and sent transaction#
5.1 Define swap parameters#
- Next, define the parameters of the swap, and get the tx information.
const swapParams = {
chainId: chainid,
fromTokenAddress: fromTokenAddress,
toTokenAddress: toTokenAddress,
amount: fromAmount,
slippage: '0.03',
userWalletAddress: user
};
5.2 Define helper functions#
- Define helper functions to interact with the DEX API.
const getSwapData = async () => {
const apiRequestUrl = getAggregatorRequestUrl('/swap', swapParams);
return fetch(apiRequestUrl, {
method: 'get',
headers: headersParams,
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
5.3 Request the /swap endpoint and send the transaction#
async function sendSwapTx() {
const {data: swapData} = await getSwapData();
console.log('swapData:', swapData)
const swapDataTxInfo = swapData[0].tx;
const nonce = await web3.eth.getTransactionCount(user, 'latest');
let signTransactionParams = {
data: swapDataTxInfo.data,
gasPrice: BigInt(swapDataTxInfo.gasPrice) * BigInt(ratio), // avoid GasPrice too low,
to: swapDataTxInfo.to,
value: swapDataTxInfo.value,
gas: BigInt(swapDataTxInfo.gas) * BigInt(ratio), // avoid GasLimit too low
nonce,
};
const {rawTransaction} = await web3.eth.accounts.signTransaction(
signTransactionParams,
privateKey
);
const chainTxInfo = await web3.eth.sendSignedTransaction(rawTransaction);
console.log('chainTxInfo:', chainTxInfo);
}