Mangrove
Developper
Developper
  • Welcome
  • Protocol
    • Introduction
    • Technical References
      • Overview
      • Ticks, ratios, and prices
      • Offer-list
        • Views on offers
      • Market-order
        • Delegation
      • Creating & Updating offers
        • Maker contract
        • Offer provisions
        • Gas requirement
        • Public data structures
        • Executing offers
      • Cleaning offers
      • Governance-parameters
        • Global variables
        • Local variables
        • Data structures and views
      • Periphery Contracts
        • MgvReader
        • MgvOracle
      • Literate Source Code
    • Background
      • Taking available liquidity
      • Making liquidity available
      • Reneging on offers
  • Strat Lib
    • What is the Strat Library?
    • Getting-started
      • Set Up Your Local Environment
      • Post a Smart Offer
    • Guides
      • Unlocking liquidity
      • Reposting an offer in the posthook
      • Using last look to renege trades
      • Determining gas requirements
      • Creating a Direct contract
      • Deploying your contract
      • Testing a maker contract
      • Safe offer logic guidelines
      • Approvals
    • Technical references
      • Principal hooks
      • Liquidity routing
      • API preferences
        • Core
          • SRC
            • IMangrove
        • Strats
          • SRC
            • Strategies
              • MangroveOffer
              • MangroveOrder
              • Integrations
                • AaveV3Borrower
                • AaveV3BorrowerImplementation
                • AaveV3BorrowerStorage
                • AaveV3Lender
                • CompoundModule
              • Interfaces
                • IForwarder
                • ILiquidityProvider
                • IOfferLogic
                • IOrderLogic
              • Offer_forwarder
                • Abstract
                  • Forwarder
              • Offer_maker
                • Abstract
                  • Direct
                • Market_making
                  • Kandel
                    • AaveKandel
                    • AaveKandelSeeder
                    • KandelSeeder
                    • Abstract
                      • AbstractKandelSeeder
                      • CoreKandel
                      • DirectWithBidsAndAsksDistribution
                      • GeometricKandel
                      • HasIndexedBidsAndAsks
                      • KandelLib
                      • TradesBaseQuotePair
              • Routeurs
                • SimpleRouter
                • Abstract
                  • AbstractRouter
                • Integrations
                  • AavePooledRouter
                  • HasAaveBalanceMemoizer
              • Utils
                • AccessControlled
              • Vendor
                • AAVE
                  • V3
                    • Contracts
                      • Dependencies
                        • Oppenzeppelin
                          • Contracts
                            • IERC20
                      • Interfaces
                        • IAToken
                        • IAaveIncentivesController
                        • IAaveOracle
                        • ICreditDelegationToken
                        • IInitializableAToken
                        • IPool
                        • IPoolAddressesProvider
                        • IPriceOracleGetter
                        • IScaledBalanceToken
                      • Protocol
                        • Libraries
                          • Configurations
                            • ReserveConfiguration
                          • Helpers
                            • Errors
                          • Types
                            • DataTypes
                    • Periphery
                      • Contracts
                        • MISC
                          • Interfaces
                            • IEACAggregatorProxy
                        • Rewards
                          • Interfaces
                            • IRewardsController
                            • IRewardsDistributor
                            • ITransferStrategyBase
                          • Libraries
                            • RewardsDataTypes
                • Compound
                  • CarefulMath
                  • Exponential
                  • ExponentialNoError
                  • ICompound
    • Background
      • Building Blocks
        • MangroveOffer
        • Direct
        • Forwarder
  • Vaults
    • Understanding vaults
      • Oracles
    • Managing a vault (CLI)
      • Deploying an oracle
      • Creating a vault
      • Monitoring the vault
      • Setting the vault position
      • Setting the fee data
      • Rebalancing
      • Adding or removing liquidity
    • Custom interactions
      • Oracles
      • Vault Factory
      • Managing a vault
        • Setting the position
        • Rebalancing
        • Setting a manager
        • Setting fee
  • Keeper Bots
    • Keeper Bots
    • Guides
      • Using borrowed funds for cleaning
    • Backgroud
      • The role of cleaning bots in Mangrove
      • The role of gas price updater bots in Mangrove
  • Adresses
    • Deployment Addresses
  • Quick Links
    • Glossary
    • Website
    • Whitepaper
Powered by GitBook
On this page
  • Decimals, raw values, and user representations
  • Converting a price to a tick
  • tickSpacing: Markets with bigger price increments
  • Tick, ratio, and price ranges
  • The TickLib library
  • Comparison to Uniswap ticks
  1. Protocol
  2. Technical References

Ticks, ratios, and prices

PreviousOverviewNextOffer-list

Last updated 28 days ago

