Twaper is a Muon app to get a token price from decentralized exchanges in a way that is secure against price manipulations. It uses the Uniswap TWAP approach with the following extra benefits over the original on-chain implementation:
- It detects and removes outlier prices before calculating averages to prevent price manipulations through applying a sharp rise/fall in the price for a short duration.
- In order to reject unexpected price changes, it applies a fuse mechanism that stops the system when a short duration average shows a large price volatility compared to a longer one.
- It does not require periodic transactions to register checkpoints on-chain which are costly and hard to maintain.
Twaper gets the price from different sources and returns their weighted average as result. Each source is defined by a route which is a list of pairs. A pair is the address of a liquidity pool on a dex which is the basic source of the price for a token. For example the MUON token price can be calculated using [MUON-WETH, WETH-USDC] route by multiplying the MUON price in WETH by the WETH price in USDC.
Install mongo & redis and use the following steps to clone the Muon node and run a local network:
$ git clone git@github.com:muon-protocol/muon-node-js.git -b testnet --recurse-submodules
$ cd muon-node-js
$ npm i
$ npm run devnet-init -- -t=3 -n=4 -infura=<your-infura-project-id>
$ npm run devnet-run -- -n=3
$ ./node_modules/.bin/ts-node ./src/cmd config set url "http://localhost:8000/v1"
$ ./node_modules/.bin/ts-node ./src/cmd app deploy "twaper"
Then the network is available on http://localhost:8000/v1/.
Above steps are required to be done only once and to run the network again you need to use only a single command:
$ npm run devnet-run -- -n=3
twaper Muon app has two methods:
- price for getting a normal ERC-20 token TWAP
- lp_price for getting a LP token TWAP
To query these methods a config should be provided for the corresponding token. config is the address of the smart contract contains routes and other required information for the price calculation. twaper is currently using fantom for deployment of the configuration contracts. This guide explains how to deploy a Config contract.
Having the configuration deployed, the methods can be queried from the local network in the following way:
http://localhost:8000/v1/?app=twaper&method=price¶ms[config]=configAddr
For example, a config for INV token is deployed at 0xb0894bd0c703EF3ee0c1E3054cABfA288762838c
and can be used to query the price from the ALICE (Muon test network) in this way:
twaper can also be used as a node module to calculate TWAP of a token.
require("twaper")
const { twaper } = utils
calculatePrice
& calculateLpPrice
functions can be used to get the price of normal ERC-20 tokens and LP ones.
Full examples that demonstrates how to use these functions are available in samples folder.
calculatePrice
is used for TWAP calculation of a normal ERC-20 token. It gets 3 inputs and returns the price
and an object of removedPrices
which are the prices removed as outliers.
const { price, removedPrices } = await twaper.calculatePrice(validPriceGap, routes, toBlocks)
validPriceGap
is the valid price difference percentage between differnt routes in scale of1e18
. If the difference is more than this valid border, the function throws an error.routes
is an array ofroute
objects. Eachroute
has these following attributes:-
path
object is an array ofpair
objects which contains:address
is the address of the pair contract.reversed
istrue
if the price oftoken1
in terms oftoken0
should be used in the price calculation orfalse
otherwise.minutesToSeed
is the time in minutes for which the time weighted average is calculated.minutesToFuse
is the time in minutes for which the longer duration time weighted average for the fuse mechanism is calculated.fusePriceTolerance
is the acceptable price difference percentage between the short and long TWAP in scale of1e18
. The fuse triggers if the difference is more than this border.
-
chainId
is the id of the chain that the pair contract deployed to. -
abiStyle
is the pair contract abi style. For now UniV2 and Solidly are supported. -
weight
is the weight of the route in the average calculation.
-
toBlocks
defines the blocks at which the time weighted average price is calculated. There should be a block number for each chain the token has a route on.
calculateLpPrice
is used for TWAP calculation of a LP token. It gets 5 inputs and returns the price of the LP token.
const price = await twaper.calculateLpPrice(chainId, pair, routes0, routes1, toBlocks)
chainId
is the id of the chain that the pair contract of the LP token is deployed to.pair
is the address of the pair contract.routes0
defines the routes for getting the price oftoken0
as described in calculatePirce.routes1
defines the routes for getting the price oftoken1
as described in calculatePirce.toBlocks
defines the blocks at which the time weighted average price is calculated. There should be a block number for each chain the pair tokens have a route on.
- Install the required modules and create an
.env
file for running test.
$ cd twaper
$ npm i
$ cd tests
$ cp .env.sample .env
- Edit the
.env
file adn replace<your-infura-project-key>
by a valid key. - Run the tests
$ npm test