// File: contracts/interface/ICoFiXV2Factory.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
interface ICoFiXV2Factory {
// All pairs: {ETH <-> ERC20 Token}
event PairCreated(address indexed token, address pair, uint256);
event NewGovernance(address _new);
event NewController(address _new);
event NewFeeReceiver(address _new);
event NewFeeVaultForLP(address token, address feeVault);
event NewVaultForLP(address _new);
event NewVaultForTrader(address _new);
event NewVaultForCNode(address _new);
event NewDAO(address _new);
/// @dev Create a new token pair for trading
/// @param token the address of token to trade
/// @param initToken0Amount the initial asset ratio (initToken0Amount:initToken1Amount)
/// @param initToken1Amount the initial asset ratio (initToken0Amount:initToken1Amount)
/// @return pair the address of new token pair
function createPair(
address token,
uint256 initToken0Amount,
uint256 initToken1Amount
)
external
returns (address pair);
function getPair(address token) external view returns (address pair);
function allPairs(uint256) external view returns (address pair);
function allPairsLength() external view returns (uint256);
function getTradeMiningStatus(address token) external view returns (bool status);
function setTradeMiningStatus(address token, bool status) external;
function getFeeVaultForLP(address token) external view returns (address feeVault); // for LPs
function setFeeVaultForLP(address token, address feeVault) external;
function setGovernance(address _new) external;
function setController(address _new) external;
function setFeeReceiver(address _new) external;
function setVaultForLP(address _new) external;
function setVaultForTrader(address _new) external;
function setVaultForCNode(address _new) external;
function setDAO(address _new) external;
function getController() external view returns (address controller);
function getFeeReceiver() external view returns (address feeReceiver); // For CoFi Holders
function getVaultForLP() external view returns (address vaultForLP);
function getVaultForTrader() external view returns (address vaultForTrader);
function getVaultForCNode() external view returns (address vaultForCNode);
function getDAO() external view returns (address dao);
}
// File: contracts/interface/ICoFiXERC20.sol
pragma solidity 0.6.12;
interface ICoFiXERC20 {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
}
// File: contracts/interface/ICoFiXV2Pair.sol
pragma solidity 0.6.12;
interface ICoFiXV2Pair is ICoFiXERC20 {
struct OraclePrice {
uint256 ethAmount;
uint256 erc20Amount;
uint256 blockNum;
uint256 K;
uint256 theta;
}
// All pairs: {ETH <-> ERC20 Token}
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, address outToken, uint outAmount, address indexed to);
event Swap(
address indexed sender,
uint amountIn,
uint amountOut,
address outToken,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1);
function mint(address to, uint amountETH, uint amountToken) external payable returns (uint liquidity, uint oracleFeeChange);
function burn(address tokenTo, address ethTo) external payable returns (uint amountTokenOut, uint amountETHOut, uint oracleFeeChange);
function swapWithExact(address outToken, address to) external payable returns (uint amountIn, uint amountOut, uint oracleFeeChange, uint256[5] memory tradeInfo);
// function swapForExact(address outToken, uint amountOutExact, address to) external payable returns (uint amountIn, uint amountOut, uint oracleFeeChange, uint256[4] memory tradeInfo);
function skim(address to) external;
function sync() external;
function initialize(address, address, string memory, string memory, uint256, uint256) external;
/// @dev get Net Asset Value Per Share
/// @param ethAmount ETH side of Oracle price {ETH <-> ERC20 Token}
/// @param erc20Amount Token side of Oracle price {ETH <-> ERC20 Token}
/// @return navps The Net Asset Value Per Share (liquidity) represents
function getNAVPerShare(uint256 ethAmount, uint256 erc20Amount) external view returns (uint256 navps);
/// @dev get initial asset ratio
/// @return _initToken0Amount Token0(ETH) side of initial asset ratio {ETH <-> ERC20 Token}
/// @return _initToken1Amount Token1(ERC20) side of initial asset ratio {ETH <-> ERC20 Token}
function getInitialAssetRatio() external view returns (uint256 _initToken0Amount, uint256 _initToken1Amount);
}
// File: contracts/interface/INestPriceFacade.sol
pragma solidity ^0.6.12;
/// @dev This interface defines the methods for price call entry
interface INestPriceFacade {
// /// @dev Price call entry configuration structure
// struct Config {
// // Single query fee(0.0001 ether, DIMI_ETHER). 100
// uint16 singleFee;
// // Double query fee(0.0001 ether, DIMI_ETHER). 100
// uint16 doubleFee;
// // The normal state flag of the call address. 0
// uint8 normalFlag;
// }
// /// @dev Modify configuration
// /// @param config Configuration object
// function setConfig(Config memory config) external;
// /// @dev Get configuration
// /// @return Configuration object
// function getConfig() external view returns (Config memory);
/// @dev Set the address flag. Only the address flag equals to config.normalFlag can the price be called
/// @param addr Destination address
/// @param flag Address flag
function setAddressFlag(address addr, uint flag) external;
/// @dev Get the flag. Only the address flag equals to config.normalFlag can the price be called
/// @param addr Destination address
/// @return Address flag
function getAddressFlag(address addr) external view returns(uint);
/// @dev Set INestQuery implemention contract address for token
/// @param tokenAddress Destination token address
/// @param nestQueryAddress INestQuery implemention contract address, 0 means delete
function setNestQuery(address tokenAddress, address nestQueryAddress) external;
/// @dev Get INestQuery implemention contract address for token
/// @param tokenAddress Destination token address
/// @return INestQuery implemention contract address, 0 means use default
function getNestQuery(address tokenAddress) external view returns (address);
/// @dev Get the latest trigger price
/// @param tokenAddress Destination token address
/// @param paybackAddress As the charging fee may change, it is suggested that the caller pay more fees, and the excess fees will be returned through this address
/// @return blockNumber The block number of price
/// @return price The token price. (1eth equivalent to (price) token)
function triggeredPrice(address tokenAddress, address paybackAddress) external payable returns (uint blockNumber, uint price);
/// @dev Get the full information of latest trigger price
/// @param tokenAddress Destination token address
/// @param paybackAddress As the charging fee may change, it is suggested that the caller pay more fees, and the excess fees will be returned through this address
/// @return blockNumber The block number of price
/// @return price The token price. (1eth equivalent to (price) token)
/// @return avgPrice Average price
/// @return sigmaSQ The square of the volatility (18 decimal places). The current implementation assumes that
/// the volatility cannot exceed 1. Correspondingly, when the return value is equal to 999999999999996447,
/// it means that the volatility has exceeded the range that can be expressed
function triggeredPriceInfo(address tokenAddress, address paybackAddress) external payable returns (uint blockNumber, uint price, uint avgPrice, uint sigmaSQ);
/// @dev Find the price at block number
/// @param tokenAddress Destination token address
/// @param height Destination block number
/// @param paybackAddress As the charging fee may change, it is suggested that the caller pay more fees, and the excess fees will be returned through this address
/// @return blockNumber The block number of price
/// @return price The token price. (1eth equivalent to (price) token)
function findPrice(address tokenAddress, uint height, address paybackAddress) external payable returns (uint blockNumber, uint price);
/// @dev Get the latest effective price
/// @param tokenAddress Destination token address
/// @param paybackAddress As the charging fee may change, it is suggested that the caller pay more fees, and the excess fees will be returned through this address
/// @return blockNumber The block number of price
/// @return price The token price. (1eth equivalent to (price) token)
function latestPrice(address tokenAddress, address paybackAddress) external payable returns (uint blockNumber, uint price);
/// @dev Get the last (num) effective price
/// @param tokenAddress Destination token address
/// @param count The number of prices that want to return
/// @param paybackAddress As the charging fee may change, it is suggested that the caller pay more fees, and the excess fees will be returned through this address
/// @return An array which length is num * 2, each two element expresses one price like blockNumber|price
function lastPriceList(address tokenAddress, uint count, address paybackAddress) external payable returns (uint[] memory);
/// @dev Returns the results of latestPrice() and triggeredPriceInfo()
/// @param tokenAddress Destination token address
/// @param paybackAddress As the charging fee may change, it is suggested that the caller pay more fees, and the excess fees will be returned through this address
/// @return latestPriceBlockNumber The block number of latest price
/// @return latestPriceValue The token latest price. (1eth equivalent to (price) token)
/// @return triggeredPriceBlockNumber The block number of triggered price
/// @return triggeredPriceValue The token triggered price. (1eth equivalent to (price) token)
/// @return triggeredAvgPrice Average price
/// @return triggeredSigmaSQ The square of the volatility (18 decimal places). The current implementation assumes that
/// the volatility cannot exceed 1. Correspondingly, when the return value is equal to 999999999999996447,
/// it means that the volatility has exceeded the range that can be expressed
function latestPriceAndTriggeredPriceInfo(address tokenAddress, address paybackAddress)
external
payable
returns (
uint latestPriceBlockNumber,
uint latestPriceValue,
uint triggeredPriceBlockNumber,
uint triggeredPriceValue,
uint triggeredAvgPrice,
uint triggeredSigmaSQ
);
/// @dev Get the latest trigger price. (token and ntoken)
/// @param tokenAddress Destination token address
/// @param paybackAddress As the charging fee may change, it is suggested that the caller pay more fees, and the excess fees will be returned through this address
/// @return blockNumber The block number of price
/// @return price The token price. (1eth equivalent to (price) token)
/// @return ntokenBlockNumber The block number of ntoken price
/// @return ntokenPrice The ntoken price. (1eth equivalent to (price) ntoken)
function triggeredPrice2(address tokenAddress, address paybackAddress) external payable returns (uint blockNumber, uint price, uint ntokenBlockNumber, uint ntokenPrice);
/// @dev Get the full information of latest trigger price. (token and ntoken)
/// @param tokenAddress Destination token address
/// @param paybackAddress As the charging fee may change, it is suggested that the caller pay more fees, and the excess fees will be returned through this address
/// @return blockNumber The block number of price
/// @return price The token price. (1eth equivalent to (price) token)
/// @return avgPrice Average price
/// @return sigmaSQ The square of the volatility (18 decimal places). The current implementation assumes that
/// the volatility cannot exceed 1. Correspondingly, when the return value is equal to 999999999999996447,
/// it means that the volatility has exceeded the range that can be expressed
/// @return ntokenBlockNumber The block number of ntoken price
/// @return ntokenPrice The ntoken price. (1eth equivalent to (price) ntoken)
/// @return ntokenAvgPrice Average price of ntoken
/// @return ntokenSigmaSQ The square of the volatility (18 decimal places). The current implementation assumes that
/// the volatility cannot exceed 1. Correspondingly, when the return value is equal to 999999999999996447,
/// it means that the volatility has exceeded the range that can be expressed
function triggeredPriceInfo2(address tokenAddress, address paybackAddress) external payable returns (uint blockNumber, uint price, uint avgPrice, uint sigmaSQ, uint ntokenBlockNumber, uint ntokenPrice, uint ntokenAvgPrice, uint ntokenSigmaSQ);
/// @dev Get the latest effective price. (token and ntoken)
/// @param tokenAddress Destination token address
/// @param paybackAddress As the charging fee may change, it is suggested that the caller pay more fees, and the excess fees will be returned through this address
/// @return blockNumber The block number of price
/// @return price The token price. (1eth equivalent to (price) token)
/// @return ntokenBlockNumber The block number of ntoken price
/// @return ntokenPrice The ntoken price. (1eth equivalent to (price) ntoken)
function latestPrice2(address tokenAddress, address paybackAddress) external payable returns (uint blockNumber, uint price, uint ntokenBlockNumber, uint ntokenPrice);
}
// File: contracts/interface/ICoFiXV2Controller.sol
pragma solidity 0.6.12;
interface ICoFiXV2Controller {
event NewK(address token, uint256 K, uint256 sigma, uint256 T, uint256 ethAmount, uint256 erc20Amount, uint256 blockNum);
event NewGovernance(address _new);
event NewOracle(address _priceOracle);
event NewKTable(address _kTable);
event NewTimespan(uint256 _timeSpan);
event NewKRefreshInterval(uint256 _interval);
event NewKLimit(int128 maxK0);
event NewGamma(int128 _gamma);
event NewTheta(address token, uint32 theta);
event NewK(address token, uint32 k);
event NewCGamma(address token, uint32 gamma);
function addCaller(address caller) external;
function setCGamma(address token, uint32 gamma) external;
function queryOracle(address token, uint8 op, bytes memory data) external payable returns (uint256 k, uint256 ethAmount, uint256 erc20Amount, uint256 blockNum, uint256 theta);
function getKInfo(address token) external view returns (uint32 k, uint32 updatedAt, uint32 theta);
function getLatestPriceAndAvgVola(address token) external payable returns (uint256, uint256, uint256, uint256);
}
// File: contracts/lib/TransferHelper.sol
pragma solidity 0.6.12;
// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
function safeApprove(address token, address to, uint value) internal {
// bytes4(keccak256(bytes('approve(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED');
}
function safeTransfer(address token, address to, uint value) internal {
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
}
function safeTransferFrom(address token, address from, address to, uint value) internal {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
}
function safeTransferETH(address to, uint value) internal {
(bool success,) = to.call{value:value}(new bytes(0));
require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
}
}
// File: contracts/lib/ABDKMath64x64.sol
/*
* ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting.
* Author: Mikhail Vladimirov <mikhail.vladimirov@gmail.com>
*/
pragma solidity 0.6.12;
/**
* Smart contract library of mathematical functions operating with signed
* 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is
* basically a simple fraction whose numerator is signed 128-bit integer and
* denominator is 2^64. As long as denominator is always the same, there is no
* need to store it, thus in Solidity signed 64.64-bit fixed point numbers are
* represented by int128 type holding only the numerator.
*/
library ABDKMath64x64 {
/**
* @dev Minimum value signed 64.64-bit fixed point number may have.
*/
int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;
/**
* @dev Maximum value signed 64.64-bit fixed point number may have.
*/
int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
/**
* Convert signed 256-bit integer number into signed 64.64-bit fixed point
* number. Revert on overflow.
*
* @param x signed 256-bit integer number
* @return signed 64.64-bit fixed point number
*/
function fromInt (int256 x) internal pure returns (int128) {
require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF);
return int128 (x << 64);
}
/**
* Convert signed 64.64 fixed point number into signed 64-bit integer number
* rounding down.
*
* @param x signed 64.64-bit fixed point number
* @return signed 64-bit integer number
*/
function toInt (int128 x) internal pure returns (int64) {
return int64 (x >> 64);
}
/**
* Convert unsigned 256-bit integer number into signed 64.64-bit fixed point
* number. Revert on overflow.
*
* @param x unsigned 256-bit integer number
* @return signed 64.64-bit fixed point number
*/
function fromUInt (uint256 x) internal pure returns (int128) {
require (x <= 0x7FFFFFFFFFFFFFFF);
return int128 (x << 64);
}
/**
* Convert signed 64.64 fixed point number into unsigned 64-bit integer
* number rounding down. Revert on underflow.
*
* @param x signed 64.64-bit fixed point number
* @return unsigned 64-bit integer number
*/
function toUInt (int128 x) internal pure returns (uint64) {
require (x >= 0);
return uint64 (x >> 64);
}
/**
* Convert signed 128.128 fixed point number into signed 64.64-bit fixed point
* number rounding down. Revert on overflow.
*
* @param x signed 128.128-bin fixed point number
* @return signed 64.64-bit fixed point number
*/
function from128x128 (int256 x) internal pure returns (int128) {
int256 result = x >> 64;
require (result >= MIN_64x64 && result <= MAX_64x64);
return int128 (result);
}
/**
* Convert signed 64.64 fixed point number into signed 128.128 fixed point
* number.
*
* @param x signed 64.64-bit fixed point number
* @return signed 128.128 fixed point number
*/
function to128x128 (int128 x) internal pure returns (int256) {
return int256 (x) << 64;
}
/**
* Calculate x + y. Revert on overflow.
*
* @param x signed 64.64-bit fixed point number
* @param y signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function add (int128 x, int128 y) internal pure returns (int128) {
int256 result = int256(x) + y;
require (result >= MIN_64x64 && result <= MAX_64x64);
return int128 (result);
}
/**
* Calculate x - y. Revert on overflow.
*
* @param x signed 64.64-bit fixed point number
* @param y signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function sub (int128 x, int128 y) internal pure returns (int128) {
int256 result = int256(x) - y;
require (result >= MIN_64x64 && result <= MAX_64x64);
return int128 (result);
}
/**
* Calculate x * y rounding down. Revert on overflow.
*
* @param x signed 64.64-bit fixed point number
* @param y signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function mul (int128 x, int128 y) internal pure returns (int128) {
int256 result = int256(x) * y >> 64;
require (result >= MIN_64x64 && result <= MAX_64x64);
return int128 (result);
}
/**
* Calculate x * y rounding towards zero, where x is signed 64.64 fixed point
* number and y is signed 256-bit integer number. Revert on overflow.
*
* @param x signed 64.64 fixed point number
* @param y signed 256-bit integer number
* @return signed 256-bit integer number
*/
function muli (int128 x, int256 y) internal pure returns (int256) {
if (x == MIN_64x64) {
require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF &&
y <= 0x1000000000000000000000000000000000000000000000000);
return -y << 63;
} else {
bool negativeResult = false;
if (x < 0) {
x = -x;
negativeResult = true;
}
if (y < 0) {
y = -y; // We rely on overflow behavior here
negativeResult = !negativeResult;
}
uint256 absoluteResult = mulu (x, uint256 (y));
if (negativeResult) {
require (absoluteResult <=
0x8000000000000000000000000000000000000000000000000000000000000000);
return -int256 (absoluteResult); // We rely on overflow behavior here
} else {
require (absoluteResult <=
0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
return int256 (absoluteResult);
}
}
}
/**
* Calculate x * y rounding down, where x is signed 64.64 fixed point number
* and y is unsigned 256-bit integer number. Revert on overflow.
*
* @param x signed 64.64 fixed point number
* @param y unsigned 256-bit integer number
* @return unsigned 256-bit integer number
*/
function mulu (int128 x, uint256 y) internal pure returns (uint256) {
if (y == 0) return 0;
require (x >= 0);
uint256 lo = (uint256 (x) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64;
uint256 hi = uint256 (x) * (y >> 128);
require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
hi <<= 64;
require (hi <=
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo);
return hi + lo;
}
/**
* Calculate x / y rounding towards zero. Revert on overflow or when y is
* zero.
*
* @param x signed 64.64-bit fixed point number
* @param y signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function div (int128 x, int128 y) internal pure returns (int128) {
require (y != 0);
int256 result = (int256 (x) << 64) / y;
require (result >= MIN_64x64 && result <= MAX_64x64);
return int128 (result);
}
/**
* Calculate x / y rounding towards zero, where x and y are signed 256-bit
* integer numbers. Revert on overflow or when y is zero.
*
* @param x signed 256-bit integer number
* @param y signed 256-bit integer number
* @return signed 64.64-bit fixed point number
*/
function divi (int256 x, int256 y) internal pure returns (int128) {
require (y != 0);
bool negativeResult = false;
if (x < 0) {
x = -x; // We rely on overflow behavior here
negativeResult = true;
}
if (y < 0) {
y = -y; // We rely on overflow behavior here
negativeResult = !negativeResult;
}
uint128 absoluteResult = divuu (uint256 (x), uint256 (y));
if (negativeResult) {
require (absoluteResult <= 0x80000000000000000000000000000000);
return -int128 (absoluteResult); // We rely on overflow behavior here
} else {
require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
return int128 (absoluteResult); // We rely on overflow behavior here
}
}
/**
* Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
* integer numbers. Revert on overflow or when y is zero.
*
* @param x unsigned 256-bit integer number
* @param y unsigned 256-bit integer number
* @return signed 64.64-bit fixed point number
*/
function divu (uint256 x, uint256 y) internal pure returns (int128) {
require (y != 0);
uint128 result = divuu (x, y);
require (result <= uint128 (MAX_64x64));
return int128 (result);
}
/**
* Calculate -x. Revert on overflow.
*
* @param x signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function neg (int128 x) internal pure returns (int128) {
require (x != MIN_64x64);
return -x;
}
/**
* Calculate |x|. Revert on overflow.
*
* @param x signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function abs (int128 x) internal pure returns (int128) {
require (x != MIN_64x64);
return x < 0 ? -x : x;
}
/**
* Calculate 1 / x rounding towards zero. Revert on overflow or when x is
* zero.
*
* @param x signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function inv (int128 x) internal pure returns (int128) {
require (x != 0);
int256 result = int256 (0x100000000000000000000000000000000) / x;
require (result >= MIN_64x64 && result <= MAX_64x64);
return int128 (result);
}
/**
* Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down.
*
* @param x signed 64.64-bit fixed point number
* @param y signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function avg (int128 x, int128 y) internal pure returns (int128) {
return int128 ((int256 (x) + int256 (y)) >> 1);
}
/**
* Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down.
* Revert on overflow or in case x * y is negative.
*
* @param x signed 64.64-bit fixed point number
* @param y signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function gavg (int128 x, int128 y) internal pure returns (int128) {
int256 m = int256 (x) * int256 (y);
require (m >= 0);
require (m <
0x4000000000000000000000000000000000000000000000000000000000000000);
return int128 (sqrtu (uint256 (m), uint256 (x) + uint256 (y) >> 1));
}
/**
* Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number
* and y is unsigned 256-bit integer number. Revert on overflow.
*
* @param x signed 64.64-bit fixed point number
* @param y uint256 value
* @return signed 64.64-bit fixed point number
*/
function pow (int128 x, uint256 y) internal pure returns (int128) {
uint256 absoluteResult;
bool negativeResult = false;
if (x >= 0) {
absoluteResult = powu (uint256 (x) << 63, y);
} else {
// We rely on overflow behavior here
absoluteResult = powu (uint256 (uint128 (-x)) << 63, y);
negativeResult = y & 1 > 0;
}
absoluteResult >>= 63;
if (negativeResult) {
require (absoluteResult <= 0x80000000000000000000000000000000);
return -int128 (absoluteResult); // We rely on overflow behavior here
} else {
require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
return int128 (absoluteResult); // We rely on overflow behavior here
}
}
/**
* Calculate sqrt (x) rounding down. Revert if x < 0.
*
* @param x signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function sqrt (int128 x) internal pure returns (int128) {
require (x >= 0);
return int128 (sqrtu (uint256 (x) << 64, 0x10000000000000000));
}
/**
* Calculate binary logarithm of x. Revert if x <= 0.
*
* @param x signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function log_2 (int128 x) internal pure returns (int128) {
require (x > 0);
int256 msb = 0;
int256 xc = x;
if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; }
if (xc >= 0x100000000) { xc >>= 32; msb += 32; }
if (xc >= 0x10000) { xc >>= 16; msb += 16; }
if (xc >= 0x100) { xc >>= 8; msb += 8; }
if (xc >= 0x10) { xc >>= 4; msb += 4; }
if (xc >= 0x4) { xc >>= 2; msb += 2; }
if (xc >= 0x2) msb += 1; // No need to shift xc anymore
int256 result = msb - 64 << 64;
uint256 ux = uint256 (x) << 127 - msb;
for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) {
ux *= ux;
uint256 b = ux >> 255;
ux >>= 127 + b;
result += bit * int256 (b);
}
return int128 (result);
}
/**
* Calculate natural logarithm of x. Revert if x <= 0.
*
* @param x signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function ln (int128 x) internal pure returns (int128) {
require (x > 0);
return int128 (
uint256 (log_2 (x)) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128);
}
/**
* Calculate binary exponent of x. Revert on overflow.
*
* @param x signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function exp_2 (int128 x) internal pure returns (int128) {
require (x < 0x400000000000000000); // Overflow
if (x < -0x400000000000000000) return 0; // Underflow
uint256 result = 0x80000000000000000000000000000000;
if (x & 0x8000000000000000 > 0)
result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128;
if (x & 0x4000000000000000 > 0)
result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128;
if (x & 0x2000000000000000 > 0)
result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128;
if (x & 0x1000000000000000 > 0)
result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128;
if (x & 0x800000000000000 > 0)
result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128;
if (x & 0x400000000000000 > 0)
result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128;
if (x & 0x200000000000000 > 0)
result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128;
if (x & 0x100000000000000 > 0)
result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128;
if (x & 0x80000000000000 > 0)
result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128;
if (x & 0x40000000000000 > 0)
result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128;
if (x & 0x20000000000000 > 0)
result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128;
if (x & 0x10000000000000 > 0)
result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128;
if (x & 0x8000000000000 > 0)
result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128;
if (x & 0x4000000000000 > 0)
result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128;
if (x & 0x2000000000000 > 0)
result = result * 0x1000162E525EE054754457D5995292026 >> 128;
if (x & 0x1000000000000 > 0)
result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128;
if (x & 0x800000000000 > 0)
result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128;
if (x & 0x400000000000 > 0)
result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128;
if (x & 0x200000000000 > 0)
result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128;
if (x & 0x100000000000 > 0)
result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128;
if (x & 0x80000000000 > 0)
result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128;
if (x & 0x40000000000 > 0)
result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128;
if (x & 0x20000000000 > 0)
result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128;
if (x & 0x10000000000 > 0)
result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128;
if (x & 0x8000000000 > 0)
result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128;
if (x & 0x4000000000 > 0)
result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128;
if (x & 0x2000000000 > 0)
result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128;
if (x & 0x1000000000 > 0)
result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128;
if (x & 0x800000000 > 0)
result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128;
if (x & 0x400000000 > 0)
result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128;
if (x & 0x200000000 > 0)
result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128;
if (x & 0x100000000 > 0)
result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128;
if (x & 0x80000000 > 0)
result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128;
if (x & 0x40000000 > 0)
result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128;
if (x & 0x20000000 > 0)
result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128;
if (x & 0x10000000 > 0)
result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128;
if (x & 0x8000000 > 0)
result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128;
if (x & 0x4000000 > 0)
result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128;
if (x & 0x2000000 > 0)
result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128;
if (x & 0x1000000 > 0)
result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128;
if (x & 0x800000 > 0)
result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128;
if (x & 0x400000 > 0)
result = result * 0x100000000002C5C85FDF477B662B26945 >> 128;
if (x & 0x200000 > 0)
result = result * 0x10000000000162E42FEFA3AE53369388C >> 128;
if (x & 0x100000 > 0)
result = result * 0x100000000000B17217F7D1D351A389D40 >> 128;
if (x & 0x80000 > 0)
result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128;
if (x & 0x40000 > 0)
result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128;
if (x & 0x20000 > 0)
result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128;
if (x & 0x10000 > 0)
result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128;
if (x & 0x8000 > 0)
result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128;
if (x & 0x4000 > 0)
result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128;
if (x & 0x2000 > 0)
result = result * 0x1000000000000162E42FEFA39F02B772C >> 128;
if (x & 0x1000 > 0)
result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128;
if (x & 0x800 > 0)
result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128;
if (x & 0x400 > 0)
result = result * 0x100000000000002C5C85FDF473DEA871F >> 128;
if (x & 0x200 > 0)
result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128;
if (x & 0x100 > 0)
result = result * 0x100000000000000B17217F7D1CF79E949 >> 128;
if (x & 0x80 > 0)
result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128;
if (x & 0x40 > 0)
result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128;
if (x & 0x20 > 0)
result = result * 0x100000000000000162E42FEFA39EF366F >> 128;
if (x & 0x10 > 0)
result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128;
if (x & 0x8 > 0)
result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128;
if (x & 0x4 > 0)
result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128;
if (x & 0x2 > 0)
result = result * 0x1000000000000000162E42FEFA39EF358 >> 128;
if (x & 0x1 > 0)
result = result * 0x10000000000000000B17217F7D1CF79AB >> 128;
result >>= 63 - (x >> 64);
require (result <= uint256 (MAX_64x64));
return int128 (result);
}
/**
* Calculate natural exponent of x. Revert on overflow.
*
* @param x signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function exp (int128 x) internal pure returns (int128) {
require (x < 0x400000000000000000); // Overflow
if (x < -0x400000000000000000) return 0; // Underflow
return exp_2 (
int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128));
}
/**
* Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
* integer numbers. Revert on overflow or when y is zero.
*
* @param x unsigned 256-bit integer number
* @param y unsigned 256-bit integer number
* @return unsigned 64.64-bit fixed point number
*/
function divuu (uint256 x, uint256 y) private pure returns (uint128) {
require (y != 0);
uint256 result;
if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
result = (x << 64) / y;
else {
uint256 msb = 192;
uint256 xc = x >> 192;
if (xc >= 0x100000000) { xc >>= 32; msb += 32; }
if (xc >= 0x10000) { xc >>= 16; msb += 16; }
if (xc >= 0x100) { xc >>= 8; msb += 8; }
if (xc >= 0x10) { xc >>= 4; msb += 4; }
if (xc >= 0x4) { xc >>= 2; msb += 2; }
if (xc >= 0x2) msb += 1; // No need to shift xc anymore
result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1);
require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
uint256 hi = result * (y >> 128);
uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
uint256 xh = x >> 192;
uint256 xl = x << 64;
if (xl < lo) xh -= 1;
xl -= lo; // We rely on overflow behavior here
lo = hi << 128;
if (xl < lo) xh -= 1;
xl -= lo; // We rely on overflow behavior here
assert (xh == hi >> 128);
result += xl / y;
}
require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
return uint128 (result);
}
/**
* Calculate x^y assuming 0^0 is 1, where x is unsigned 129.127 fixed point
* number and y is unsigned 256-bit integer number. Revert on overflow.
*
* @param x unsigned 129.127-bit fixed point number
* @param y uint256 value
* @return unsigned 129.127-bit fixed point number
*/
function powu (uint256 x, uint256 y) private pure returns (uint256) {
if (y == 0) return 0x80000000000000000000000000000000;
else if (x == 0) return 0;
else {
int256 msb = 0;
uint256 xc = x;
if (xc >= 0x100000000000000000000000000000000) { xc >>= 128; msb += 128; }
if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; }
if (xc >= 0x100000000) { xc >>= 32; msb += 32; }
if (xc >= 0x10000) { xc >>= 16; msb += 16; }
if (xc >= 0x100) { xc >>= 8; msb += 8; }
if (xc >= 0x10) { xc >>= 4; msb += 4; }
if (xc >= 0x4) { xc >>= 2; msb += 2; }
if (xc >= 0x2) msb += 1; // No need to shift xc anymore
int256 xe = msb - 127;
if (xe > 0) x >>= xe;
else x <<= -xe;
uint256 result = 0x80000000000000000000000000000000;
int256 re = 0;
while (y > 0) {
if (y & 1 > 0) {
result = result * x;
y -= 1;
re += xe;
if (result >=
0x8000000000000000000000000000000000000000000000000000000000000000) {
result >>= 128;
re += 1;
} else result >>= 127;
if (re < -127) return 0; // Underflow
require (re < 128); // Overflow
} else {
x = x * x;
y >>= 1;
xe <<= 1;
if (x >=
0x8000000000000000000000000000000000000000000000000000000000000000) {
x >>= 128;
xe += 1;
} else x >>= 127;
if (xe < -127) return 0; // Underflow
require (xe < 128); // Overflow
}
}
if (re > 0) result <<= re;
else if (re < 0) result >>= -re;
return result;
}
}
/**
* Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer
* number.
*
* @param x unsigned 256-bit integer number
* @return unsigned 128-bit integer number
*/
function sqrtu (uint256 x, uint256 r) private pure returns (uint128) {
if (x == 0) return 0;
else {
require (r > 0);
while (true) {
uint256 rr = x / r;
if (r == rr || r + 1 == rr) return uint128 (r);
else if (r == rr + 1) return uint128 (rr);
r = r + rr + 1 >> 1;
}
}
}
}
// File: @openzeppelin/contracts/math/SafeMath.sol
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
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;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}
// File: contracts/CoFiXV2Controller.sol
pragma solidity 0.6.12;
// Controller contract to call NEST Oracle for prices, managed by governance
// Governance role of this contract should be the `Timelock` contract, which is further managed by a multisig contract
contract CoFiXV2Controller is ICoFiXV2Controller { // ctrl-03: change contract name to avoid truffle complaint
using SafeMath for uint256;
enum CoFiX_OP { QUERY, MINT, BURN, SWAP_WITH_EXACT, SWAP_FOR_EXACT } // operations in CoFiX
// uint256 constant public AONE = 1 ether;
// uint256 constant public K_BASE = 1E8;
uint256 constant public K_GAMMA_BASE = 10;
uint256 constant public NAVPS_BASE = 1E18; // NAVPS (Net Asset Value Per Share), need accuracy
uint256 constant internal K_ALPHA = 2000; // α=2e-05*1e8
uint256 constant internal K_BETA = 4000000000; // β=40*1e8
uint256 internal T = 3600; // ctrl-v2: V1 (900) -> V2 (3600)
uint256 internal K_EXPECTED_VALUE = 0.005*1E8; // ctrl-v2: V1 (0.0025) -> V2 (0.005)
// impact cost params
uint256 constant internal C_BUYIN_ALPHA = 0; // α=0
uint256 constant internal C_BUYIN_BETA = 20000000000000; // β=2e-05*1e18
uint256 constant internal C_SELLOUT_ALPHA = 0; // α=0
uint256 constant internal C_SELLOUT_BETA = 20000000000000; // β=2e-05*1e18
mapping(address => uint32) public CGammaMap;
// int128 constant internal SIGMA_STEP = 0x346DC5D638865; // (0.00005*2**64).toString(16), 0.00005 as 64.64-bit fixed point
// int128 constant internal ZERO_POINT_FIVE = 0x8000000000000000; // (0.5*2**64).toString(16)
uint256 constant PRICE_DEVIATION = 10; // price deviation < 10%
mapping(address => uint32[3]) internal KInfoMap; // gas saving, index [0] is k vlaue, index [1] is updatedAt, index [2] is theta
mapping(address => bool) public callerAllowed;
// managed by governance
address public governance;
address public immutable oracle;
address public immutable nestToken;
address public immutable factory;
uint256 public timespan = 14;
// uint256 public kRefreshInterval = 5 minutes;
// uint256 public DESTRUCTION_AMOUNT = 0 ether; // from nest oracle
// int128 public MAX_K0 = 0xCCCCCCCCCCCCD00; // (0.05*2**64).toString(16)
// int128 public GAMMA = 0x8000000000000000; // (0.5*2**64).toString(16)
struct OracleParams {
uint256 ethAmount;
uint256 erc20Amount;
uint256 blockNum;
uint256 K;
uint256 T; // time offset
uint256 avgPrice; // average price
uint256 theta;
uint256 sigma;
uint256 tIdx;
uint256 sigmaIdx;
}
modifier onlyGovernance() {
require(msg.sender == governance, "CoFiXCtrl: !governance");
_;
}
constructor(address _oracle, address _nest, address _factory) public {
governance = msg.sender;
oracle = _oracle;
nestToken = _nest;
factory = _factory;
// add previous pair as caller
ICoFiXV2Factory cFactory = ICoFiXV2Factory(_factory);
uint256 pairCnt = cFactory.allPairsLength();
for (uint256 i = 0; i < pairCnt; i++) {
address pair = cFactory.allPairs(i);
callerAllowed[pair] = true;
}
}
receive() external payable {}
/* setters for protocol governance */
function setGovernance(address _new) external onlyGovernance {
governance = _new;
emit NewGovernance(_new);
}
function setTimespan(uint256 _timeSpan) external onlyGovernance {
timespan = _timeSpan;
emit NewTimespan(_timeSpan);
}
// function setKRefreshInterval(uint256 _interval) external onlyGovernance {
// kRefreshInterval = _interval;
// emit NewKRefreshInterval(_interval);
// }
// function setOracleDestructionAmount(uint256 _amount) external onlyGovernance {
// DESTRUCTION_AMOUNT = _amount;
// }
function setTLimit(uint256 _T) external onlyGovernance { // ctrl-v2: new setter for T
T = _T;
}
function setK(address token, uint32 k) external onlyGovernance { // ctrl-v2: new setter for K, adjustable by governance
K_EXPECTED_VALUE = uint256(k);
emit NewK(token, k); // new event for setting K
}
function setTheta(address token, uint32 theta) external onlyGovernance {
KInfoMap[token][2] = theta;
emit NewTheta(token, theta);
}
function addCaller(address caller) external override {
require(msg.sender == factory || msg.sender == governance, "CoFiXCtrl: only factory or gov");
callerAllowed[caller] = true;
}
function setCGamma(address token, uint32 gamma) external override onlyGovernance {
CGammaMap[token] = gamma;
emit NewCGamma(token, gamma);
}
// Calc variance of price and K in CoFiX is very expensive
// We use expected value of K based on statistical calculations here to save gas
// In the near future, NEST could provide the variance of price directly. We will adopt it then.
// We can make use of `data` bytes in the future
function queryOracle(address token, uint8 op, bytes memory data) external override payable returns (uint256 _k, uint256 _ethAmount, uint256 _erc20Amount, uint256 _blockNum, uint256 _theta) {
require(callerAllowed[msg.sender], "CoFiXCtrl: caller not allowed");
(_k, _ethAmount, _erc20Amount, _blockNum) = getLatestPrice(token);
CoFiX_OP cop = CoFiX_OP(op);
uint256 impactCost;
if (cop == CoFiX_OP.SWAP_WITH_EXACT) {
impactCost = calcImpactCostFor_SWAP_WITH_EXACT(token, data, _ethAmount, _erc20Amount);
} else if (cop == CoFiX_OP.SWAP_FOR_EXACT) {
revert("disabled experimental feature!"); // ctrl-v2: disable swapForExact function
} else if (cop == CoFiX_OP.BURN) {
impactCost = calcImpactCostFor_BURN(token, data, _ethAmount, _erc20Amount);
}
_k = _k.add(impactCost); // ctrl-v2: adjustable K + impactCost is the final K
_theta = KInfoMap[token][2];
return (_k, _ethAmount, _erc20Amount, _blockNum, _theta);
}
function calcImpactCostFor_BURN(address token, bytes memory data, uint256 ethAmount, uint256 erc20Amount) public view returns (uint256 impactCost) {
// bytes memory data = abi.encode(msg.sender, liquidity, initToken1Amount);
(, uint256 liquidity) = abi.decode(data, (address, uint256));
(uint256 initToken0Amount, uint256 initToken1Amount) = ICoFiXV2Pair(msg.sender).getInitialAssetRatio(); // pair call controller, msg.sender is pair
// calc real vol by liquidity * np
uint256 navps = ICoFiXV2Pair(msg.sender).getNAVPerShare(ethAmount, erc20Amount); // pair call controller, msg.sender is pair
// ethOut
uint256 buyVol = liquidity.mul(navps).div(NAVPS_BASE);
/*
tokenOut = liquidity * navps * \frac{k_0}{P_t}\\\\
= liquidity * navps * \frac{initToken1Amount * ethAmount}{initToken0Amount * erc20Amount * NAVPS\_BASE}
*/
uint256 initToken1AmountMulEthAmount = initToken1Amount.mul(ethAmount);
uint256 erc20AmountMulInitToken0Amount = erc20Amount.mul(initToken0Amount);
uint256 sellOutVol = liquidity.mul(navps).mul(initToken1AmountMulEthAmount).div(erc20AmountMulInitToken0Amount).div(NAVPS_BASE);
// buy in ETH, outToken is ETH
uint256 impactCostForBuy = impactCostForBuyInETH(token, buyVol);
// sell out liquidity, outToken is token, take this as sell out ETH and get token
uint256 impactCostForSellOut = impactCostForSellOutETH(token, sellOutVol);
return impactCostForBuy.add(impactCostForSellOut);
}
function calcImpactCostFor_SWAP_WITH_EXACT(address token, bytes memory data, uint256 ethAmount, uint256 erc20Amount) public view returns (uint256 impactCost) {
(, address outToken, , uint256 amountIn) = abi.decode(data, (address, address, address, uint256));
if (outToken != token) {
// buy in ETH, outToken is ETH, amountIn is token
// convert to amountIn in ETH
uint256 vol = uint256(amountIn).mul(ethAmount).div(erc20Amount);
return impactCostForBuyInETH(token, vol);
}
// sell out ETH, amountIn is ETH
return impactCostForSellOutETH(token, amountIn);
}
function calcImpactCostFor_SWAP_FOR_EXACT(address token, bytes memory data, uint256 ethAmount, uint256 erc20Amount) public view returns (uint256 impactCost) {
(, address outToken, uint256 amountOutExact,) = abi.decode(data, (address, address, uint256, address));
if (outToken != token) {
// buy in ETH, outToken is ETH, amountOutExact is ETH
return impactCostForBuyInETH(token, amountOutExact);
}
// sell out ETH, amountIn is ETH, amountOutExact is token
// convert to amountOutExact in ETH
uint256 vol = uint256(amountOutExact).mul(ethAmount).div(erc20Amount);
return impactCostForSellOutETH(token, vol);
}
// impact cost
// - C = 0, if VOL < 50 / γ
// - C = (α + β * VOL) * γ, if VOL >= 50 / γ
// α=0,β=2e-05
function impactCostForBuyInETH(address token, uint256 vol) public view returns (uint256 impactCost) {
uint32 gamma = CGammaMap[token];
if (vol.mul(gamma) < 50 ether) {
return 0;
}
// return C_BUYIN_ALPHA.add(C_BUYIN_BETA.mul(vol).div(1e18)).mul(1e8).div(1e18);
return (C_BUYIN_ALPHA.add(C_BUYIN_BETA.mul(vol).div(1e18)).div(1e10)).mul(gamma); // combine mul div
}
// α=0,β=2e-05
function impactCostForSellOutETH(address token, uint256 vol) public view returns (uint256 impactCost) {
uint32 gamma = CGammaMap[token];
if (vol.mul(gamma) < 50 ether) {
return 0;
}
// return (C_SELLOUT_BETA.mul(vol).div(1e18)).sub(C_SELLOUT_ALPHA).mul(1e8).div(1e18);
return ((C_SELLOUT_BETA.mul(vol).div(1e18)).sub(C_SELLOUT_ALPHA).div(1e10)).mul(gamma); // combine mul div
}
function getKInfo(address token) external override view returns (uint32 k, uint32 updatedAt, uint32 theta) {
// ctrl-v3: load from storage instead of constant value
uint32 kStored = KInfoMap[token][0];
if (kStored != 0) {
k = kStored;
} else {
k = uint32(K_EXPECTED_VALUE);
}
updatedAt = KInfoMap[token][1];
theta = KInfoMap[token][2];
}
// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
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;
}
}
function getLatestPrice(address token) internal returns (uint256 _k, uint256 _ethAmount, uint256 _erc20Amount, uint256 _blockNum) {
uint256 _balanceBefore = address(this).balance;
OracleParams memory _op;
// query oracle
/// latestPriceBlockNumber The block number of latest price
/// latestPriceValue The token latest price. (1eth equivalent to (price) token)
/// triggeredPriceBlockNumber The block number of triggered price
/// triggeredPriceValue The token triggered price. (1eth equivalent to (price) token)
/// triggeredAvgPrice Average price
/// triggeredSigmaSQ The square of the volatility (18 decimal places).
(
_op.blockNum,
_op.erc20Amount,
/* uint triggeredPriceBlockNumber */,
/* uint triggeredPriceValue */,
_op.avgPrice,
_op.sigma
) = INestPriceFacade(oracle).latestPriceAndTriggeredPriceInfo{value: msg.value}(token, address(this));
_op.sigma = sqrt(_op.sigma.mul(1e18));
_op.ethAmount = 1 ether;
// validate T
_op.T = block.number.sub(_op.blockNum).mul(timespan);
require(_op.T < T, "CoFiXCtrl: oralce price outdated"); // ctrl-v2: adjustable T
{
// check if the price is steady
uint256 price;
bool isDeviated;
price = _op.erc20Amount.mul(1e18).div(_op.ethAmount);
uint256 diff = price > _op.avgPrice? (price - _op.avgPrice) : (_op.avgPrice - price);
isDeviated = (diff.mul(100) < _op.avgPrice.mul(PRICE_DEVIATION))? false : true;
require(isDeviated == false, "CoFiXCtrl: price deviation"); // validate
}
// calc K
// K=(0.00001*T+10*σ)*γ(σ)
{
uint256 gamma = calcGamma(_op.sigma);
_k = K_ALPHA.mul(_op.T).mul(1e18).add(K_BETA.mul(_op.sigma)).mul(gamma).div(K_GAMMA_BASE).div(1e18);
emit NewK(token, _k, _op.sigma, _op.T, _op.ethAmount, _op.erc20Amount, _op.blockNum);
}
{
// return oracle fee change
// we could decode data in the future to pay the fee change and mining award token directly to reduce call cost
// TransferHelper.safeTransferETH(payback, msg.value.sub(_balanceBefore.sub(address(this).balance)));
uint256 oracleFeeChange = msg.value.sub(_balanceBefore.sub(address(this).balance));
if (oracleFeeChange > 0) TransferHelper.safeTransferETH(msg.sender, oracleFeeChange);
KInfoMap[token][0] = uint32(_k);
KInfoMap[token][1] = uint32(block.timestamp); // 2106
return (_k, _op.ethAmount, _op.erc20Amount, _op.blockNum);
}
}
/**
* @notice Calc K value
* @param vola The square of the volatility (18 decimal places).
* @param bn The block number when (ETH, TOKEN) price takes into effective
* @return k The K value
*/
function calcK(uint256 vola, uint256 bn) external view returns (uint32 k) {
uint256 _T = block.number.sub(bn).mul(timespan);
vola = sqrt(vola.mul(1e18));
uint256 gamma = calcGamma(vola);
k = uint32(K_ALPHA.mul(_T).mul(1e18).add(K_BETA.mul(vola)).mul(gamma).div(K_GAMMA_BASE).div(1e18));
}
function calcGamma(uint256 vola) public pure returns (uint256 gamma) {
// (0.0003 0.0005) => (300000000000000 500000000000000)
if (vola <= 300000000000000) { // ? ≤ 0.0003, gamma = 1
return 10;
} else if (vola > 500000000000000) { // ? > 0.0005, gamma = 2
return 20;
} else { // 0.0003 < ? ≤ 0.0005, gamma = 1.5
return 15;
}
}
function getLatestPriceAndAvgVola(address token) public override payable returns (uint256, uint256, uint256, uint256) {
require(callerAllowed[msg.sender], "CoFiXCtrl: caller not allowed");
uint256 _balanceBefore = address(this).balance;
(
uint256 bn,
uint256 erc20Amount,
/* uint triggeredPriceBlockNumber */,
/* uint triggeredPriceValue */,
uint256 avg,
uint256 triggeredSigmaSQ
) = INestPriceFacade(oracle).latestPriceAndTriggeredPriceInfo{value: msg.value}(token, address(this));
uint256 vola = sqrt(triggeredSigmaSQ);
// (uint256 ethAmount, uint256 erc20Amount, uint256 avg, int128 vola, uint256 bn) =
// INestQuery(oracle).queryPriceAvgVola{value: msg.value}(token, address(this));
uint256 _T = block.number.sub(bn).mul(timespan);
require(_T < T, "CoFiXCtrl: oralce price outdated"); // ctrl-v2: adjustable T
uint256 oracleFeeChange = msg.value.sub(_balanceBefore.sub(address(this).balance));
if (oracleFeeChange > 0) TransferHelper.safeTransferETH(msg.sender, oracleFeeChange);
return (1 ether, erc20Amount, avg, vola);
}
// ctrl-v2: remove unused code bellow according to PeckShield's advice
}
{
"compilationTarget": {
"CoFiXV2Controller.sol": "CoFiXV2Controller"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 6666
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_oracle","type":"address"},{"internalType":"address","name":"_nest","type":"address"},{"internalType":"address","name":"_factory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint32","name":"gamma","type":"uint32"}],"name":"NewCGamma","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int128","name":"_gamma","type":"int128"}],"name":"NewGamma","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_new","type":"address"}],"name":"NewGovernance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"K","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sigma","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"T","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"erc20Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockNum","type":"uint256"}],"name":"NewK","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint32","name":"k","type":"uint32"}],"name":"NewK","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int128","name":"maxK0","type":"int128"}],"name":"NewKLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_interval","type":"uint256"}],"name":"NewKRefreshInterval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_kTable","type":"address"}],"name":"NewKTable","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_priceOracle","type":"address"}],"name":"NewOracle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint32","name":"theta","type":"uint32"}],"name":"NewTheta","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_timeSpan","type":"uint256"}],"name":"NewTimespan","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"CGammaMap","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"K_GAMMA_BASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NAVPS_BASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"addCaller","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vola","type":"uint256"}],"name":"calcGamma","outputs":[{"internalType":"uint256","name":"gamma","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"}],"name":"calcImpactCostFor_BURN","outputs":[{"internalType":"uint256","name":"impactCost","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"}],"name":"calcImpactCostFor_SWAP_FOR_EXACT","outputs":[{"internalType":"uint256","name":"impactCost","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"erc20Amount","type":"uint256"}],"name":"calcImpactCostFor_SWAP_WITH_EXACT","outputs":[{"internalType":"uint256","name":"impactCost","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vola","type":"uint256"},{"internalType":"uint256","name":"bn","type":"uint256"}],"name":"calcK","outputs":[{"internalType":"uint32","name":"k","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"callerAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getKInfo","outputs":[{"internalType":"uint32","name":"k","type":"uint32"},{"internalType":"uint32","name":"updatedAt","type":"uint32"},{"internalType":"uint32","name":"theta","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getLatestPriceAndAvgVola","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"vol","type":"uint256"}],"name":"impactCostForBuyInETH","outputs":[{"internalType":"uint256","name":"impactCost","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"vol","type":"uint256"}],"name":"impactCostForSellOutETH","outputs":[{"internalType":"uint256","name":"impactCost","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nestToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint8","name":"op","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"queryOracle","outputs":[{"internalType":"uint256","name":"_k","type":"uint256"},{"internalType":"uint256","name":"_ethAmount","type":"uint256"},{"internalType":"uint256","name":"_erc20Amount","type":"uint256"},{"internalType":"uint256","name":"_blockNum","type":"uint256"},{"internalType":"uint256","name":"_theta","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"gamma","type":"uint32"}],"name":"setCGamma","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_new","type":"address"}],"name":"setGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"k","type":"uint32"}],"name":"setK","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_T","type":"uint256"}],"name":"setTLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"theta","type":"uint32"}],"name":"setTheta","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_timeSpan","type":"uint256"}],"name":"setTimespan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"timespan","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]