Trade API

Build Swap Applications on Sui#

There are two approaches to building swap applications with OKX DEX on Sui:

  • The API-first approach directly interacting with OKX DEX API endpoints
  • The SDK approach using the @okx-dex/okx-dex-sdk package for a simplified developer experience

This guide covers both methods to help you choose the approach that best fits your needs.

Method 1: API-first Approach#

In this guide, we will provide a use case for Sui token exchange through the OKX DEX.

1. Set Up Your Environment#

Import the necessary Node.js libraries and set up your environment variables:

// Required libraries
import { SuiWallet } from "@okxweb3/coin-sui";
import { getFullnodeUrl, SuiClient } from '@mysten/sui/client';
import { Transaction } from '@mysten/sui/transactions';
import cryptoJS from "crypto-js";
// Install dependencies
// npm i @okxweb3/coin-sui
// npm i @mysten/sui
// npm i crypto-js
// Set up environment variables
const apiKey = 'your_api_key';
const secretKey = 'your_secret_key';
const apiPassphrase = 'your_passphrase';
const projectId = 'your_project_id';
const userAddress = 'your_sui_wallet_address';
const userPrivateKey = 'your_sui_wallet_private_key';
// Constants
const SUI_CHAIN_ID = "784";
const DEFAULT_GAS_BUDGET = 50000000;
const MAX_RETRIES = 3;
// Initialize Sui client
const wallet = new SuiWallet();
const client = new SuiClient({
    url: getFullnodeUrl('mainnet')
});
// For Sui, you need to use the hexWithoutFlag format of your private key
// You can convert your key using sui keytool:
// sui keytool convert <your_sui_private_key>

2. Obtain Token Information and Swap Quote#

First, create a utility function to handle API authentication headers:

function getHeaders(timestamp, method, requestPath, queryString = "") {
    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,
    };
}

Then, create a function to get token information:

async function getTokenInfo(fromTokenAddress, toTokenAddress) {
    const timestamp = new Date().toISOString();
    const requestPath = "/api/v5/dex/aggregator/quote";
    const params = {
        chainId: SUI_CHAIN_ID,
        fromTokenAddress,
        toTokenAddress,
        amount: "1000000",
        slippage: "0.005",// 0.5% slippage
    };
    const queryString = "?" + new URLSearchParams(params).toString();
    const headers = getHeaders(timestamp, "GET", requestPath, queryString);
    const response = await fetch(
        `https://web3.okx.com${requestPath}${queryString}`,
        { method: "GET", headers }
    );
    if (!response.ok) {
        throw new Error(`Failed to get quote: ${await response.text()}`);
    }
    const data = await response.json();
    if (data.code !== "0" || !data.data?.[0]) {
        throw new Error("Failed to get token information");
    }
    const quoteData = data.data[0];
    return {
        fromToken: {
            symbol: quoteData.fromToken.tokenSymbol,
            decimals: parseInt(quoteData.fromToken.decimal),
            price: quoteData.fromToken.tokenUnitPrice
        },
        toToken: {
            symbol: quoteData.toToken.tokenSymbol,
            decimals: parseInt(quoteData.toToken.decimal),
            price: quoteData.toToken.tokenUnitPrice
        }
    };
}

Create a function to convert human-readable amounts to base units:

function convertAmount(amount, decimals) {
    try {
        if (!amount || isNaN(parseFloat(amount))) {
            throw new Error("Invalid amount");
        }
        const value = parseFloat(amount);
        if (value <= 0) {
            throw new Error("Amount must be greater than 0");
        }
        return (BigInt(Math.floor(value * Math.pow(10, decimals)))).toString();
    } catch (err) {
        console.error("Amount conversion error:", err);
        throw new Error("Invalid amount format");
    }
}

3. Get Swap Data#

3.1 Define swap parameters

const swapParams = {
      chainId: chainId,
      fromTokenAddress,
      toTokenAddress,
      amount,
      userWalletAddress: userAddress,
      slippage
};

3.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 slippage - Maximum slippage (e.g., "0.5" for 0.5%)
 * @returns Swap transaction data
 */
