Skip to Content

LP-API Types

The parameters list of any JSONRPC call can be encoded as a JSON array or as an object. The advantage of the latter is that it’s more explicit and allows for optional values to be omitted.

Addresses

Addresses should be encoded according to their host chains:

  • Ethereum and Arbitrum addresses should be encoded as Hex strings, for example "0xfa36e03defc6e4d140cc61fcaab9d1fbef18642f".
  • Polkadot addresses can be encoded using SS58 or Hex strings, for example: "13zyEWmmLDx63Y99TL9SkxBe1DqPVCrcjXytxM3ZHGRyEJV5" or "0x84aec0876dbb3cb7391eeded2eef5fbcf0d1a34f7c9f86f9af205f944b461761"
  • Bitcoin addresses should be encoded using the appropriate bitcoin standard for the address type. For example "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx" is a valid bech32 address on Bitcoin testnet.

Amount

An integer encoded as a big-endian hex string, between 0 and 2^128 - 1, representing a quantity of a token in it’s smallest unit.

For example, ETH uses 18 decimal places, so 100ETH is 100,000,000,000,000,000,000 Units, which encoded as hex is "0x56bc75e2d63100000". As another example, USDC uses 6 decimals, so 10USDC is 10,000,000 Units, which encoded as hex is "0x989680".

Liquidity

An integer encoded as a big-endian hex string, between 0 and 2^128 - 1, representing the amount of liquidity in/the size of a range order.

Asset

Assets are specified as a { chain, asset } object, where the chain is as described below, and the asset is an upper-case string. Assets returned from the RPCs will always take the explicit form, for example { chain: "Ethereum", asset: "ETH" }.

Chain

Chains are specified as the full name of the chain, capitalized, for example "Ethereum", "Bitcoin", “Solana".

Order IDs

Order IDs are integers encoded as a big-endian hex string, between 0 and 2^64 - 1.

Order IDs are assigned by the LP, are unique within each pool, order type, side, and account, and can be re-used. Note that two limit orders, one buy and one sell, in the same pool (i.e. ETH-USDC) could simultaneously have the same ID, but two buys or two sells could not.

Depending on the order type you need different information to uniquely identify it:

  • For limit orders, you need the LP who created it, the pool it is in i.e. the base and quote asset of the pool, the side of the limit order, and its Order ID.
  • For range orders, you need the LP who created it, the pool it is in, and its Order Id.

Order Updates

Changes in order size are communicated via an increase_or_decrease object which has this structures for range orders:

{ "increase": <range_order_size> } // OR { "decrease": <range_order_size> }

And this structure for limit orders:

{ "increase": <amount> } // OR for a decrease: { "decrease": <amount> }

These are used when calling the update_limit_order or update_range_order rpcs/extrinsics.

Range order size

Range orders are made up from a ratio of both the base and quote assets. This ratio is determined by the current price of the pool, and must remain constant. Therefore the amounts that are added or removed from a range order must match this ratio.

When describing the size of a range order you can use a liquidity number which is independent of the ratio, and so any liquidity number is valid. As the liquidity number is not an easily understandable concept, alternatively you can specify ranges for the amounts of both assets you want to add or remove or set the order to contain, but this will only be valid if an amount of both assets exists inside the ranges that matches the range order’s required ratio.

For example if the ratio is 1 ETH:50 USDC, and you specify minimums of 10 ETH:2000 USDC, and maximums of 100 ETH:10000 USDC. It will pick 100 ETH:5000 USDC, as these are the largest amounts inside the specified ranges that match the required ratio. But if the maximums where instead 20 ETH:10000 USDC then there is no amount of ETH or USDC inside those ranges that matches the required 1 ETH:50 USDC ratio.

Encoded as JSON, depending on if you want to specify the “size” as amount ranges or liquidity, theses can be encoded like this:

{ "AssetAmounts": { "maximum": { "base": <amount>, "quote": <amount> }, "minimum": { "base": <amount>, "quote": <amount> } } } // OR { "Liquidity": { "liquidity": <liquidity> } }

Tick

An integer between -887272 and 887272 inclusively representing a price. To calculate the nearest tick to a given price in the quote asset, use this formula: log1.0001(price * <QUOTE_ASSET_PRECISION> / <BASE_ASSET_PRECISION>). The tick representation of a price can be calculated by taking the log1.0001 of the price in the quote asset, and rounded down to the nearest integer. A tick can be turned into a price (into the quote asset) with this formula: (1.0001^tick) * <BASE_ASSET_PRECISION> / <QUOTE_ASSET_PRECISION>. <ASSET_PRECISION> for ETH would be 10^18 and for USDC it would be 10^6.

