DEX API

Build limit order applications#

In this guide, we will provide an example token limit order through OKX DEX. This process includes:

  • Set up your environment
  • Check allowance
  • Create order
  • Cancel order

1. Set up your environment#

Import the necessary Node.js libraries and set your environment variables as well as define helper functions and assembly parameters Node.js Environment Settings.

2. Check allowance#

2.1 Please refer to the tutorial#

Get allowance

  • The variable allowanceAmount in the following text represents the actual allowance amount on the blockchain.

2.2 Get allowance amount#

Obtain the specific quantity. If allowanceAmount !== '0', please check step 4. If allowanceAmount === '0', please check step 5.

const { data: allowanceData } = await getAllowanceData();
const allowanceAmount = allowanceData?.[0]?.allowanceAmount;

3. Create order#

Tip
If allowanceAmount is greater than zero, it indicates that the approval operation has been performed and the transaction can be directly sent.

3.1 Offline signature#

Structured signature using EIP712, the wallet will show the order structured content when signing.

const ethers = require('ethers');

// Build domain data
const domainData = {
    name: 'OKX LIMIT ORDER',
    version: '2.0',
    chainId: 1, // The chainId of the ETH mainnet
    verifyingContract: '0x2ae8947FB81f0AAd5955Baeff9Dcc7779A3e49F2' // LimitOrder contract address
};

// Define the limit order type
const Order = [
    { name: "salt", type: "uint256" },
    { name: "makerToken", type: "address" },
    { name: "takerToken", type: "address" },
    { name: "maker", type: "address" },
    { name: "receiver", type: "address" },
    { name: "allowedSender", type: "address" },
    { name: "makingAmount", type: "uint256" },
    { name: "takingAmount", type: "uint256" },
    { name: "minReturn", type: "uint256" },
    { name: "deadLine", type: "uint256" },
    { name: "partiallyAble", type: "bool" }
];

const orderData = {
    salt: 1702979522,
    makerToken: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
    takerToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    maker: "0x5F73e69237eB5bA3544273D165907447bF6a0AA7",
    receiver: "0x5F73e69237eB5bA3544273D165907447bF6a0AA7",
    allowedSender: "0x0000000000000000000000000000000000000000",
    makingAmount: 1000000,
    takingAmount: 1000000,
    minReturn: 1000000,
    deadLine: 1703254942,
    partiallyAble: true
}

// Build EIP-712 messages
const message = {
    domain: domainData,
    types: { Order },
    value: orderData,
};

// Create an Ethereum signature wallet
const privateKey = '<YOUR_PRIVATE_KEY>';
const wallet = new ethers.Wallet(privateKey);

(async () => {
    // Calculate the EIP-712 orderHash
    const orderHash = ethers.utils._TypedDataEncoder.hash(message.domain,
        message.types,
        message.value);
    console.log("Order data:", orderData);
    console.log("Order Hash:", orderHash);

    // Sign the message with the privatekey
    const signature =
        await wallet._signTypedData(
            message.domain,
            message.types,
            message.value
        );
    console.log("Order signature:", signature);

})();



3.2 Define parameters#

const limitOrderRequestParams = { orderHash, orderData, chainId, signature };

3.3 Create order#

const resp = await fetch(apiBaseUrl +"/limit-order/save-order", {
    method: "POST",
    body: JSON.stringify(limitOrderRequestParams),
    headers: headersParams,
  });

4. Cancel order#

const ethers = require('ethers');

// Example Limit Order Data
const orderData = {
    salt: 1700110500,    // random number salt as idempotent identifier , Current timestamp (millisecond value)
    makerToken: "0xMakerTokenAddress",    //Address of makerToken
    takerToken: "0xTakerTokenAddress",    //Address of takerToken
    maker: "0xMakerAddress",    //Address of order signer
    receiver: "0xTakerAddress",    //Default is zero address, and the recipient’s assets will be sent to the address of the limit order creator. If you set a value, the asset will be sent to the current address.
    allowedSender: "0xAllowedSender",    //User-specified counterparty address
    makingAmount: 2000,    //The number of a token to be sold
    takingAmount: 1000,    //The number of a token to be bought
    minReturn: 950,    //Minimum number of currencies to be cashed out for slippage control
    deadLine: 1700120500,    //Order Timeout Timestamp
    partiallyAble: false    //Whether to support partial transaction
};

// Connecting to an Ethernet node
const provider = new ethers.providers.JsonRpcProvider("https://mainnet.infura.io/v3/your_infura_project_id");

// Contract address and ABI
const contractAddress = "0x2ae8947FB81f0AAd5955Baeff9Dcc7779A3e49F2"; // LimitOrder contract address
const contractABI = [
    {
        "inputs": [
            {
                "components": [
                    {
                        "internalType": "uint256",
                        "name": "salt",
                        "type": "uint256"
                    },
                    {
                        "internalType": "address",
                        "name": "makerToken",
                        "type": "address"
                    },
                    {
                        "internalType": "address",
                        "name": "takerToken",
                        "type": "address"
                    },
                    {
                        "internalType": "address",
                        "name": "maker",
                        "type": "address"
                    },
                    {
                        "internalType": "address",
                        "name": "receiver",
                        "type": "address"
                    },
                    {
                        "internalType": "address",
                        "name": "allowedSender",
                        "type": "address"
                    },
                    {
                        "internalType": "uint256",
                        "name": "makingAmount",
                        "type": "uint256"
                    },
                    {
                        "internalType": "uint256",
                        "name": "takingAmount",
                        "type": "uint256"
                    },
                    {
                        "internalType": "uint256",
                        "name": "minReturn",
                        "type": "uint256"
                    },
                    {
                        "internalType": "uint256",
                        "name": "deadLine",
                        "type": "uint256"
                    },
                    {
                        "internalType": "bool",
                        "name": "partiallyAble",
                        "type": "bool"
                    }
                ],
                "internalType": "struct OrderLibV2.Order",
                "name": "_order",
                "type": "tuple"
            }
        ],
        "name": "cancelOrder",
        "outputs": [
            {
                "internalType": "uint256",
                "name": "orderRemaining",
                "type": "uint256"
            },
            {
                "internalType": "bytes32",
                "name": "orderHash",
                "type": "bytes32"
            }
        ],
        "stateMutability": "nonpayable",
        "type": "function"
    }
];

// Creating Contract Instances with Contract ABIs and Addresses
const contract = new ethers.Contract(contractAddress, contractABI, provider);

// Creating a wallet with a private key
const privateKey = "your_private_key";
const wallet = new ethers.Wallet(privateKey, provider);

// Cancel order
async function cancelOrder(orderData) {
    const tx = await contract.connect(wallet).cancelOrder(orderData);
    await tx.wait;
    console.log("txhash:", tx.hash);
}

// Example of interaction
(async () => {
    await cancelOrder(orderData);
})();