async function getSwapTransaction(
  fromTokenAddress: string,
  toTokenAddress: string,
  amount: string,
  userAddress: string,
  slippage: string = '0.5'
): Promise<any> {
  try {
    const path = 'dex/aggregator/swap';
    const url = `${baseUrl}${path}`;

    const params = {
      chainId: chainId,
      fromTokenAddress,
      toTokenAddress,
      amount,
      userWalletAddress: userAddress,
      slippage
    };

    // Prepare authentication
    const timestamp = new Date().toISOString();
    const requestPath = `/api/v5/${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;
  }
}

4. 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:

This function uses the Onchain gateway API. This API is available to our enterprise customers only. If you are interested, please contact us dexapi@okx.com.

async function simulateTransaction(txData) {
    try {
        if (!txData) {
            throw new Error('Invalid transaction data format');
        }

        const params = {
            chainIndex: SUI_CHAIN_ID,
            txData: txData,
            includeDebug: true
        };

        const timestamp = new Date().toISOString();
        const requestPath = "/api/v5/dex/pre-transaction/simulate";
        const requestBody = JSON.stringify(params);
        const headers = getHeaders(timestamp, "POST", requestPath, "", requestBody);

        console.log('Simulating transaction...');
        const response = await fetch(
            `https://web3.okx.com${requestPath}`,
            {
                method: 'POST',
                headers,
                body: requestBody
            }
        );

        const data = await response.json();
        if (data.code !== "0") {
            throw new Error(`Simulation failed: ${data.msg || "Unknown simulation error"}`);
        }

        const simulationResult = 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;
    }
}

5. Execute the Transaction#

First, prepare and sign the transaction:

async function executeSwap(txData, privateKey) {
    // Create transaction block
    const txBlock = Transaction.from(txData);
    txBlock.setSender(normalizedWalletAddress);
    // Set gas parameters
    const referenceGasPrice = await client.getReferenceGasPrice();
    txBlock.setGasPrice(BigInt(referenceGasPrice));
    txBlock.setGasBudget(BigInt(DEFAULT_GAS_BUDGET));
    // Build and sign transaction
    const builtTx = await txBlock.build({ client });
    const txBytes = Buffer.from(builtTx).toString('base64');
    const signedTx = await wallet.signTransaction({
        privateKey,
        data: {
            type: 'raw',
            data: txBytes
        }
    });
    if (!signedTx?.signature) {
        throw new Error("Failed to sign transaction");
    }
    return { builtTx, signature: signedTx.signature };
}

Then, send the transaction with either RPC or Onchain Gateway Methods

Using RPC:

async function sendTransaction(builtTx, signature) {
    // Execute transaction
    const result = await client.executeTransactionBlock({
        transactionBlock: builtTx,
        signature: [signature],
        options: {
            showEffects: true,
            showEvents: true,
        }
    });
    // Wait for confirmation
    const confirmation = await client.waitForTransaction({
        digest: result.digest,
        options: {
            showEffects: true,
            showEvents: true,
        }
    });
    console.log("\nSwap completed successfully!");
    console.log("Transaction ID:", result.digest);
    console.log("Explorer URL:", `https://suiscan.xyz/mainnet/tx/${result.digest}`);
    return result.digest;
}

Using Onchain gateway API:

This function uses the Onchain gateway API. This API is available to our enterprise customers only. If you are interested, please contact us dexapi@okx.com.

async function broadcastTransaction(signedTx, chainId, walletAddress) {
    try {
        const path = 'dex/pre-transaction/broadcast-transaction';
        const url = `https://web3.okx.com/api/v5/${path}`;

        const broadcastData = {
            signedTx: signedTx.rawTransaction,
            chainIndex: chainId,
            address: walletAddress
        };

        // Prepare authentication with body included in signature
        const bodyString = JSON.stringify(broadcastData);
        const timestamp = new Date().toISOString();
        const requestPath = `/api/v5/${path}`;
        const headers = getHeaders(timestamp, 'POST', requestPath, bodyString);

        const response = await fetch(url, {
            method: 'POST',
            headers,
            body: bodyString
        });

        const data = await response.json();
        if (data.code === '0') {
            const orderId = data.data[0].orderId;
            console.log(`Swap transaction broadcast successfully, Order ID: ${orderId}`);
            return orderId;
        } else {
            throw new Error(`API Error: ${data.msg || 'Unknown error'}`);
        }
    } catch (error) {
        console.error('Failed to broadcast swap transaction:', error);
        throw error;
    }
}

