⚙️
Native Developers
  • Native V1
    • Overview
    • Operations
      • Single-Hop Transaction: Off-chain Pricing
      • Single-Hop Transaction: On-chain Pricing
      • Multi-Hop Transaction
      • L1 Token Swap
      • Creating a Pool
      • Updating a Pool
    • System Components
      • GUI
      • Native Backend
      • Pricer
      • Registry
      • Signer
      • Pool
      • Pool Factory
      • Treasury
      • Router
    • Smart Contracts
      • Core
        • Registry
        • NativePool
        • NativePoolFactory
        • NativeRouter
      • Liquidity Pools
    • Contract Address
    • API References
      • GET /v1/indicative-quote
      • GET /v1/orderbook
      • GET v1/firm-quote
      • GET v1/firm-quote/calldata
    • Guide
      • Get quote for ETH to USDT
      • Execute ETH to USDT swap on Native
    • Routing
  • Market Maker Integration
    • WebSocket Connection
Powered by GitBook
On this page
  • Overview
  • Endpoints for pricer and signer
  • Authentication (optional)
  • Orderbook Endpoint
  • Firm quote Endpoint
  • Sign quote Endpoint
  • Pool Setup

Market Maker Integration

Off-chain pricing is Native's biggest strength. We collaborate with diverse liquidity sources to provide the best pricing. This document outlines the technical setup to integrate as market maker.

Overview

Native's architecture is built upon three essential components: pricer, signer, and pool, within a secure liquidity network, enabling secure off-chain pricing.

  • The pricer ensures accurate price provision for order requests.

  • The signer safeguards order data for on-chain transactions.

  • The pool executes on-chain swaps using self-custody treasury.

When users request pricing on Native, Native's backend navigates various liquidity sources to discover the best price. Upon user confirmation to execute a swap, the market maker is engaged to acquire the final price, securely endorsed by the assigned signer. Signer gives market makers an additional layer to validate an order and control of the transaction can be executed by the pool smart contract.

Endpoints for pricer and signer

Native relies on API connections to the market makers to achieve off-chain pricing. HTTPS protocol and authentication are required for all the endpoints. Native supports authentication methods including API key and HMAC-SHA256. All endpoints should return JSON data and appropriate status codes.

In general, 3 API endpoints are required to integrate as a market maker with Native.

  1. orderbook for Native to estimate the price for different order sizes

  2. firm-quote for Native to obtain price from market maker given the requested token and amount

  3. sign-quote for market maker to validate and sign the order to be executed on-chain

Authentication (optional)

By default, Native will pass the api_key in the header of every request for you to authenticate that the request is coming from Native. You can get the api_key by reaching out to the Native team.

Furthermore, Native allows for a flexible authentication method. If you have a different authentication method, reach out to our team so we can build a specialized interface for you.

Orderbook Endpoint

GET <market_maker_base_url>/orderbook

This endpoint is queried regularly to estimate the available liquidity and quote for different order sizes.

Params

Name
Description

chainId

Chain ID of the network. In integer. eg: 1 for Ethereum, 56 for BSC.

Response

The orderbook is "non-cumulative" in manner.

[
    {
        "base_symbol": "USDT",
        "base_address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
        "quote_symbol": "AAVE",
        "quote_address": "0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9",
        "levels": [
            [
                278.6807021305324,
                0.016506345666681244
            ],
            [
                2883.2639932781804,
                0.0165090675397643
            ],
            // ... more price levels
        ],
        "side": "ask",
        "minimum_in_base": 0.0001
    },
    {
        "base_symbol": "AAVE",
        "base_address": "0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9",
        "quote_symbol": "USDT",
        "quote_address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
        "levels": [
            [
                4.6,
                60.58276133272444
            ],
            [
                47.6,
                60.572772968029
            ],
            // ... more price levels
        ],
        "side": "bid",
        "minimum_in_base": 0.0001
    }
    // ... more token pairs
]

Firm quote Endpoint

GET <market_maker_base_url>/firm-quote

This endpoint provides amount of token out the market maker will quote given the requested token and token amount. The parameters will be encoded to url when Native send this request to the market maker. Params

Name
Description

sellerTokenAmount

The token input amount of the order. In wei.

chainId

Chain ID of the network. In integer. eg: 1 for Ethereum, 56 for BSC.

sellerToken

The ERC20 token address will be sent to market maker.

buyerToken

The ERC20 token address will receive from market maker.

pool

Native pool contract address that will execute this order. Additional data for market maker to do validation or differential pricing.

