This repository has been archived by the owner on Jul 1, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 198
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add "Spot price aggregator" article (#30)
- Loading branch information
Showing
8 changed files
with
347 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"label": "Spot Price Aggregator", | ||
"position": 3, | ||
"collapsed": false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
--- | ||
sidebar_position: 2 | ||
--- | ||
|
||
# Examples | ||
|
||
## Single token-to-ETH price usage | ||
|
||
``` | ||
const Web3 = require('web3'); | ||
const { BigNumber } = require('ethers'); | ||
const yourInfuraKey = 'add your key here'; | ||
const web3 = new Web3(`https://mainnet.infura.io/v3/${yourInfuraKey}`); | ||
// eslint-disable-next-line max-len | ||
const OffChainOracleAbi = '[{"inputs":[{"internalType":"contract MultiWrapper","name":"_multiWrapper","type":"address"},{"internalType":"contract IOracle[]","name":"existingOracles","type":"address[]"},{"internalType":"enum OffchainOracle.OracleType[]","name":"oracleTypes","type":"uint8[]"},{"internalType":"contract IERC20[]","name":"existingConnectors","type":"address[]"},{"internalType":"contract IERC20","name":"wBase","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"connector","type":"address"}],"name":"ConnectorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"connector","type":"address"}],"name":"ConnectorRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract MultiWrapper","name":"multiWrapper","type":"address"}],"name":"MultiWrapperUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IOracle","name":"oracle","type":"address"},{"indexed":false,"internalType":"enum OffchainOracle.OracleType","name":"oracleType","type":"uint8"}],"name":"OracleAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IOracle","name":"oracle","type":"address"},{"indexed":false,"internalType":"enum OffchainOracle.OracleType","name":"oracleType","type":"uint8"}],"name":"OracleRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"contract IERC20","name":"connector","type":"address"}],"name":"addConnector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOracle","name":"oracle","type":"address"},{"internalType":"enum OffchainOracle.OracleType","name":"oracleKind","type":"uint8"}],"name":"addOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"connectors","outputs":[{"internalType":"contract IERC20[]","name":"allConnectors","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"dstToken","type":"address"},{"internalType":"bool","name":"useWrappers","type":"bool"}],"name":"getRate","outputs":[{"internalType":"uint256","name":"weightedRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"bool","name":"useSrcWrappers","type":"bool"}],"name":"getRateToEth","outputs":[{"internalType":"uint256","name":"weightedRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"multiWrapper","outputs":[{"internalType":"contract MultiWrapper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracles","outputs":[{"internalType":"contract IOracle[]","name":"allOracles","type":"address[]"},{"internalType":"enum OffchainOracle.OracleType[]","name":"oracleTypes","type":"uint8[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"connector","type":"address"}],"name":"removeConnector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOracle","name":"oracle","type":"address"},{"internalType":"enum OffchainOracle.OracleType","name":"oracleKind","type":"uint8"}],"name":"removeOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract MultiWrapper","name":"_multiWrapper","type":"address"}],"name":"setMultiWrapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]'; | ||
const offChainOracleAddress = '0x07D91f5fb9Bf7798734C3f606dB065549F6893bb'; | ||
const offChainOracleContract = new web3.eth.Contract(JSON.parse(OffChainOracleAbi), offChainOracleAddress); | ||
const token = { | ||
address: '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT | ||
decimals: 6, | ||
}; | ||
offChainOracleContract.methods.getRateToEth( | ||
token.address, // source token | ||
true, // use source wrappers | ||
).call() | ||
.then((rate) => { | ||
const numerator = BigNumber.from(10).pow(token.decimals); | ||
const denominator = BigNumber.from(10).pow(18); // eth decimals | ||
const price = BigNumber.from(rate).mul(numerator).div(denominator); | ||
console.log(price.toString()); // 472685293218315 | ||
}) | ||
.catch(console.log); | ||
``` | ||
|
||
## Multiple token-to-ETH prices usage | ||
|
||
``` | ||
const Web3 = require('web3'); | ||
const { BigNumber } = require('ethers'); | ||
const yourInfuraKey = 'add your key here'; | ||
const web3 = new Web3(`https://mainnet.infura.io/v3/${yourInfuraKey}`); | ||
// eslint-disable-next-line max-len | ||
const MultiCallAbi = '[{"inputs":[{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct MultiCall.Call[]","name":"calls","type":"tuple[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"},{"internalType":"bool[]","name":"success","type":"bool[]"}],"stateMutability":"view","type":"function"}]'; | ||
// eslint-disable-next-line max-len | ||
const OffChainOracleAbi = '[{"inputs":[{"internalType":"contract MultiWrapper","name":"_multiWrapper","type":"address"},{"internalType":"contract IOracle[]","name":"existingOracles","type":"address[]"},{"internalType":"enum OffchainOracle.OracleType[]","name":"oracleTypes","type":"uint8[]"},{"internalType":"contract IERC20[]","name":"existingConnectors","type":"address[]"},{"internalType":"contract IERC20","name":"wBase","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"connector","type":"address"}],"name":"ConnectorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"connector","type":"address"}],"name":"ConnectorRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract MultiWrapper","name":"multiWrapper","type":"address"}],"name":"MultiWrapperUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IOracle","name":"oracle","type":"address"},{"indexed":false,"internalType":"enum OffchainOracle.OracleType","name":"oracleType","type":"uint8"}],"name":"OracleAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IOracle","name":"oracle","type":"address"},{"indexed":false,"internalType":"enum OffchainOracle.OracleType","name":"oracleType","type":"uint8"}],"name":"OracleRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"contract IERC20","name":"connector","type":"address"}],"name":"addConnector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOracle","name":"oracle","type":"address"},{"internalType":"enum OffchainOracle.OracleType","name":"oracleKind","type":"uint8"}],"name":"addOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"connectors","outputs":[{"internalType":"contract IERC20[]","name":"allConnectors","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"dstToken","type":"address"},{"internalType":"bool","name":"useWrappers","type":"bool"}],"name":"getRate","outputs":[{"internalType":"uint256","name":"weightedRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"bool","name":"useSrcWrappers","type":"bool"}],"name":"getRateToEth","outputs":[{"internalType":"uint256","name":"weightedRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"multiWrapper","outputs":[{"internalType":"contract MultiWrapper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracles","outputs":[{"internalType":"contract IOracle[]","name":"allOracles","type":"address[]"},{"internalType":"enum OffchainOracle.OracleType[]","name":"oracleTypes","type":"uint8[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"connector","type":"address"}],"name":"removeConnector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOracle","name":"oracle","type":"address"},{"internalType":"enum OffchainOracle.OracleType","name":"oracleKind","type":"uint8"}],"name":"removeOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract MultiWrapper","name":"_multiWrapper","type":"address"}],"name":"setMultiWrapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]'; | ||
const offChainOracleAddress = '0x07D91f5fb9Bf7798734C3f606dB065549F6893bb'; | ||
const multiCallContract = new web3.eth.Contract(JSON.parse(MultiCallAbi), '0xda3c19c6fe954576707fa24695efb830d9cca1ca'); | ||
const offChainOracleContract = new web3.eth.Contract(JSON.parse(OffChainOracleAbi)); | ||
const tokens = [ | ||
{ | ||
address: '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI | ||
decimals: 18, | ||
}, | ||
{ | ||
address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC | ||
decimals: 6, | ||
}, | ||
{ | ||
address: '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT | ||
decimals: 6, | ||
}, { | ||
address: '0x111111111117dc0aa78b770fa6a738034120c302', // 1INCH | ||
decimals: 18, | ||
}, | ||
]; | ||
const callData = tokens.map((token) => ({ | ||
to: offChainOracleAddress, | ||
data: offChainOracleContract.methods.getRateToEth( | ||
token.address, | ||
true, // use wrapper | ||
).encodeABI(), | ||
})); | ||
multiCallContract.methods.multicall(callData).call() | ||
.then(({ | ||
results, | ||
success, | ||
}) => { | ||
const prices = {}; | ||
for (let i = 0; i < results.length; i++) { | ||
if (!success[i]) { | ||
continue; | ||
} | ||
const decodedRate = web3.eth.abi.decodeParameter('uint256', results[i]).toString(); | ||
const numerator = BigNumber.from(10).pow(tokens[i].decimals); | ||
const denominator = BigNumber.from(10).pow(18); // eth decimals | ||
const price = BigNumber.from(decodedRate).mul(numerator).div(denominator); | ||
prices[tokens[i].address] = price.toString(); | ||
} | ||
console.log(prices); | ||
/* | ||
{ | ||
'0x6b175474e89094c44da98b954eedeac495271d0f': '527560209915550', | ||
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48': '507746821617073', | ||
'0xdac17f958d2ee523a2206206994597c13d831ec7': '529527134930000', | ||
'0x111111111117dc0aa78b770fa6a738034120c302': '1048752594621361' | ||
} | ||
*/ | ||
}) | ||
.catch(console.log); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
--- | ||
sidebar_position: 1 | ||
--- | ||
|
||
# Introduction | ||
|
||
The 1inch spot price aggregator is a set of smart contracts that extract price data for tokens traded on DEXes from the blockchain. To avoid price manipulations within a transaction, the spot price aggregator should ONLY be used off-chain. DO NOT use it on-chain. For off-chain usage see [Examples](/docs/spot-price-aggregator/examples) section below. | ||
|
||
## Wrappers | ||
|
||
To handle wrapped tokens, such as wETH, cDAI, aDAI etc., the 1inch spot price aggregator uses custom wrapper smart contracts that wrap/unwrap tokens at the current wrapping exchange rate. | ||
|
||
## Connectors | ||
|
||
If no direct liquidity pair exists between two tokens, the spot price aggregator calculates rates for those coins using another token that has pairs with both of them – a connector token. | ||
|
||
## Supported Deployments | ||
|
||
### Ethereum Mainnet | ||
|
||
#### Oracle [0x07D91f5fb9Bf7798734C3f606dB065549F6893bb](https://etherscan.io/address/0x07D91f5fb9Bf7798734C3f606dB065549F6893bb) | ||
|
||
#### Supported DEXes | ||
|
||
* Mooniswap | ||
* 1inch Liquidity Protocol V1.1 | ||
* Uniswap V1 | ||
* Uniswap V2 | ||
* Sushiswap | ||
* Equalizer.fi | ||
* Uniswap V3 | ||
* Synthetix | ||
* Chainlink | ||
* Shibaswap | ||
|
||
#### Supported wrappers | ||
|
||
* Aave V1 | ||
* Aave V2 | ||
* Compound | ||
* Fulcrum V1 | ||
* Fulcrum V2 | ||
* WETH | ||
* Cream | ||
|
||
#### Supported connectors | ||
|
||
* ETH | ||
* WETH | ||
* DAI | ||
* USDC | ||
* USDT | ||
* WBTC | ||
* 1INCH | ||
|
||
### Binance Smart Chain | ||
|
||
#### Oracle [0xfbD61B037C325b959c0F6A7e69D8f37770C2c550](https://bscscan.com/address/0xfbD61B037C325b959c0F6A7e69D8f37770C2c550) | ||
|
||
#### Supported DEXes | ||
|
||
* 1inch Liquidity Protocol V1.1 | ||
* Pancakeswap | ||
* Streetswap | ||
* Bakeryswap | ||
* Julswap | ||
* Demaxswap | ||
|
||
#### Supported wrappers | ||
|
||
* Venus | ||
* WBNB | ||
|
||
#### Supported connectors | ||
|
||
* BNB | ||
* WBNB | ||
* ETH | ||
* DAI | ||
* USDC | ||
* USDT | ||
* BUSD | ||
|
||
### Polygon | ||
|
||
#### Oracle [0x7F069df72b7A39bCE9806e3AfaF579E54D8CF2b9](https://explorer-mainnet.maticvigil.com/address/0x7F069df72b7A39bCE9806e3AfaF579E54D8CF2b9/transactions) | ||
|
||
#### Supported DEXes | ||
|
||
* QuickSwap | ||
* ComethSwap | ||
* SushiSwap | ||
* Dfyn | ||
|
||
#### Supported wrappers | ||
|
||
* AAVE | ||
* WMATIC | ||
|
||
#### Supported connectors | ||
|
||
* MATIC | ||
* WMATIC | ||
|
||
### Kovan | ||
|
||
#### Oracle [0x29BC86Ad68bB3BD3d54841a8522e0020C1882C22](https://kovan.etherscan.io/address/0x29BC86Ad68bB3BD3d54841a8522e0020C1882C22) | ||
|
||
#### Supported DEXes | ||
|
||
* 1inch Liquidity Protocol V1.1 | ||
* Uniswap V2 | ||
* Uniswap V1 | ||
|
||
#### Supported wrappers | ||
|
||
* Venus | ||
* WETH | ||
|
||
#### Supported connectors | ||
|
||
* ETH | ||
* WETH | ||
|
||
### Optimism | ||
|
||
#### Oracle [0x11DEE30E710B8d4a8630392781Cc3c0046365d4c](https://optimistic.etherscan.io/address/0x11DEE30E710B8d4a8630392781Cc3c0046365d4c) | ||
|
||
#### Supported DEXes | ||
|
||
* Uniswap V3 | ||
* Synthetix | ||
|
||
### Arbitrum | ||
|
||
#### Oracle [0x735247fb0a604c0adC6cab38ACE16D0DbA31295F](https://arbiscan.io/address/0x735247fb0a604c0adc6cab38ace16d0dba31295f) | ||
|
||
#### Supported DEXes | ||
|
||
* Uniswap V3 | ||
* Sushiswap | ||
* Swapr | ||
|
||
### Avax | ||
|
||
#### Oracle [0xBd0c7AaF0bF082712EbE919a9dD94b2d978f79A9](https://snowtrace.io/address/0xBd0c7AaF0bF082712EbE919a9dD94b2d978f79A9) | ||
|
||
#### Supported DEXes | ||
|
||
* Trader Joe | ||
* Pangolin Exchange | ||
|
||
### xDai | ||
|
||
#### Oracle [0x142DB045195CEcaBe415161e1dF1CF0337A4d02E](https://blockscout.com/xdai/mainnet/address/0x142DB045195CEcaBe415161e1dF1CF0337A4d02E) | ||
|
||
#### Supported DEXes | ||
|
||
* Honeyswap | ||
* Levinswap | ||
* Swapr | ||
* Sushiswap |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.