6. Track Transaction#

Using Onchain gateway API:

This function uses the Onchain gateway API. This API is available to our enterprise customers only. If you are interested, please contact us dexapi@okx.com.

async function trackTransaction(orderId, intervalMs = 5000, timeoutMs = 300000) {
    console.log(`Tracking transaction with Order ID: ${orderId}`);

    const startTime = Date.now();
    let lastStatus = '';

    while (Date.now() - startTime < timeoutMs) {
        try {
            const path = 'dex/post-transaction/orders';
            const url = `https://web3.okx.com/api/v5/${path}`;

            const params = {
                orderId: orderId,
                chainIndex: SUI_CHAIN_ID,
                address: userAddress,
                limit: '1'
            };

            const timestamp = new Date().toISOString();
            const requestPath = `/api/v5/${path}`;
            const queryString = "?" + new URLSearchParams(params).toString();
            const headers = getHeaders(timestamp, 'GET', requestPath, queryString);

            const response = await fetch(`${url}${queryString}`, { headers });
            const data = await response.json();

            if (data.code === '0' && data.data && data.data.length > 0) {
                if (data.data[0].orders && data.data[0].orders.length > 0) {
                    const txData = data.data[0].orders[0];
                    const status = txData.txStatus;

                    if (status !== lastStatus) {
                        lastStatus = status;

                        if (status === '1') {
                            console.log(`Transaction pending: ${txData.txHash || 'Hash not available yet'}`);
                        } else if (status === '2') {
                            console.log(`Transaction successful: https://suiscan.xyz/mainnet/tx/${txData.txHash}`);
                            return txData;
                        } else if (status === '3') {
                            const failReason = txData.failReason || 'Unknown reason';
                            console.error(`Transaction failed: ${failReason}`);
                            throw new Error(`Transaction failed: ${failReason}`);
                        }
                    }
                }
            }
        } catch (error) {
            console.warn('Error checking transaction status:', error.message);
        }

        await new Promise(resolve => setTimeout(resolve, intervalMs));
    }

    throw new Error('Transaction tracking timed out');
}

Track transaction using SWAP API:

async function trackTransactionWithSwapAPI(txHash) {
    try {
        const path = 'dex/aggregator/history';
        const url = `${baseUrl}${path}`;

        const params = {
            chainId: SUI_CHAIN_ID,
            txHash: txHash,
            isFromMyProject: 'false'
        };

        const timestamp = new Date().toISOString();
        const requestPath = `/api/v5/${path}`;
        const queryString = "?" + new URLSearchParams(params).toString();
        const headers = getHeaders(timestamp, 'GET', requestPath, queryString);

        console.log('Fetching transaction status...');
        const response = await fetch(`${url}${queryString}`, { headers });
        const data = await response.json();

        if (!data) {
            throw new Error('No response data received from API');
        }

        if (data.code !== '0') {
            throw new Error(`API Error: ${data.msg || 'Unknown error'}`);
        }

        if (!data.data || !Array.isArray(data.data) || data.data.length === 0) {
            console.log('Transaction not found in history yet, might be too recent');
            return { status: 'pending', details: null };
        }

        const txData = data.data[0];
        if (!txData) {
            console.log('Transaction data not available yet');
            return { status: 'pending', details: null };
        }

        const status = txData.status;
        console.log(`Transaction status: ${status}`);

        if (status === 'pending') {
            console.log(`Transaction is still pending: ${txHash}`);
            return { status: 'pending', details: txData };
        } else if (status === 'success') {
            console.log(`Transaction successful!`);
            console.log(`From: ${txData.fromTokenDetails.symbol} - Amount: ${txData.fromTokenDetails.amount}`);
            console.log(`To: ${txData.toTokenDetails.symbol} - Amount: ${txData.toTokenDetails.amount}`);
            console.log(`Transaction Fee: ${txData.txFee}`);
            console.log(`Explorer URL: https://suiscan.xyz/mainnet/tx/${txHash}`);
            return { status: 'success', details: txData };
        } else if (status === 'fail') {
            const errorMsg = txData.errorMsg || 'Unknown reason';
            console.error(`Transaction failed: ${errorMsg}`);
            return { status: 'failure', details: txData, error: errorMsg };
        }

        return { status: 'unknown', details: txData };
    } catch (error) {
        console.error('Failed to track transaction status:', error.message);
        return { status: 'pending', details: null, error: error.message };
    }
}

