Contract Name:
OffchainOracle
Contract Source Code:
File 1 of 1 : OffchainOracle
// SPDX-License-Identifier: No
pragma solidity 0.7.6;
library SafeMath {
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}
library Sqrt {
function sqrt(uint y) internal pure returns (uint z) {
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
}
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
// For Uniswap v1
interface IUniswapV1 {
function getTokenToEthInputPrice(uint256 tokens_sold) external view returns (uint256 );
}
// For Uniswap v2, Quickswap v2, Sushiswap v2, pancakeswap v2
interface IUniswapV2Pair {
function getReserves() external view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast);
function token0() external view returns (address);
function token1() external view returns (address);
}
// For Uniswap v3, Sushiswap v3, Pancakeswap v3
interface IUniswapV3Pool {
function slot0() external view returns (uint160 sqrtPriceX96, int24, uint16, uint16, uint16, uint8, bool);
function ticks(int24 tick) external view returns (uint128, int128, uint256, uint256, int56, uint160, uint32, bool);
function tickSpacing() external view returns (int24);
function token0() external view returns (address);
function token1() external view returns (address);
function liquidity() external view returns (uint128);
}
// For Quickswap v3
interface IAlgebraPool {
function globalState() external view returns (uint160 price, int24 tick, uint16 fee, uint16 timepointIndex, uint8 communityFeeToken0, uint8 communityFeeToken1, bool unlocked);
function token0() external view returns (address);
function token1() external view returns (address);
function liquidity() external view returns (uint128);
}
// For Meshswap
interface IMeshswapPool {
function token0() external view returns (address);
function token1() external view returns (address);
function reserve0() external view returns (uint112);
function reserve1() external view returns (uint112);
}
// For Balancer v2
interface IStablePool {
function getTokenRate(address) external view returns (uint256);
}
// For Balancer v2
interface IWeightedPool {
function getPoolId() external view returns (bytes32);
function getNormalizedWeights() external view returns (uint256[] memory);
}
// For Balancer v2
interface IVault {
function getPoolTokens(bytes32 poolId) external view returns (address[] memory tokens, uint256[] memory balances, uint256 lastChangeBlock);
}
// For Kyber swap
interface IKyberDmmPool {
function getTradeInfo() external view returns (uint112 reserve0, uint112 reserve1, uint112 _vReserve0, uint112 _vReserve1, uint256 feeInPrecision);
function token0() external view returns (address);
function token1() external view returns (address);
}
// For Curve v2
interface ICurveV2Pool {
function price_oracle() external view returns (uint256);
function coins(uint256 arg0) external view returns (address);
}
// For Curve Stable
interface ICurveStablePool {
function price_oracle(uint256 arg0) external view returns (uint256);
function N_COINS() external view returns (uint256);
function coins(uint256 arg0) external view returns (address);
}
// // For Dodoex V2 DSP
// interface IDodoDSP {
// function _BASE_TOKEN_() external view returns (address);
// function _QUOTE_TOKEN_() external view returns (address);
// function querySellBase(address trader, uint256 payBaseAmount) external view returns (uint256 receiveQuoteAmount, uint256 mtFee, uint8 newRState, uint256 newBaseTarget);
// function querySellQuote(address trader, uint256 payQuoteAmount) external view returns (uint256 receiveBaseAmount, uint256 mtFee, uint8 newRState, uint256 newQuoteTarget);
// }
// For Dodoex V2 DVM & DSP
interface IDodoDVM {
function _BASE_TOKEN_() external view returns (address);
function _QUOTE_TOKEN_() external view returns (address);
function querySellBase(address trader, uint256 payBaseAmount) external view returns (uint256 receiveQuoteAmount, uint256 mtFee);
function querySellQuote(address trader, uint256 payQuoteAmount) external view returns (uint256 receiveBaseAmount, uint256 mtFee);
}
contract OffchainOracle {
using SafeMath for uint256;
using SafeMath for uint112;
using Sqrt for uint256;
function getRate(address tokenFrom, address tokenTo, address poolAddress, uint8 ver) external view returns (uint256 rate, uint8 decimalsFrom, uint8 decimalsTo) {
if(ver == 2 || ver == 5 || ver == 7) {
address token0;
address token1;
uint256 reserve0;
uint256 reserve1;
// version = 2 : Uniswap V2 like
if(ver == 2) {
token0 = IUniswapV2Pair(poolAddress).token0();
token1 = IUniswapV2Pair(poolAddress).token1();
(reserve0, reserve1,) = IUniswapV2Pair(poolAddress).getReserves();
}
// version = 5 : Meshswap
else if(ver == 5) {
token0 = IMeshswapPool(poolAddress).token0();
token1 = IMeshswapPool(poolAddress).token1();
reserve0 = IMeshswapPool(poolAddress).reserve0();
reserve1 = IMeshswapPool(poolAddress).reserve1();
}
// version = 7 : Kyber swap
else if(ver == 7) {
token0 = IKyberDmmPool(poolAddress).token0();
token1 = IKyberDmmPool(poolAddress).token1();
(,, reserve0, reserve1,) = IKyberDmmPool(poolAddress).getTradeInfo();
}
if(token0 == tokenFrom) {
decimalsFrom = IERC20Metadata(token0).decimals();
decimalsTo = IERC20Metadata(token1).decimals();
rate = reserve1.mul(1e18).div(reserve0);
} else {
decimalsFrom = IERC20Metadata(token1).decimals();
decimalsTo = IERC20Metadata(token0).decimals();
rate = reserve0.mul(1e18).div(reserve1);
}
}
// version = 1 : Uniswap V1
else if(ver == 1) {
decimalsFrom = IERC20Metadata(tokenFrom).decimals();
decimalsTo = 18;
rate = IUniswapV1(poolAddress).getTokenToEthInputPrice(10**decimalsFrom);
}
// version = 3 : Uniswap V3
else if(ver == 3) {
address token0 = IUniswapV3Pool(poolAddress).token0();
address token1 = IUniswapV3Pool(poolAddress).token1();
(uint256 sqrtPriceX96,,,,,,) = IUniswapV3Pool(poolAddress).slot0();
if(token0 == tokenFrom) {
decimalsFrom = IERC20Metadata(token0).decimals();
decimalsTo = IERC20Metadata(token1).decimals();
rate = (((1e18 * sqrtPriceX96) >> 96) * sqrtPriceX96) >> 96;
} else {
decimalsFrom = IERC20Metadata(token1).decimals();
decimalsTo = IERC20Metadata(token0).decimals();
rate = (1e18 << 192) / sqrtPriceX96 / sqrtPriceX96;
}
}
// version = 4 : QuickSwap V3
else if(ver == 4) {
address token0 = IAlgebraPool(poolAddress).token0();
address token1 = IAlgebraPool(poolAddress).token1();
(uint256 sqrtPriceX96,,,,,,) = IAlgebraPool(poolAddress).globalState();
if(token0 == tokenFrom) {
decimalsFrom = IERC20Metadata(token0).decimals();
decimalsTo = IERC20Metadata(token1).decimals();
rate = (((1e18 * sqrtPriceX96) >> 96) * sqrtPriceX96) >> 96;
} else {
decimalsFrom = IERC20Metadata(token1).decimals();
decimalsTo = IERC20Metadata(token0).decimals();
rate = (1e18 << 192) / sqrtPriceX96 / sqrtPriceX96;
}
}
// version = 6 : balancer v2
else if(ver == 6) {
address balancerVaultAddress = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
bytes32 poolId = IWeightedPool(poolAddress).getPoolId();
(uint256[] memory weights) = IWeightedPool(poolAddress).getNormalizedWeights();
(address[] memory tokens, uint256[] memory balances,) = IVault(balancerVaultAddress).getPoolTokens(poolId);
uint256 balanceOfTokenFrom = 0;
uint256 balanceOfTokenTo = 0;
for(uint8 i = 0; i < tokens.length; i++ ){
if(tokens[i] == tokenFrom) {
balanceOfTokenFrom = balances[i].mul(1e18).div(weights[i]);
}
if(tokens[i] == tokenTo) {
balanceOfTokenTo = balances[i].mul(1e18).div(weights[i]);
}
}
decimalsFrom = IERC20Metadata(tokenFrom).decimals();
decimalsTo = IERC20Metadata(tokenTo).decimals();
rate = balanceOfTokenTo.mul(1e18).div(balanceOfTokenFrom);
}
// version = 8 : Curve v2
// return price_oracle
else if(ver == 8) {
rate = ICurveV2Pool(poolAddress).price_oracle();
address token0 = ICurveV2Pool(poolAddress).coins(0);
if(tokenFrom == token0) {
uint256 num1e36 = 1e36;
rate = num1e36.div(rate);
}
decimalsFrom = 18;
decimalsTo = 18;
}
// version = 9 : Dodoex v2 DVM & DSP with sell = 1
// (ver=12 : sell=10^(-4))
// (ver=13 : sell=10^(-2))
// (ver=14 : sell=10^(2))
// (ver=15 : sell=10^(4))
// (ver=16 : sell=10^(6))
else if(ver == 9 || ver == 12 || ver == 13 || ver == 14 || ver == 15 || ver == 16) {
address token0 = IDodoDVM(poolAddress)._BASE_TOKEN_();
address token1 = IDodoDVM(poolAddress)._QUOTE_TOKEN_();
uint8 num = 4;
if(ver == 9) {
num = 4;
} else if(ver == 12) {
num = 0;
} else if(ver == 13) {
num = 2;
} else if(ver == 14) {
num = 6;
} else if(ver == 15) {
num = 8;
} else if(ver == 16) {
num = 10;
}
if(tokenFrom == token0) {
decimalsFrom = IERC20Metadata(token0).decimals();
decimalsTo = IERC20Metadata(token1).decimals();
(uint256 receiveQuoteAmount, ) = IDodoDVM(poolAddress).querySellBase(0xD692C31E5ec0d87D032ABC243c1eb2C9Ae204FeA, 10**(decimalsFrom+num-4));
rate = receiveQuoteAmount.mul(1e18).div(10**decimalsTo);
} else {
decimalsFrom = IERC20Metadata(token1).decimals();
decimalsTo = IERC20Metadata(token0).decimals();
(uint256 receiveBaseAmount, ) = IDodoDVM(poolAddress).querySellQuote(0xD692C31E5ec0d87D032ABC243c1eb2C9Ae204FeA, 10**(decimalsFrom+num-4));
rate = receiveBaseAmount.mul(1e18).div(10**decimalsTo);
}
decimalsFrom = 18;
decimalsTo = 18;
}
// version = 10: Balancer stable pool
else if(ver == 10) {
uint256 rateFrom = IStablePool(poolAddress).getTokenRate(tokenFrom);
uint256 rateTo = IStablePool(poolAddress).getTokenRate(tokenTo);
rate = rateFrom.mul(1e18).div(rateTo);
decimalsFrom = 18;
decimalsTo = 18;
}
// version = 11 :, Curve Stable
// return price_oracle(i)
else if(ver == 11) {
uint256 N_COINS = ICurveStablePool(poolAddress).N_COINS();
uint256 tokenFromInd = 0;
for(uint256 i = 1; i < N_COINS; i++) {
address tmpToken = ICurveStablePool(poolAddress).coins(i);
if(tmpToken == tokenFrom) {
tokenFromInd = i - 1;
break;
}
}
rate = ICurveStablePool(poolAddress).price_oracle(tokenFromInd);
decimalsFrom = 18;
decimalsTo = 18;
}
}
}