Skip to content

Repository files navigation

1inch Limit Order Protocol v4 SDK

Installation

npm install '@1inch/limit-order-sdk'

Docs

Usage examples

Order creation

import {LimitOrder, MakerTraits, Address, Sdk, randBigInt, FetchProviderConnector} from "@1inch/limit-order-sdk"
import {Wallet} from 'ethers'

// it is a well-known test private key, do not use it in production
const privKey =
    '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
const authKey = '...'
const maker = new Wallet(privKey)
const expiresIn = 120n // 2m
const expiration = BigInt(Math.floor(Date.now() / 1000)) + expiresIn

const UINT_40_MAX = (1n << 48n) - 1n

// see MakerTraits.ts
const makerTraits = MakerTraits.default()
  .withExpiration(expiration)
  .withNonce(randBigInt(UINT_40_MAX))

const sdk = new Sdk({ authKey, networkId: 1, httpConnector: new FetchProviderConnector() })

const order = await sdk.createOrder({
    makerAsset: new Address('0xdac17f958d2ee523a2206206994597c13d831ec7'),
    takerAsset: new Address('0x111111111117dc0aa78b770fa6a738034120c302'),
    makingAmount: 100_000000n, // 100 USDT
    takingAmount: 10_00000000000000000n, // 10 1INCH
    maker: new Address(maker.address),
    // salt? : bigint
    // receiver? : Address
}, makerTraits)

const typedData = order.getTypedData()
const signature = await maker.signTypedData(
    typedData.domain,
    {Order: typedData.types.Order},
    typedData.message
)

await sdk.submitOrder(order, signature)

Integrator fee (dev-portal partners)

When the limit-orders API returns integrator fee fields from /fee-info, Sdk.createOrder automatically:

  • embeds IntegratorFee from integratorFeeBps, integratorFeeReceiver, and integratorFeeSharePercent
  • sets the salt track code from source (only when orderInfo.salt is omitted)

integratorFeeSharePercent is a whole-number percent (the API rejects fractional rev-share). If fee bps is set but share is missing, the SDK treats the response as malformed and uses a zero integrator fee. Pass extra.integratorFee to override. Integrators without fee config are unaffected — orders keep a zero integrator fee as before.

Source / track code: the API returns bare hex (e.g. aba10994), not 0x-prefixed. setSource keccak-hashes non-0x values into the salt track bits — do not “fix” the API value by adding 0x, or attribution will break. If you pass an explicit orderInfo.salt, the API source is not applied; your salt’s track bits win.

Bps.fromPercent / fromSharePercent: values with more than 2 decimal places are truncated to 1 bps precision (e.g. 0.125 → 12 bps) instead of throwing. Negative values are rejected.

RFQ Order creation

RfqOrder is a light, gas efficient version of LimitOrder, but it does not support multiple fills and extension Mainly used by market makers

import {RfqOrder, Address, randBigInt} from "@1inch/limit-order-sdk"
import {UINT_40_MAX} from "@1inch/byte-utils"
import {Wallet} from 'ethers'

// it is a well-known test private key, do not use it in production
const privKey =
    '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'

const maker = new Wallet(privKey)
const expiresIn = 120n // 2m
const expiration = BigInt(Math.floor(Date.now() / 1000)) + expiresIn

const order = new RfqOrder({
    makerAsset: new Address('0xdac17f958d2ee523a2206206994597c13d831ec7'),
    takerAsset: new Address('0x111111111117dc0aa78b770fa6a738034120c302'),
    makingAmount: 100_000000n, // 100 USDT
    takingAmount: 10_00000000000000000n, // 10 1INCH
    maker: new Address(maker.address)
}, {
    allowedSender: new Address('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'),
    expiration,
    nonce: randBigInt(UINT_40_MAX),
})

const typedData = order.getTypedData()
const signature = await maker.signTypedData(
    typedData.domain,
    {Order: typedData.types.Order},
    typedData.message
)

Order filling

To execute a limit order, you need to build the appropriate calldata using the LimitOrderContract class. There are different methods depending on whether the order has extensions/interactions and whether the maker is an EOA or smart contract.

import {LimitOrderContract, TakerTraits, LimitOrder} from '@1inch/limit-order-sdk'

// Assume you already have a FusionOrder in `order` variable
const order = ... // your LimitOrder instance
const signature = '0x...' // order signature
const amount = 100_000000n // amount to fill (in taker or maker asset units, based on set `AmountMode`)

// Create taker traits
const takerTraits = TakerTraits.default()
    // Optional: Configure taker preferences
    // .setAmountMode(AmountMode.TakerAmount) // or MakerAmount
    // .enableNativeUnwrap() // for WETH -> ETH conversion
    // .setAmountThreshold(minAmount) // minimum/maximum acceptable amount (based on set `AmountMode`)

// Build calldata for different scenarios:

// 1. Simple order fill (no extensions, no interactions)
const simpleCalldata = LimitOrderContract.getFillOrderCalldata(
    order.build(), // converts to LimitOrderV4Struct
    signature,
    takerTraits,
    amount
)

