Create Limit Order

Overview

KyberSwap Limit Order enables Makers to create orders without incurring gas. To achieve this, signed Maker orders are stored off-chain but settled by the Takers on-chain. By gaslessly signing the off-chain order, a Maker pre-commits to a limit order whose parameters can't be tampered with ensuring order predicatability and security. Please refer to Off-Chain Relay, On-Chain Settlement for a design overview.

Sequence diagram

KyberSwap exposes 2 APIs which Makers will need to call to create a new order:

In addition to the above, Makers are also able to query their active making amount to aid with token approvals:

TypeScript Example

Limit Order API Demo

The code snippets in the guide below have been extracted from our demo GitHub repo which showcases the full end-to-end Limit Order operations in a TypeScript environment.

Step 1: Get the unsigned EIP712 Create Order message

To create a new limit order, the Maker must first specify the order parameters to be sent to the KyberSwap LO Service via the POST request body:

const requestBody: CreateOrderUnsignedBody = {
    chainId: ChainId.MATIC.toString(),
    makerAsset: makerAsset.address, // USDC
    takerAsset: takerAsset.address, // KNC
    maker: signerAddress,
    allowedSenders: [signerAddress], // Included so that only our account can fill this order
    makingAmount: "10000", // 0.01 USDC
    takingAmount: "20000000000000000", // 0.02 KNC
    expiredAt: Math.floor(Date.now() / 1000) + 60 * 60 // 60mins
};

postCreateOrdersUnsigned.ts

As part of the order configuration, note that the makingAmount and takingAmount are denominated in wei units and dependent on the decimals configured in the asset's ERC20 contract. The allowedSenders parameter is optional but have been included for the purposes of this demo so that only our controlled address can fill the created order.

The makerAsset and takerAsset are defined in the constants.ts file to enable convenient reuse across various operations.

Full details for each parameter can be found via the Get Unsigned Create Order Message API specification. Upon posting the above request body to /write/api/v1/orders/sign-message, the KyberSwap LO Service will return an unsigned EIP712 Create Order message.

Step 2: Check Limit Order contract spending allowance

Before signing the order creation message, we will first need to ensure that the LO smart contract has sufficient allowance to spend the Maker's ERC20 token. This ensures that when a Taker fills the order, the LO smart contract has the necessary approvals to transfer the specified makerAsset amount from the Maker's wallet.

KyberSwap Limit Order Contract Addresses

Please refer to Limit Order Contract Addresses for the exact addresses for KyberSwap Limit Order for each supported chain.

To get the Maker's current making amount for a specific token, KyberSwap exposes a /read-ks/api/v1/orders/active-making-amount API:

const targetPathConfig = {
    params: {
        chainId: ChainId.MATIC,
        makerAsset: makerAsset.address,
        maker: signerAddress
    }
};

getMakerActiveAmount.ts

Note that the new order making amount will have to be added to the current making amount. In other words, when creating a new order, the KyberSwap service checks that it has sufficient allowance across all open Maker orders.

const currentMakingAmount = await getMakerActiveAmount();
const newMakingAmount = Number(currentMakingAmount) + Number(unsignedOrderReqBody.makingAmount);

postCreateOrder.ts

If there is insufficient spending allowance, we can then request for a higher allowance via the makerAsset ERC20 token contract using our getTokenApproval() helper function:

if (Number(limitOrderContractAllowance) < spendingAmount) {
    console.log(`Insufficient allowance, getting approval for ${await tokenContract.symbol()}...`);
    try {
        // Call the ERC20 approve method
        const approvalTx = await tokenContract.approve(
            spenderAddress, 
            BigInt(spendingAmount), 
            {maxFeePerGas: 100000000000, maxPriorityFeePerGas: 100000000000}
            );

        // Wait for the approve tx to be executed
        const approvalTxReceipt = await approvalTx.wait();
        console.log(`Approve tx executed with hash: ${approvalTxReceipt?.hash}`);

    } catch(error) {
        console.log(error);
    }
};    

approval.ts

Step 3: Sign the EIP712 Create Order message

Once we have the necessary allowances in place, we can then go ahead and request the signature from the Maker:

const signature = await signer.signTypedData(
    unsignedOrderReturnData.domain,
    { Order: unsignedOrderReturnData.types.Order },
    unsignedOrderReturnData.message
);

postCreateOrder.ts

EIP712 Sign Typed Data

EIP712 exposes human-readable data for user's to view prior to signing the transaction.

Our example assumes a pure Node.js implementation and therefore uses the ethers.js signTypedData() function to sign the EIP712 message. Note that the type object used is per the primaryType that is returned by the KyberSwap LO Service.

For further information on how to implement this from the browser UI, you can also take reference from MetaMask's SignTypedData V4.

Step 4: Format the create order request body

With the maker-signed transaction, we can then format the request body according to the /write/api/v1/orders specs:

const requestBody: CreateOrderSignedBody = {
    ...unsignedOrderReqBody,
    salt: unsignedOrderReturnData.message.salt,
    signature: signature
};

postCreateOrder.ts

Note that apart from appending the signature to the existing request body from our earlier API call, we are also extracting the salt that was returned.

Step 5: Post the create order

We are now ready to call the /write/api/v1/orders API with the formatted create order message:

const {data} = await axios.post(
    LimitOrderDomain+targetPath,
    requestBody
);

postCreateOrder.ts

Note that the KyberSwap Limit Order domain is stored under constants.ts for easy retrieval across various API operations.

Once the KyberSwap LO service receives the creation order, a new off-chain order will be created in it's orderbook from which our network of Takers will be able to query. The created orderId will also be returned as part of the response values from the /write/api/v1/orders API.

Last updated