7. Complete Implementation#

Here's a complete implementation putting it all together:

// swap.ts
import { SuiWallet } from "@okxweb3/coin-sui";
import { getFullnodeUrl, SuiClient } from '@mysten/sui/client';
import { Transaction } from '@mysten/sui/transactions';
import cryptoJS from "crypto-js";
import dotenv from 'dotenv';
dotenv.config();

// Environment variables
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;
const userAddress = process.env.WALLET_ADDRESS;
const userPrivateKey = process.env.PRIVATE_KEY;

// Constants
const SUI_CHAIN_ID = "784";
const DEFAULT_GAS_BUDGET = 50000000;
const MAX_RETRIES = 3;

// Initialize clients
const wallet = new SuiWallet();
const client = new SuiClient({
    url: getFullnodeUrl('mainnet')
});

// Normalize wallet address
const normalizedWalletAddress = userAddress;

function getHeaders(timestamp: string, method: string, requestPath: string, queryString: string = "", requestBody: string = "") {
    if (!apiKey || !secretKey || !apiPassphrase || !projectId) {
        throw new Error("Missing required environment variables");
    }
    const stringToSign = timestamp + method + requestPath + queryString + requestBody;
    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,
    };
}
async function getTokenInfo(fromTokenAddress: string, toTokenAddress: string) {
    const timestamp = new Date().toISOString();
    const requestPath = "/api/v5/dex/aggregator/quote";
    const params = {
        chainId: SUI_CHAIN_ID,
        fromTokenAddress,
        toTokenAddress,
        amount: "1000000",
        slippage: "0.005",// 0.5% slippage
    };
    const queryString = "?" + new URLSearchParams(params).toString();
    const headers = getHeaders(timestamp, "GET", requestPath, queryString);
    const response = await fetch(
        `https://web3.okx.com${requestPath}${queryString}`,
        { method: "GET", headers }
    );
    if (!response.ok) {
        throw new Error(`Failed to get quote: ${await response.text()}`);
    }
    const data = await response.json();
    if (data.code !== "0" || !data.data?.[0]) {
        throw new Error("Failed to get token information");
    }
    const quoteData = data.data[0];
    return {
        fromToken: {
            symbol: quoteData.fromToken.tokenSymbol,
            decimals: parseInt(quoteData.fromToken.decimal),
            price: quoteData.fromToken.tokenUnitPrice
        },
        toToken: {
            symbol: quoteData.toToken.tokenSymbol,
            decimals: parseInt(quoteData.toToken.decimal),
            price: quoteData.toToken.tokenUnitPrice
        }
    };
}
function convertAmount(amount: string | number, decimals: number) {
    try {
        if (!amount || isNaN(parseFloat(amount.toString()))) {
            throw new Error("Invalid amount");
        }
        const value = parseFloat(amount.toString());
        if (value <= 0) {
            throw new Error("Amount must be greater than 0");
        }
        return (BigInt(Math.floor(value * Math.pow(10, decimals)))).toString();
    } catch (err) {
        console.error("Amount conversion error:", err);
        throw new Error("Invalid amount format");
    }
}