// 2. Fill contract order (when maker is a smart contract)
const contractCalldata = LimitOrderContract.getFillContractOrderCalldata(
    order.build(),
    signature,
    takerTraits,
    amount
)

// 3. Order with extensions or taker interactions
const extension = '0x...' // extension calldata
const interaction = '0x...' // taker interaction calldata

const takerTraitsWithArgs = TakerTraits.default()
    .setExtension(extension)
    .setInteraction(interaction)
    // .setReceiver(receiverAddress) // optional: custom receiver

const argsCalldata = LimitOrderContract.getFillOrderArgsCalldata(
    order.build(),
    signature,
    takerTraitsWithArgs,
    amount
)

// 4. Contract order with extensions/interactions
const contractArgsCalldata = LimitOrderContract.getFillContractOrderArgsCalldata(
    order.build(),
    signature,
    takerTraitsWithArgs,
    amount
)

The calldata should be sent to the 1inch Limit Order Protocol contract addresses can be found at docs

Taker Traits Configuration

TakerTraits allows you to configure various execution parameters:

import {TakerTraits, AmountMode, Address} from '@1inch/limit-order-sdk'

const takerTraits = TakerTraits.default()
    // Set amount calculation mode
    .setAmountMode(AmountMode.MakerAmount) // fill by maker amount
    // or
    // .setAmountMode(AmountMode.TakerAmount) // fill by taker amount (default)

    // Enable WETH unwrapping to ETH
    .enableNativeUnwrap()

    // Skip order permit (if not needed)
    .skipOrderPermit()

    // Enable Permit2
    .enablePermit2()

    // Set amount threshold
    .setAmountThreshold(1000000n)

    // Set custom receiver for maker assets
    .setReceiver(new Address('0x...'))

    // Add extension calldata (for order extensions)
    .setExtension('0x...')

    // Add taker interaction calldata
    .setInteraction('0x...')

Choosing the Right Fill Method

Method Use When
getFillOrderCalldata EOA maker, no extensions/interactions
getFillContractOrderCalldata Smart contract maker, no extensions/interactions
getFillOrderArgsCalldata EOA maker, with extensions/interactions
getFillContractOrderArgsCalldata Smart contract maker, with extensions/interactions

API

import {Api, FetchProviderConnector, LimitOrder, HttpProviderConnector} from '@1inch/limit-order-sdk'

const networkId = 1 // ethereum
const api = new Api({
    networkId,
    authKey: 'key', // get it at https://portal.1inch.dev/
    httpConnector: new FetchProviderConnector() // or use any connector which implements `HttpProviderConnector`
})

// submit order
const order = ... /// see `Order creation` section
const signature = '0x'
await api.submitOrder(order, signature)

// get order by hash
const orderHash = order.getOrderHash(networkId)
const orderInfo = await api.getOrderByHash(orderHash)

// get orders by maker with cursor pagination
import { CursorPager } from '@1inch/limit-order-sdk'

// First page - no cursor needed
const firstPager = new CursorPager({ limit: 10 })
const response = await api.getOrdersByMaker(order.maker, {
    pager: firstPager,
    statuses: [1] // only valid orders
})

const orders = response.items
const { hasMore, nextCursor, count } = response.meta

// Get next page if available
if (hasMore && nextCursor) {
    const nextPager = new CursorPager({ limit: 10, cursor: nextCursor })
    const nextPage = await api.getOrdersByMaker(order.maker, {
        pager: nextPager,
        statuses: [1]
    })
}

// Or use without explicit pager (defaults to limit: 100, no cursor)
const makerOrders = await api.getOrdersByMaker(order.maker, {
    statuses: [1]
})

// get all orders from orderbook with pagination
const allOrdersPager = new CursorPager({ limit: 20 })
const allOrdersResponse = await api.getAllOrders({
    pager: allOrdersPager,
    statuses: [1, 2] // valid and temporarily invalid orders
})

// pagination works the same way
if (allOrdersResponse.meta.hasMore && allOrdersResponse.meta.nextCursor) {
    const nextAllOrders = await api.getAllOrders({
        pager: new CursorPager({ 
            limit: 20, 
            cursor: allOrdersResponse.meta.nextCursor 
        })
    })
}

With axios as http provider

axios package should be installed

import {Api, LimitOrder} from "@1inch/limit-order-sdk"
import {AxiosProviderConnector} from '@1inch/limit-order-sdk/axios'

const api = new Api({
    networkId: 1, // ethereum
    authKey: 'key', // get it at https://portal.1inch.dev/
    httpConnector: new AxiosProviderConnector()
})

Testing

Unit tests

Install dependencies

pnpm install

Run tests

pnpm test

Integration tests

Integration tests are inside tests folder. They use foundry fork node and execute transaction on it.

Install dependencies

pnpm install && forge install

Run tests

pnpm test:integration

About

No description, website, or topics provided.

Resources

Stars

13 stars

Watchers

14 watching

Forks

Releases

Packages

Used by

Contributors

Languages