reservoir-labs / oracle

GNU General Public License v3.0
0 stars 0 forks source link

feat: price oracle #2

Closed xenide closed 5 months ago

xenide commented 7 months ago

On the topic of routing, I think we need to be supporting prices from multiple hops (referred to as composite prices from now on) as our AMM might not have direct pairs for all of them.

Take for example we want to know the price of DAI/EUR, but there is no such direct pair, but only the following pairs:

We can have a mapping of price routes, which needs to be actively maintained by governance. Like the priceCache mapping, it's also sorted, with the lower token being token0. The implications are that:

mapping(address tokenA => mapping(address tokenB => address[] path)) public routes;

routes[DAI][USDC] == [ 0xdai, 0xusdc]
routes[USDC][EURC] == [ 0xusdc, 0xeurc ]
routes[DAI][EURC] == [ 0xdai, 0xusdc, 0xeurc ]

getQuote will then first go look for the DAI/EURC price in routes. It sees that DAI/EURC is a composite route as it has more than 2 items in the array. It iterates starting from the first pair (DAI/USDC) It sees that there is a route defined for DAI/USDC, and that it is a direct route, and looks it up on priceCache. If it's not a direct route (i.e. composite route), then we recursively go calculate this route first and wait for return. Subsequently the function does the same for obtaining the USDC/EURC price. The function needs to sort the tokens in order before looking up to find the route as the route is also stored in sequence.

We can limit the number of hops to any arbitrary number as needed (e.g. 3).

Similarly, for the inverse route, the function will first sort the tokens in the sequence. Then multiply and invert accordingly.

E.g. query of EURC/DAI

getQuote(123, EURC, DAI)

(token0, token1) = (DAI, EURC) 

route[DAI][EURC] == [ 0xdai, 0xusdc, 0xeurc ]

If there is no route for a given pair defined by the priceCache, the PriceCache will return a NoRoute() error

xenide commented 6 months ago

Remaining questions:

  1. do we need a max price limit?
    1. minimum is already 1 wei. We need it as a denominator for division. Will cause the div by 0 problem otherwise
    2. Due to the mul operation on this line, the max price limit should be around uint196.
    3. recommendation:
      1. Limit price to type(uint128).max
  2. do we need a max amount in limit?
    1. given our the robust multiplication + using fullMulDiv, it's almost impossible to overflow given any real prices + amounts
    2. recommendation:
      1. Limit amount in to type(uint128).max

Setting both limits to type(uint128).max will ensure that calculations will never revert with fullMulDiv.