function priceToTick(price, decimals_of_quote_asset, decimals_of_base_asset) { const quote = price * Math.pow(10, decimals_of_quote_asset-decimals_of_base_asset); const tick = Math.round(Math.log(quote) / Math.log(1.0001)); return Math.min(Math.max(tick, -887272), 887272); } function tickToPrice(tick, decimals_of_quote_asset, decimals_of_base_asset) { const quote = Math.pow(1.0001, tick); return quote * Math.pow(10, decimals_of_base_asset-decimals_of_quote_asset); }

Boost Fee

A number of basis points that the depositor is willing to pay to opt-in to boosting their deposit.

If, at the time the deposit is witnessed by the network, boost liquidity at or below the desired boost fee is sufficient to cover the entire deposit, the deposit will be boosted. This means the deposit will be credited after a single confirmation, rather than waiting for the usual safety margin or finality window.

At the time of writing, available boost pools are fixed at 5, 10 and 30 basis points.

Wait For

Some RPCs that submit extrinsics take an optional parameter wait_for, this allows you to control under what condition the RPC will wait for until returning successfully. It can have these values:

  • NoWait: This will cause the RPC to return once the extrinsic has been successfully submitted to the node.
  • InBlock: This will cause the RPC to return once the extrinsic has been included in a block.
  • Finalized: This will cause the RPC to return once the extrinsic has been included in a block that has been finalized. This is the default if the argument is not specified.

This option also affects the data returned by the rpcs, see Wait For Result.

Wait For Result

RPCs that take a wait_for parameter when successful will return a object with one of these structures:

// For "wait_for: NoWait" { "tx_hash": <hex encoded transaction hash of the submitted extrinsic> } // OR for "wait_for: InBlock/Finalized" { "tx_details": { "tx_hash": <hex encoded transaction hash of the submitted extrinsic>, "response": <details of the outcome of the extrinsic, which varies between each RPC>, } }

Fixed Point Number

Fixed point numbers work similarly to integers, except instead of the all the bits representing positive integer powers of 2, the lower bits represent negative integer powers of two. For example if you have a normal 8 bit unsigned integer 01011100, this represents 64 + 16 + 8 + 4 = 92, but if we treat the same binary as an unsigned fixed point number with 4 fractional bits it would represent 4 + 1 + 0.5 + 0.25 = 5.75. This is because in the integer case the bit’s values are [128, 64, 32, 16, 8, 4, 2, 1], but in the fixed point case they are [8, 4, 2, 1, 1/2, 1/4, 1/8, 1/16]. See how there are 4 bits that have fractional/non-integer values.

function numberToFixed(number, fractional_precision) { return Math.round(number * Math.pow(2, fractional_precision)); } function fixedToNumber(fixed_precision_number, fractional_precision) { return fixed_precision_number / Math.pow(2, fractional_precision); }

Hex Price

A big-endian hex string encoded 256 bit unsigned fixed point number, with 128 fractional bits, representing a price as the amount of quote asset a single unit of the base asset is valued at. Note the amounts are in the smallest units of both assets.

For example the price 10000 USDC/ETH represented in this format would be hex(floor((10000 * 10^6 / 10^18) * 2^128)) = "0x2af31dc4611873bf3f70834acd". Note the 10^6 is 1 USDC is USDC’s smallest unit, 10^18 is 1 ETH in ETH’s smallest unit (wei), and the 128 in 2^128 is the number of fractional bits in the price.

function priceToHexPrice(price, decimals_of_quote_asset, decimals_of_base_asset) { let shifted = BigInt(price * 2**128); let hex_price = shifted * 10n**BigInt(decimals_of_quote_asset) / 10n**BigInt(decimals_of_base_asset); let hex_string = hex_price.toString(16); return '0x' + (hex_string.length % 2 ? '0' : '') + hex_string; } function hexPriceToPrice(hex_price, decimals_of_quote_asset, decimals_of_base_asset) { return Number(BigInt(hex_price)) / 2**128 * 10**(decimals_of_base_asset - decimals_of_quote_asset); }

Square Root Price

A big-endian hex string encoded 160 bit unsigned fixed point number, with 96 fractional bits, representing the square root of a Price.

function sqrtPriceToPrice(hex_input, decimals_of_quote_asset, decimals_of_base_asset){ let raw_price = Math.pow(Number(BigInt(hex_input)) / (2**96), 2); return raw_price * (10**(decimals_of_base_asset - decimals_of_quote_asset)); } function priceToSqrtPrice(price, decimals_of_quote_asset, decimals_of_base_asset){ let hex_sqrt = BigInt(Math.sqrt(price / 10**(decimals_of_base_asset - decimals_of_quote_asset)) * 2**96); let hex_string = hex_sqrt.toString(16); return '0x' + (hex_string.length % 2 ? '0' : '') + hex_string; }

Side

The “side” of a limit order indicates if the limit order is buying the pool’s base asset, or is selling the pool’s base asset. It can have two values either "buy" or "sell".

Last updated on