async function trackTransactionWithSwapAPI(txHash: string) {
    try {
        const path = 'dex/aggregator/history';
        const url = `https://web3.okx.com/api/v5/${path}`;

        const params = {
            chainId: SUI_CHAIN_ID,
            txHash: txHash,
            isFromMyProject: 'false'
        };

        const timestamp = new Date().toISOString();
        const requestPath = `/api/v5/${path}`;
        const queryString = "?" + new URLSearchParams(params).toString();
        const headers = getHeaders(timestamp, 'GET', requestPath, queryString);

        const response = await fetch(`${url}${queryString}`, { headers });
        const data = await response.json();

        if (data.code !== '0') {
            throw new Error(`API Error: ${data.msg || 'Unknown error'}`);
        }

        return data.data?.[0] || { status: 'pending' };
    } catch (error) {
        console.error('Failed to track transaction:', error);
        return { status: 'error', error: error instanceof Error ? error.message : 'Unknown error' };
    }
}


async function main() {
    try {
        const args = process.argv.slice(2);
        if (args.length < 3) {
            console.log("Usage: ts-node swap.ts <amount> <fromTokenAddress> <toTokenAddress>");
            console.log("Example: ts-node swap.ts 1.5 0x2::sui::SUI 0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC");
            process.exit(1);
        }

        const [amount, fromTokenAddress, toTokenAddress] = args;
        if (!userPrivateKey || !userAddress) {
            throw new Error("Private key or user address not found");
        }

        // Get token information
        console.log("Getting token information...");
        const tokenInfo = await getTokenInfo(fromTokenAddress, toTokenAddress);
        console.log(`From: ${tokenInfo.fromToken.symbol} (${tokenInfo.fromToken.decimals} decimals)`);
        console.log(`To: ${tokenInfo.toToken.symbol} (${tokenInfo.toToken.decimals} decimals)`);

        // Convert amount using fetched decimals
        const rawAmount = convertAmount(amount, tokenInfo.fromToken.decimals);
        console.log(`Amount in ${tokenInfo.fromToken.symbol} base units:`, rawAmount);

        // Get swap quote
        const quoteParams = {
            chainId: SUI_CHAIN_ID,
            amount: rawAmount,
            fromTokenAddress,
            toTokenAddress,
            slippage: "0.005",// 0.5% slippage
            userWalletAddress: normalizedWalletAddress || "",
        };

        // Get swap data
        const timestamp = new Date().toISOString();
        const requestPath = "/api/v5/dex/aggregator/swap";
        const queryString = "?" + new URLSearchParams(quoteParams).toString();
        const headers = getHeaders(timestamp, "GET", requestPath, queryString);

        console.log("Requesting swap quote...");
        const response = await fetch(
            `https://web3.okx.com${requestPath}${queryString}`,
            { method: "GET", headers }
        );
        const data = await response.json();

        if (data.code !== "0") {
            throw new Error(`API Error: ${data.msg}`);
        }

        const swapData = data.data[0];

        // Show estimated output and price impact
        const outputAmount = parseFloat(swapData.routerResult.toTokenAmount) / Math.pow(10, tokenInfo.toToken.decimals);
        console.log("\nSwap Quote:");
        console.log(`Input: ${amount} ${tokenInfo.fromToken.symbol} ($${(parseFloat(amount) * parseFloat(tokenInfo.fromToken.price)).toFixed(2)})`);
        console.log(`Output: ${outputAmount.toFixed(tokenInfo.toToken.decimals)} ${tokenInfo.toToken.symbol} ($${(outputAmount * parseFloat(tokenInfo.toToken.price)).toFixed(2)})`);

        if (swapData.priceImpactPercentage) {
            console.log(`Price Impact: ${swapData.priceImpactPercentage}%`);
        }

        console.log("\nExecuting swap transaction...");
        let retryCount = 0;
        while (retryCount < MAX_RETRIES) {
            try {
                // Create transaction block
                const txBlock = Transaction.from(swapData.tx.data);
                if (!normalizedWalletAddress) {
                    throw new Error("Wallet address is not defined");
                }
                txBlock.setSender(normalizedWalletAddress);
                // Set gas parameters
                const referenceGasPrice = await client.getReferenceGasPrice();
                txBlock.setGasPrice(BigInt(referenceGasPrice));
                txBlock.setGasBudget(BigInt(DEFAULT_GAS_BUDGET));
                // Build and sign transaction
                const builtTx = await txBlock.build({ client });
                const txBytes = Buffer.from(builtTx).toString('base64');
                const signedTx = await wallet.signTransaction({
                    privateKey: userPrivateKey,
                    data: {
                        type: 'raw',
                        data: txBytes
                    }
                });
                if (!signedTx?.signature) {
                    throw new Error("Failed to sign transaction");
                }

                // Execute transaction
                const result = await client.executeTransactionBlock({
                    transactionBlock: builtTx,
                    signature: [signedTx.signature],
                    options: {
                        showEffects: true,
                        showEvents: true,
                    }
                });

                // Wait for confirmation
                const confirmation = await client.waitForTransaction({
                    digest: result.digest,
                    options: {
                        showEffects: true,
                        showEvents: true,
                    }
                });

                console.log("\nSwap completed successfully!");
                console.log("Transaction ID:", result.digest);
                console.log("Explorer URL:", `https://suiscan.xyz/mainnet/tx/${result.digest}`);

                // Track transaction
                const txStatus = await trackTransactionWithSwapAPI(result.digest);
                console.log("Transaction Status:", txStatus);

                process.exit(0);
            } catch (error) {
                console.error(`Attempt ${retryCount + 1} failed:`, error);
                retryCount++;
                if (retryCount === MAX_RETRIES) {
                    throw error;
                }
                await new Promise(resolve => setTimeout(resolve, 2000 * retryCount));
            }
        }
    } catch (error) {
        console.error("Error:", error instanceof Error ? error.message : "Unknown error");
        process.exit(1);
    }
}

