diff --git a/docs/spot-price-aggregator/_category_.json b/docs/spot-price-aggregator/_category_.json new file mode 100644 index 000000000..b89fda033 --- /dev/null +++ b/docs/spot-price-aggregator/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Spot Price Aggregator", + "position": 3, + "collapsed": false +} diff --git a/docs/spot-price-aggregator/examples.mdx b/docs/spot-price-aggregator/examples.mdx new file mode 100644 index 000000000..df0fe5743 --- /dev/null +++ b/docs/spot-price-aggregator/examples.mdx @@ -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); +``` diff --git a/docs/spot-price-aggregator/introduction.mdx b/docs/spot-price-aggregator/introduction.mdx new file mode 100644 index 000000000..1a8a330d8 --- /dev/null +++ b/docs/spot-price-aggregator/introduction.mdx @@ -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 diff --git a/docusaurus.config.js b/docusaurus.config.js index 9ca450524..34c06bb18 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -60,6 +60,10 @@ const config = { { label: 'Limit order protocol', href: '/docs/limit-order-protocol/introduction' + }, + { + label: 'Spot Price Aggregator', + href: '/docs/spot-price-aggregator/introduction' } ] }, @@ -89,10 +93,6 @@ const config = { label: 'EIP-2612 utils library', href: 'https://github.com/1inch/permit-signed-approvals-utils', }, - { - label: 'Spot price aggregator', - href: 'https://github.com/1inch/offchain-oracle', - }, { label: 'Cumulative merkle drop', href: 'https://github.com/1inch/merkle-distribution', diff --git a/src/components/homepage-features/HomepageFeatures.js b/src/components/homepage-features/HomepageFeatures.js index cf2f29ebf..b49702d05 100644 --- a/src/components/homepage-features/HomepageFeatures.js +++ b/src/components/homepage-features/HomepageFeatures.js @@ -2,6 +2,7 @@ import React from 'react'; import clsx from 'clsx'; import styles from './HomepageFeatures.module.css'; import ArrowOutside from '../../../static/img/icons/arrow-outside.svg'; +import Link from "@docusaurus/core/lib/client/exports/Link"; const FeatureList = [ { @@ -60,28 +61,58 @@ const FeatureList = [ }, ] }, + { + title: 'Spot price aggregator', + description: 'The 1inch spot price aggregator is a set of smart contracts that extract price data for tokens traded on DEXes from the blockchain', + image: ( +
+ ), + links: [ + { + title: 'Introduction', + href: '/docs/spot-price-aggregator/introduction' + }, + { + title: 'Github', + href: 'https://github.com/1inch/spot-price-aggregator' + }, + { + title: 'Smart contract', + href: 'https://etherscan.io/address/0x07D91f5fb9Bf7798734C3f606dB065549F6893bb' + }, + ] + }, ]; function Feature({title, description, image, links}) { return ({description}
+{description}
+- {link.title} - {link.isSwagger && Swagger} - {link.versionLabel && {link.versionLabel}} -
-+ {link.title} + {link.isSwagger && Swagger} + {link.versionLabel && {link.versionLabel}} +
+