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=3twaper 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 } = utilscalculatePrice & 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)validPriceGapis 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.routesis an array ofrouteobjects. Eachroutehas these following attributes:-
pathobject is an array ofpairobjects which contains:addressis the address of the pair contract.reversedistrueif the price oftoken1in terms oftoken0should be used in the price calculation orfalseotherwise.minutesToSeedis the time in minutes for which the time weighted average is calculated.minutesToFuseis the time in minutes for which the longer duration time weighted average for the fuse mechanism is calculated.fusePriceToleranceis 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.
-
chainIdis the id of the chain that the pair contract deployed to. -
abiStyleis the pair contract abi style. For now UniV2 and Solidly are supported. -
weightis the weight of the route in the average calculation.
-
toBlocksdefines 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)chainIdis the id of the chain that the pair contract of the LP token is deployed to.pairis the address of the pair contract.routes0defines the routes for getting the price oftoken0as described in calculatePirce.routes1defines the routes for getting the price oftoken1as described in calculatePirce.toBlocksdefines 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
.envfile for running test.
$ cd twaper
$ npm i
$ cd tests
$ cp .env.sample .env- Edit the
.envfile adn replace<your-infura-project-key>by a valid key. - Run the tests
$ npm test