Build Your DApp#
If you're a DApp developer, this demo shows you how to build your DApp.
Example: Using the Trade API endpoint for token swaps. You'll swap USDC for ETH on the Ethereum network.
1. Set Up Your Environment#
// --------------------- npm package ---------------------
import { Web3 } from 'web3';
import axios from 'axios';
import * as dotenv from 'dotenv';
import CryptoJS from 'crypto-js';
// The URL for the Ethereum node you want to connect to
const web3 = new Web3('https://......com');
// --------------------- environment variable ---------------------
// Load hidden environment variables
dotenv.config();
// Your wallet information - REPLACE WITH YOUR OWN VALUES
const WALLET_ADDRESS: string = process.env.EVM_WALLET_ADDRESS || '0xYourWalletAddress';
const PRIVATE_KEY: string = process.env.EVM_PRIVATE_KEY || 'YourPrivateKey';
// Token addresses for swap on Ethereum
const ETH_ADDRESS: string = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; // Native ETH
const USDC_ADDRESS: string = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; // USDC on Ethereum
// Chain ID for Ethereum
const chainIndex: string = '1';
// API URL
const baseUrl: string = 'https://web3.okx.com/api/v6/';
// Amount to swap in smallest unit (10 USDC)
const SWAP_AMOUNT: string = '10000000'; // 10 USDC (USDC has 6 decimals)
const SLIPPAGEPERCENT: string = '0.5'; // 0.5% slippagePercent tolerance
// --------------------- util function ---------------------
export function getHeaders(timestamp: string, method: string, requestPath: string, queryString = "") {
// Check https://web3.okx.com/zh-hans/web3/build/docs/waas/rest-authentication for api-key
const apiKey = process.env.OKX_API_KEY;
const secretKey = process.env.OKX_SECRET_KEY;
const apiPassphrase = process.env.OKX_API_PASSPHRASE;
const projectId = process.env.OKX_PROJECT_ID;
if (!apiKey || !secretKey || !apiPassphrase || !projectId) {
throw new Error("Missing required environment variables");
}
const stringToSign = timestamp + method + requestPath + queryString;
return {
"Content-Type": "application/json",
"OK-ACCESS-KEY": apiKey,
"OK-ACCESS-SIGN": CryptoJS.enc.Base64.stringify(
CryptoJS.HmacSHA256(stringToSign, secretKey)
),
"OK-ACCESS-TIMESTAMP": timestamp,
"OK-ACCESS-PASSPHRASE": apiPassphrase,
"OK-ACCESS-PROJECT": projectId,
};
};
2. Check Allowance#
You need to check if the token has been approved for the DEX to spend. This step is only needed for ERC20 tokens, not for native tokens like ETH.
/**
* Check token allowance for DEX
* @param tokenAddress - Token contract address
* @param ownerAddress - Your wallet address
* @param spenderAddress - DEX spender address
* @returns Allowance amount
*/
async function checkAllowance(
tokenAddress: string,
ownerAddress: string,
spenderAddress: string
): Promise<bigint> {
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"
}
];
const tokenContract = new web3.eth.Contract(tokenABI, tokenAddress);
try {
const allowance = await tokenContract.methods.allowance(ownerAddress, spenderAddress).call();
return BigInt(String(allowance));
} catch (error) {
console.error('Failed to query allowance:', error);
throw error;
}
}
3. Check the Approval Parameters and Initiate the Approval#
If the allowance is lower than the amount you want to swap, you need to approve the token.
3.1 Define your transaction approval parameters#
const getApproveTransactionParams = {
chainIndex: chainIndex,
tokenContractAddress: tokenAddress,
approveAmount: amount
};
3.2 Define helper functions#
async function getApproveTransaction(
tokenAddress: string,
amount: string
): Promise<any> {
try {
const path = 'dex/aggregator/approve-transaction';
const url = `${baseUrl}${path}`;
const params = {
chainIndex: chainIndex,
tokenContractAddress: tokenAddress,
approveAmount: amount
};
// Prepare authentication
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
if (response.data.code === '0') {
return response.data.data[0];
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get approval transaction data:', (error as Error).message);
throw error;
}
}
3.3 Create Compute gasLimit utility function#
Using the Onchain gateway API to get the gas limit.
/**
* Get transaction gas limit from Onchain gateway API
* @param fromAddress - Sender address
* @param toAddress - Target contract address
* @param txAmount - Transaction amount (0 for approvals)
* @param inputData - Transaction calldata
* @returns Estimated gas limit
*/
async function getGasLimit(
fromAddress: string,
toAddress: string,
txAmount: string = '0',
inputData: string = ''
): Promise<string> {
try {
const path = 'dex/pre-transaction/gas-limit';
const url = `https://web3.okx.com/api/v6/${path}`;
const body = {
chainIndex: chainIndex,
fromAddress: fromAddress,
toAddress: toAddress,
txAmount: txAmount,
extJson: {
inputData: inputData
}
};
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
return response.data.data[0].gasLimit;
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get gas limit:', (error as Error).message);
throw error;
}
}
Using RPC to get the gas limit.
const gasLimit = await web3.eth.estimateGas({
from: WALLET_ADDRESS,
to: tokenAddress,
value: '0',
data: approveData.data
});
// Add 20% buffer
const gasLimit = (BigInt(gasLimit) * BigInt(12) / BigInt(10)).toString();
3.4 Get transaction information and send approveTransaction#
/**
* Sign and send approve transaction
* @param tokenAddress - Token to approve
* @param amount - Amount to approve
* @returns Transaction hash of the approval transaction
*/
async function approveToken(tokenAddress: string, amount: string): Promise<string | null> {
const spenderAddress = '0x40aA958dd87FC8305b97f2BA922CDdCa374bcD7f'; // Ethereum Mainnet DEX spender
// See Router addresses at: https://web3.okx.com/build/docs/waas/dex-smart-contract
const currentAllowance = await checkAllowance(tokenAddress, WALLET_ADDRESS, spenderAddress);
if (currentAllowance >= BigInt(amount)) {
console.log('Sufficient allowance already exists');
return null;
}
console.log('Insufficient allowance, approving tokens...');
// Get approve transaction data from OKX DEX API
const approveData = await getApproveTransaction(tokenAddress, amount);
// Get accurate gas limit using RPC
const gasLimit = await web3.eth.estimateGas({
from: WALLET_ADDRESS,
to: tokenAddress,
value: '0',
data: approveData.data
});
// Get accurate gas limit using Onchain gateway API
// const gasLimit = await getGasLimit(WALLET_ADDRESS, tokenAddress, '0', approveData.data);
// Get current gas price
const gasPrice = await web3.eth.getGasPrice();
const adjustedGasPrice = BigInt(gasPrice) * BigInt(15) / BigInt(10); // 1.5x for faster confirmation
// Get current nonce
const nonce = await web3.eth.getTransactionCount(WALLET_ADDRESS, 'latest');
// Create transaction object
const txObject = {
from: WALLET_ADDRESS,
to: tokenAddress,
data: approveData.data,
value: '0',
gas: gasLimit,
gasPrice: adjustedGasPrice.toString(),
nonce: nonce
};
// Sign and broadcast transaction
const signedTx = await web3.eth.accounts.signTransaction(txObject, PRIVATE_KEY);
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);
console.log(`Approval transaction successful: ${receipt.transactionHash}`);
return receipt.transactionHash;
}
4. Get Quote Data#
4.1 Define quote parameters#
const quoteParams = {
amount: fromAmount,
chainIndex: chainIndex,
toTokenAddress: toTokenAddress,
fromTokenAddress: fromTokenAddress,
};
4.2 Define helper functions#
/**
* Get swap quote from DEX API
* @param fromTokenAddress - Source token address
* @param toTokenAddress - Destination token address
* @param amount - Amount to swap
* @param slippagePercent - Maximum slippagePercent (e.g., "0.5" for 0.5%)
* @returns Swap quote
*/
async function getSwapQuote(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
slippagePercent: string = '0.5'
): Promise<any> {
try {
const path = 'dex/aggregator/quote';
const url = `${baseUrl}${path}`;
const params = {
chainIndex: chainIndex,
fromTokenAddress,
toTokenAddress,
amount,
slippagePercent
};
// Prepare authentication
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
if (response.data.code === '0') {
return response.data.data[0];
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get swap quote:', (error as Error).message);
throw error;
}
}
5. Prepare Transaction#
5.1 Define swap parameters#
const swapParams = {
chainIndex: chainIndex,
fromTokenAddress,
toTokenAddress,
amount,
userWalletAddress: userAddress,
slippagePercent
};
5.2 Request swap transaction data#
/**
* Get swap transaction data from DEX API
* @param fromTokenAddress - Source token address
* @param toTokenAddress - Destination token address
* @param amount - Amount to swap
* @param userAddress - User wallet address
* @param slippagePercent - Maximum slippagePercent (e.g., "0.5" for 0.5%)
* @returns Swap transaction data
*/
async function getSwapTransaction(
fromTokenAddress: string,
toTokenAddress: string,
amount: string,
userAddress: string,
slippagePercent: string = '0.5'
): Promise<any> {
try {
const path = 'dex/aggregator/swap';
const url = `${baseUrl}${path}`;
const params = {
chainIndex: chainIndex,
fromTokenAddress,
toTokenAddress,
amount,
userWalletAddress: userAddress,
slippagePercent
};
// Prepare authentication
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const queryString = "?" + new URLSearchParams(params).toString();
const headers = getHeaders(timestamp, 'GET', requestPath, queryString);
const response = await axios.get(url, { params, headers });
if (response.data.code === '0') {
return response.data.data[0];
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get swap transaction data:', (error as Error).message);
throw error;
}
}
6. Simulate Transaction#
Before executing the actual swap, it's crucial to simulate the transaction to ensure it will succeed and to identify any potential issues:
The Simulate API is available to our whitelisted customers only. Please reach out to dexapi@okx.com to request access.
async function simulateTransaction(swapData: any) {
try {
if (!swapData.tx) {
throw new Error('Invalid swap data format - missing transaction data');
}
const tx = swapData.tx;
const params: any = {
fromAddress: tx.from,
toAddress: tx.to,
txAmount: tx.value || '0',
chainIndex: chainIndex,
extJson: {
inputData: tx.data
},
includeDebug: true
};
const timestamp = new Date().toISOString();
const requestPath = "/api/v6/dex/pre-transaction/simulate";
const requestBody = JSON.stringify(params);
const headers = getHeaders(timestamp, "POST", requestPath, "", requestBody);
console.log('Simulating transaction...');
const response = await axios.post(
`https://web3.okx.com${requestPath}`,
params,
{ headers }
);
if (response.data.code !== "0") {
throw new Error(`Simulation failed: ${response.data.msg || "Unknown simulation error"}`);
}
const simulationResult = response.data.data[0];
// Check simulation success
if (simulationResult.success === false) {
console.error('Transaction simulation failed:', simulationResult.error);
throw new Error(`Transaction would fail: ${simulationResult.error}`);
}
console.log('Transaction simulation successful');
console.log(`Estimated gas used: ${simulationResult.gasUsed || 'N/A'}`);
if (simulationResult.logs) {
console.log('Simulation logs:', simulationResult.logs);
}
return simulationResult;
} catch (error) {
console.error("Error simulating transaction:", error);
throw error;
}
}
7. Broadcast Transaction#
First, use the Transaction API for gas estimation. This approach leverages Transaction API, which provides more accurate gas estimations than standard methods.
/**
* Get transaction gas limit from Onchain gateway API
* @param fromAddress - Sender address
* @param toAddress - Target contract address
* @param txAmount - Transaction amount (0 for approvals)
* @param inputData - Transaction calldata
* @returns Estimated gas limit
*/
async function getGasLimit(
fromAddress: string,
toAddress: string,
txAmount: string = '0',
inputData: string = ''
): Promise<string> {
try {
const path = 'dex/pre-transaction/gas-limit';
const url = `https://web3.okx.com/api/v6/${path}`;
const body = {
chainIndex: chainIndex,
fromAddress: fromAddress,
toAddress: toAddress,
txAmount: txAmount,
extJson: {
inputData: inputData
}
};
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
return response.data.data[0].gasLimit;
} else {
throw new Error(`API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to get gas limit:', (error as Error).message);
throw error;
}
}
Then sign the transaction locally and broadcast it through the Onchain gateway API. The API returns an orderId that you can use to track the transaction status.
/**
* Sign the transaction and broadcast it via Onchain gateway API
* @param swapData - Swap transaction data from step 5
* @returns Order ID for tracking the transaction status
*/
async function sendSwapTransaction(swapData: any): Promise<string> {
try {
const tx = swapData.tx;
// 1. Get an accurate gas limit from the Onchain gateway API
const gasLimit = await getGasLimit(tx.from, tx.to, tx.value || '0', tx.data);
// 2. Build the transaction object and sign it locally
const nonce = await web3.eth.getTransactionCount(userAddress, 'latest');
const txObject = {
from: tx.from,
to: tx.to,
data: tx.data,
value: tx.value || '0',
gas: gasLimit,
gasPrice: tx.gasPrice,
nonce: nonce
};
const signedTx = await web3.eth.accounts.signTransaction(txObject, PRIVATE_KEY);
// 3. Broadcast the signed transaction
const path = 'dex/pre-transaction/broadcast-transaction';
const url = `https://web3.okx.com/api/v6/${path}`;
const rawTxHex = typeof signedTx.rawTransaction === 'string'
? signedTx.rawTransaction
: web3.utils.bytesToHex(signedTx.rawTransaction);
const body = {
signedTx: rawTxHex,
chainIndex: chainIndex,
address: userAddress
};
// Prepare authentication with body included in signature
const bodyString = JSON.stringify(body);
const timestamp = new Date().toISOString();
const requestPath = `/api/v6/${path}`;
const headers = getHeaders(timestamp, 'POST', requestPath, "", bodyString);
const response = await axios.post(url, body, { headers });
if (response.data.code === '0') {
return response.data.data[0].orderId;
} else {
throw new Error(`Broadcast API Error: ${response.data.msg || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to broadcast transaction:', (error as Error).message);
throw error;
}
}
- 1. Set Up Your Environment2. Check Allowance3. Check the Approval Parameters and Initiate the Approval3.1 Define your transaction approval parameters3.2 Define helper functions3.3 Create Compute gasLimit utility function3.4 Get transaction information and send approveTransaction4. Get Quote Data4.1 Define quote parameters4.2 Define helper functions5. Prepare Transaction5.1 Define swap parameters5.2 Request swap transaction data6. Simulate Transaction7. Broadcast Transaction