if (require.main === module) {
    main();
}

Method 2: SDK Approach#

Using the OKX DEX SDK provides a much simpler developer experience while retaining all the functionality of the API-first approach. The SDK handles many implementation details for you, including retry logic, error handling, and transaction management.

1. Install the SDK#

npm install @okx-dex/okx-dex-sdk
# or
yarn add @okx-dex/okx-dex-sdk
# or
pnpm add @okx-dex/okx-dex-sdk

2. Setup Your Environment#

Create a .env file with your API credentials and wallet information:

# OKX API Credentials
OKX_API_KEY=your_api_key
OKX_SECRET_KEY=your_secret_key
OKX_API_PASSPHRASE=your_passphrase
OKX_PROJECT_ID=your_project_id
# Sui Configuration
SUI_WALLET_ADDRESS=your_sui_wallet_address
SUI_PRIVATE_KEY=your_sui_private_key

Remember that you need to use the hexWithoutFlag format of your SUI private key, which you can obtain using the SUI CLI:

sui keytool convert <your_sui_private_key>

3. Initialize the Client#

Create a file for your DEX client (e.g., DexClient.ts):

// DexClient.ts
import { OKXDexClient } from '@okx-dex/okx-dex-sdk';
import 'dotenv/config';

// Validate environment variables
const requiredEnvVars = [
    'OKX_API_KEY',
    'OKX_SECRET_KEY',
    'OKX_API_PASSPHRASE',
    'OKX_PROJECT_ID',
    'SUI_WALLET_ADDRESS',
    'SUI_PRIVATE_KEY'
];

for (const envVar of requiredEnvVars) {
    if (!process.env[envVar]) {
        throw new Error(`Missing required environment variable: ${envVar}`);
    }
}

// Initialize the client
export const client = new OKXDexClient({
    apiKey: process.env.OKX_API_KEY!,
    secretKey: process.env.OKX_SECRET_KEY!,
    apiPassphrase: process.env.OKX_API_PASSPHRASE!,
    projectId: process.env.OKX_PROJECT_ID!,
    sui: {
        privateKey: process.env.SUI_PRIVATE_KEY!,
        walletAddress: process.env.SUI_WALLET_ADDRESS!,
        connection: {
            rpcUrl: 'https://sui-mainnet.blockvision.org'
        }
    }
});

4. Create a Token Helper (Optional)#

You can create a token list helper for easy reference:

// Common tokens on Sui mainnet
export const TOKENS = {
    SUI: "0x2::sui::SUI",
    USDC: "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC"
} as const;