On Mangrove, the price space of a market is discretized into ticks: It contains the price 1 quote/base and the smallest price increment (and decrement) is 1 basis point = 0.01%.

Formally, a tick∈Ztick∈Ztick∈Ztick∈Ztick∈Ztick∈Z corresponds to the 1.0001tick=wants/gives1.0001tick=wants/gives1.0001tick=wants/gives1.0001tick=wants/gives1.0001tick=wants/gives1.0001tick=wants/gives between the amount givesgivesgivesgivesgivesgives promised by an offer and the amount wantswantswantswantswantswants it requests. This corresponds to a in the following way:

priceside∈{asks,bids}(tick)={ratio=wants/gives=1.0001tickif side≡asks1/ratio=gives/wants=1.0001−tickif side≡bids.price_{side \in \{asks, bids\}}(tick) = \begin{cases} ratio = wants/gives = 1.0001^{tick} &\text{if } side \equiv asks \\ 1/ratio = gives/wants = 1.0001^{-tick} &\text{if } side \equiv bids. \end{cases}priceside∈{asks,bids}​(tick)={ratio=wants/gives=1.0001tick1/ratio=gives/wants=1.0001−tick​if side≡asksif side≡bids.​

Beware decimals! As always when ERC-20 tokens are involved, careful handling of decimals is essential! Both in code and in documentation.

In the formulae on this page, we only consider raw values, i.e. token decimals are disregarded. On-chain, Mangrove does the same.

As an important example of the implications, note that while the price space contains the raw price 1, it may not contain the user readable price 1. This is likely if base and quote have different numbers of decimals.

The next section discusses this in detail and explains how to handle decimals, including how to convert raw ratios and raw prices to their user representations.

Note that the relationship between ratio and price depends on which side of the book you are looking at:

  • On the asks side, it's fairly natural: The price is the ratio $wants/gives$ between the amount $gives$ of base tokens promised by an offer and the amount $wants$ of quote tokens it requests.

  • On the bids side, however, the price is the reciprocal of the $wants/gives$ ratio, i.e. price = $gives/wants$. This is because a bid wants base tokens and offers quote tokens and thus the ratio $wants/gives$ is in base/quote.

Example : On a WETH/DAI market:

  • The price would be expressed in DAI per WETH (DAI/WETH)

    • Eg. 2,224 DAI/WETH

  • On the asks side WETH-DAI (selling WETH or buying DAI), the ratio equals the price and has unit DAI/WETH

    • Eg. 2,224 DAI/WETH

  • On the bids side DAI-WETH (buying WETH or selling DAI), the ratio equals 1/price and has unit WETH/DAI.

    • Eg. 0.0004496 WETH/DAI

Remember : We often casually use the word ’price’ to refer to the ratio between the amount promised by an offer and the amount it requests. In the code however, we use the generic word ’ratio’ to avoid confusion with the normal notion of market price expressed in ’quote’ tokens per ‘base’ token.

Decimals, raw values, and user representations

On-chain, ERC-20 amounts are integers. But when presented to users, ERC-20 tokens typically have a number of decimals, such that fractional amounts can be expressed.

The number of decimals is specified by the ERC-20 contract and differs between different ERC-20 tokens.

Examples of ERC-20 amounts in raw and user representation : Here are some ERC-20 tokens and example amounts in both the on-chain (so-called 'raw') format and their user representation.

ERC-20
Decimals
Raw amount example
User representation

USDC

6

87654321

87.654321

DAI

18

876543210987654321

0.876543210987654321

WETH

18

109876543210987654321

109.876543210987654321

:::

When tokens have different numbers of decimals, their raw amounts are not easily relatable. The USD stable coins USDC and DAI are a good example illustrated in the example above: The amounts are in principle directly relatable as they denote a USD amount. But it's not obvious from the raw amounts, which of them represents the larger USD amount.

It becomes even more tricky on markets, where we need to express ratios and prices. When the base and quote tokens have different numbers of decimals, raw ratios and raw prices become non-intuitive.

Here are some examples of what this can look like:

Market
Decimals
Ask user ratio example (quote/base)
Ask ratio (raw)
Bid user ratio example (base/quote)
Bid ratio (raw)

DAI/USDC

DAI: 18 USDC: 6

$1$

$0.000000000001$ $= 1 * 10^{-12}$ $= 1 * 10^{6}/10^{18}$

$1$

$1,000,000,000,000$ $= 1 * 10^{12}$ $= 1 * 10^{18}/10^{6}$

WETH/DAI

WETH: 18 DAI: 18

$2,224$

$2,224$ $= 2,224 * 10^{0}$ $= 2,224 * 10^{18}/10^{18}$

$0.0004410$

$0.0004410$ $= 0.0004410 * 10^{0}$ $= 0.0004410 * 10^{18}/10^{18}$

On-chain, Mangrove only works with raw token amounts and ratios between them. And the relationsships between ticks, ratios, and prices described on this page hold for raw token amounts.

The formulae for converting between raw ratios&prices and their user representations are the following:

userRepresentation(raw_ratio)=raw_ratio∗10outbound_decimals−inbound_decimalsraw(user_ratio)=user_ratio/10outbound_decimals−inbound_decimalsuserRepresentation(raw_price)=raw_price∗10base_decimals−quote_decimalsraw(user_price)=user_price/10base_decimals−quote_decimals\def\arraystretch{1.5} \begin{array}{rcl} userRepresentation(raw\_ratio) & = & raw\_ratio * 10^{outbound\_decimals - inbound\_decimals} \\ raw(user\_ratio) & = & user\_ratio / 10^{outbound\_decimals - inbound\_decimals} \\ userRepresentation(raw\_price) & = & raw\_price * 10^{base\_decimals - quote\_decimals} \\ raw(user\_price) & = & user\_price / 10^{base\_decimals - quote\_decimals} \end{array}userRepresentation(raw_ratio)raw(user_ratio)userRepresentation(raw_price)raw(user_price)​====​raw_ratio∗10outbound_decimals−inbound_decimalsuser_ratio/10outbound_decimals−inbound_decimalsraw_price∗10base_decimals−quote_decimalsuser_price/10base_decimals−quote_decimals​

Converting a price to a tick

Since the price space is discretized, not all prices and ratios can be represented exactly. Converting a price or ratio to a tick may therefore require rounding and one should take care to round appropriately; We'll discuss this in detail below.

First, calculate an exact but possibly non-representable tick' which may have a real component:

tickside∈{asks,bids}′(price)={log1.0001(price)if side≡asks−log1.0001(price)if side≡bids.tick′(ratio)=log1.0001(ratio)\begin{array}{rcl} tick'_{side \in \{asks, bids\}}(price) & = & \begin{cases} log_{1.0001}(price) &\text{if } side \equiv asks \\ -log_{1.0001}(price) &\text{if } side \equiv bids. \end{cases} \\ \\ tick'(ratio) & = & log_{1.0001}(ratio) \end{array}tickside∈{asks,bids}′​(price)tick′(ratio)​==​{log1.0001​(price)−log1.0001​(price)​if side≡asksif side≡bids.​log1.0001​(ratio)​

Next, round this value to an integer to get a representable tick. One will typically want to round down or up depending on whether the price or ratio is used in a maker or taker context:

Role
Tick rounding
Explanation

Taker

Round down

Takers specify a max price when buying and min price when selling. In both cases, this price corresponds to a max ratio they're willing to accept. Rounding down the tick corresponds to choosing the tick closest to but not exceeding that ratio. This ensures the taker doesn't get a worse price than specified.

Maker

Round up

Makers specify the price they're willing to accept. This price corresponds to a ratio as discussed above. Rounding up the tick corresponds to choosing the tick closest to but not below that ratio. This ensures the maker doesn't receive too little.

tickSpacing: Markets with bigger price increments

Mangrove allows markets to require bigger price increments than 0.01%. This is achieved through use of the tickSpacing parameter which restricts the allowed ticks to multiples of tickSpacing: Only ticks where tick % tickSpacing == 0 are valid on a market.

This means that the price increments are 1.0001tickSpacing−11.0001tickSpacing−11.0001tickSpacing−11.0001tickSpacing−11.0001tickSpacing−11.0001tickSpacing−1.

Example : For a market with tickSpacing = 5, the allowed ticks are ..., -15, -10, -5, 0, 5, 10, 15, ... and the price increments are 1.00015−1=0.0005001=0,050011.00015−1=0.0005001=0,05001%1.00015−1=0.0005001=0,05001%1.00015−1=0.0005001=0,05001.

Tick, ratio, and price ranges

The supported ranges for ticks, ratios, and prices in Mangrove are:

Value
Min
Max

Tick

-887272

887272

Ratio

2.9e-39

3.4e38

Price

2.9e-39

3.4e38

The TickLib library

It also contains functions for calculations involving ticks and ratios, such as computing the inbound volume corresponding to a tick and outbound volume.

Comparison to Uniswap ticks

Mangrove's ticks are inspired by Uniswap’s tick, with the following notable differences:

  • Directly computing ticks base 1.0001 (not base sqrt(1.0001))

  • Directly computing ratios (not sqrt(ratio) - it makes the code simpler when dealing with actual ratios and logs of ratios)

  • Ratios are floating-point numbers, not fixed-point numbers (it increases the precision when computing amounts).

The library contains utility functions for converting between ticks and ratios.

ratio
price
TickLib