seller

The address that will send seller token to market maker. (NativeRouter in most cases)

quoteId

Unique ID for this order request in UUID v5.

feeBps

For some order flows Native receives, the initiator (Aggregator) requests a fee. Native will just pass the fee param over to market makers (in bps). Market makers are supposed to factor the fee into the quote.

Native will work with market makers to coordinate the tracking and payment of fees, off-chain. Native will sign contracts with market makers, specifying how fees are set and paid. We will also provide data sources for market makers to monitor the fee owed.

Example

<market_maker_base_url>/firm-quote?sellerTokenAmount=10000000000000000000000&chainId=97&sellerToken=0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d&buyerToken=0x55d398326f99059ff775485246999027b3197955&pool=0xaaE854bdd940cf402d79e8051DC7E3390e32A3ac&seller=0x7d1F5C43998570629f5d00134321fB6a95451ec3&quoteId=62716-206e-41cb-8559-013f1ed1a65a&feeBps=12

In this example, a user sells 10,000 USDC to market maker in exchange for USDT on Binance Smart Chain (BSC).

Response

{
  "success": true,
  "buyerTokenAmount": "10100000000000000000000",
  "deadlineTimestamp": "1671086729", // will be passed to sign-quote
  "auth": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" // (optional) will be passed to sign-quote
}

In the response above, market maker returns 10,100 USDT for the order sent in the example.

Sign quote Endpoint

POST <base-url>/sign-quote

This endpoints provide amount of token out the market maker will quote given the requested token and token amount. The parameters will be encoded to url when Native send this request to the market maker. Params

Name
Description

nonce

ID to prevent transaction to be executed more than once. Incremental for each transaction by txOrigin and pool.

signer

Public address of the signer that will sign this order

seller

The address that will send seller token to market maker.

buyer

Native pool contract address that will execute this order.

sellerTokenAmount

The token input amount of the order. In wei.

buyerTokenAmount

The token output amount of the order. In wei. Can be modified by the signer to determine the final output amount.

sellerToken

The ERC20 token address will be sent to market maker.

buyerToken

The ERC20 token address will receive from market maker.

chainId

Chain ID of the network. In integer. eg: 1 for Ethereum, 56 for BSC.

deadlineTimestamp

The expiration time of the order, in block timestamp. Can be modified by the signer to determine the expiration time.

txOrigin

Address of the trader who initiated the order.

quoteId

Unique ID for this order request in UUID v5.

auth

The auth string received from get-quote. Can be used by the signer to verify the authenticity of the quote data. Signer and pricer can decide what auth protocol to use. Native just relay the auth string from pricer to signer.

Example body

{
    "nonce": 0,
    "signer": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
    "buyer": "0xaaE854bdd940cf402d79e8051DC7E3390e32A3ac",
    "seller": "0x7d1F5C43998570629f5d00134321fB6a95451ec3",
    "buyerToken": "0x55d398326f99059ff775485246999027b3197955",
    "sellerToken": "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d",
    "buyerTokenAmount": "10100000000000000000000", // from firm-quote
    "sellerTokenAmount": "10000000000000000000000",
    "deadlineTimestamp": "1671086729", // from firm-quote
    "chainId": 56,
    "caller": "0x7d1F5C43998570629f5d00134321fB6a95451ec3",
    "auth": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", // from firm-quote
    "quoteId": "62716-206e-41cb-8559-013f1ed1a65a"
}

In this example, a user requests to sign an order of 10,000 USDC to 10,100 USDT on Binance Smart Chain (BSC) to be transacted on market maker's Native pool (0xaaE854bdd940cf402d79e8051DC7E3390e32A3ac). The order have to be signed by the signer (0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266) to be successfully executed on-chain.

Order signing

async function signOrderNative(data) {
    const wallet = new ethers.Wallet(privateKey, provider);
    const order = {
        id: data.nonce,
        signer: data.signer,
        buyer: data.buyer,
        seller: data.seller,
        buyerToken: data.buyerToken,
        sellerToken: data.sellerToken,
        buyerTokenAmount: data.buyerTokenAmount,
        sellerTokenAmount: data.sellerTokenAmount,
        deadlineTimestamp: data.deadlineTimestamp,
        txOrigin: data.txOrigin,
        quoteId: data.quoteId
    };
    const signature = await generateSignature(order, wallet, data.buyer, data.chainId);

    return {
        success: true,
        signature: signature,
        order: order
    };
}