5. Execute a Swap With the SDK#

Create a swap execution file:

// swap.ts
import { client } from './DexClient';
import { TOKENS } from './Tokens'; // Optional, if you created the token helper

/**
 * Example: Execute a swap from SUI to USDC
 */
async function executeSwap() {
  try {
    if (!process.env.SUI_PRIVATE_KEY) {
      throw new Error('Missing SUI_PRIVATE_KEY in .env file');
    }

    // First, get token information using a quote
    console.log("Getting token information...");
    const fromTokenAddress = TOKENS.SUI; // Or use directly: "0x2::sui::SUI"
    const toTokenAddress = TOKENS.USDC; // Or use directly: "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC"

    const quote = await client.dex.getQuote({
        chainId: '784', // Sui chain ID
        fromTokenAddress,
        toTokenAddress,
        amount: '1000000', // Small amount for quote
        slippage: '0.005' // 0.5% slippage
    });

    const tokenInfo = {
        fromToken: {
            symbol: quote.data[0].fromToken.tokenSymbol,
            decimals: parseInt(quote.data[0].fromToken.decimal),
            price: quote.data[0].fromToken.tokenUnitPrice
        },
        toToken: {
            symbol: quote.data[0].toToken.tokenSymbol,
            decimals: parseInt(quote.data[0].toToken.decimal),
            price: quote.data[0].toToken.tokenUnitPrice
        }
    };

    // Convert amount to base units
    const humanReadableAmount = 1.5; // 1.5 SUI
    const rawAmount = (humanReadableAmount * Math.pow(10, tokenInfo.fromToken.decimals)).toString();

    console.log("\nSwap Details:");
    console.log("--------------------");
    console.log(`From: ${tokenInfo.fromToken.symbol}`);
    console.log(`To: ${tokenInfo.toToken.symbol}`);
    console.log(`Amount: ${humanReadableAmount} ${tokenInfo.fromToken.symbol}`);
    console.log(`Amount in base units: ${rawAmount}`);
    console.log(`Approximate USD value: $${(humanReadableAmount * parseFloat(tokenInfo.fromToken.price)).toFixed(2)}`);

    // Execute the swap
    console.log("\nExecuting swap...");
    const swapResult = await client.dex.executeSwap({
      chainId: '784', // Sui chain ID
      fromTokenAddress,
      toTokenAddress,
      amount: rawAmount,
      slippage: '0.005', // 0.5% slippage
      userWalletAddress: process.env.SUI_WALLET_ADDRESS!
    });

    console.log('Swap executed successfully:');
    console.log("\nTransaction ID:", swapResult.transactionId);
    console.log("Explorer URL:", swapResult.explorerUrl);

    if (swapResult.details) {
        console.log("\nDetails:");
        console.log(`Input: ${swapResult.details.fromToken.amount} ${swapResult.details.fromToken.symbol}`);
        console.log(`Output: ${swapResult.details.toToken.amount} ${swapResult.details.toToken.symbol}`);
        if (swapResult.details.priceImpact) {
            console.log(`Price Impact: ${swapResult.details.priceImpact}%`);
        }
    }

    return swapResult;
  } catch (error) {
    if (error instanceof Error) {
      console.error('Error executing swap:', error.message);
      // API errors include details in the message
      if (error.message.includes('API Error:')) {
        const match = error.message.match(/API Error: (.*)/);
        if (match) console.error('API Error Details:', match[1]);
      }
    }
    throw error;
  }
}

// Run if this file is executed directly
if (require.main === module) {
  executeSwap()
    .then(() => process.exit(0))
    .catch((error) => {
      console.error('Error:', error);
      process.exit(1);
    });
}

export { executeSwap };

Additional SDK functionality

The SDK provides additional methods that simplify development:

Get a quote for a token pair

const quote = await client.dex.getQuote({
    chainId: '784',  // Sui
    fromTokenAddress: '0x2::sui::SUI', // SUI
    toTokenAddress: '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC', // USDC
    amount: '100000000',  // In base units
    slippage: '0.005'     // 0.5% slippage
});