async function generateSignature(data, wallet, nativePool, chainId){
    const signatureData = {
        types: {
            Order: [
                { name: "id", type: "uint256" },
                { name: "signer", type: "address" },
                { name: "buyer", type: "address" },
                { name: "seller", type: "address" },
                { name: "buyerToken", type: "address" },
                { name: "sellerToken", type: "address" },
                { name: "buyerTokenAmount", type: "uint256" },
                { name: "sellerTokenAmount", type: "uint256" },
                { name: "deadlineTimestamp", type: "uint256" },
                { name: "txOrigin", type: "address" },
                { name: "quoteId", type: "bytes16" }
            ],
        },
        primaryType: 'Order',
        domain: {
            name: "native pool",
            version: "1",
            verifyingContract: nativePool,
            chainId
        },
        message: {
            id: order.id,
            signer: order.signer,
            buyer: order.buyer,
            seller: order.seller,
            buyerToken: order.buyerToken,
            sellerToken: order.sellerToken,
            buyerTokenAmount: order.buyerTokenAmount,
            sellerTokenAmount: order.sellerTokenAmount,
            deadlineTimestamp: order.deadlineTimestamp,
            txOrigin: order.txOrigin,
            quoteId: Buffer.from(order.quoteId.replace(/-/g, ""), "hex")
        },
    }

    const signature = await userSigner._signTypedData(signatureData.domain, signatureData.types, signatureData.message);
    return signature;
}

Response

{
  "success": true,
  "signature": "0x0000000000000000000000000000000000000000000000000000000000000001f39fd6e51aad88f6f4ce6ab8827279cfffb92266bbf1de53900b78962f06efbb3a946c2dc5f9bf7eb5c489b32ff2111574c54339e147dde37a8fdc89f641e24cda0685aabe2bb88ef14d085cc3d7613452705086ae19feaef97ab0f3905a360857b8219900000000000000000000000000000000000000000000000001168fa91b8c800000000000000000000000000000000000000000000000000001195ffafa36000000000000000000000000000000000000000000000000000000000000639ac289d2d3051ca7b408bfc32478164a9d838a0a80f9d98120ae0902956fe0ea4af11fb550d9294d809864f3fe0f210ad4e45626dfcd9745b705708f06535f2248460fec2f927f1686994f4a2c4b81da367bcd0991f2001c",
  "order": {
    "id": 1,
    "signer": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
    "buyer": "0xaaE854bdd940cf402d79e8051DC7E3390e32A3ac",
    "seller": "0x7d1F5C43998570629f5d00134321fB6a95451ec3",
    "buyerToken": "0x55d398326f99059ff775485246999027b3197955",
    "sellerToken": "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d",
    "buyerTokenAmount": "10100000000000000000000",
    "sellerTokenAmount": "10000000000000000000000",
    "deadlineTimestamp": "1671086729",
    "caller": "0x7d1F5C43998570629f5d00134321fB6a95451ec3", // same as seller
    "quoteId": "62716-206e-41cb-8559-013f1ed1a65a"
  }
}

In the response above, market maker returns the signature generated and the corresponding order object to Native. With the signature customer can submit the transaction to execute the swap.

Pool Setup

Currently, Native User Interface does not support the creation of Market Maker Pool. However, you can reach out to the Native team so we can create the pool and transfer the ownership to you. You just need to provide this information:

  1. Pool Name: The name you'd like to give your pool.

  2. Chain and list of token addresses: The chain and the allowed tokens to be traded in the pool

  3. Owner address: the owner of the pool where we will transfer the ownership to

  4. Treasury address: the address where the fund is stored. Ideally, this should be the same as the owner address

  5. Signer address: The key you'll use to sign quotes off-chain for your market maker. Put the public key (address) here and ensure the private key is stored securely. You'll need to be able to programmatically sign quotes with the private key later on (in the signQuote API).

To fund the market maker pool, you will need an External Account (EoA or Smart Contract) that needs to give token allowance to the pool. Note that this account address must be the same as the treasury address specified in the pool creation. With this method, you do not need to lock funds into a pool and still have access to all the assets.

PreviousRoutingNextWebSocket Connection

Last updated 1 year ago

Native utilises signature to ensure the order is endorsed only by the assigned signer. The inputs required to sign the order corresponds to the data sent in POST sign-quote above. The code snippet below generates bytes encoded in hexdecimal to be returned in response for the POST /sign-quote request.

After the pool is created, you can access the pool details and get the API key from our dashboard ().

EIP-712
app.native.org