From ccd01e5b6328b4891c257301d8aaafbb30c137c7 Mon Sep 17 00:00:00 2001 From: Paul-D-Ant <1611768193@qq.com> Date: Thu, 24 Mar 2022 15:25:11 +0800 Subject: [PATCH 1/7] add kcc chain --- migrations/10_initialize.js | 6 ++++++ migrations/2_deploy_basic.js | 22 +++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) mode change 100644 => 100755 migrations/10_initialize.js mode change 100644 => 100755 migrations/2_deploy_basic.js diff --git a/migrations/10_initialize.js b/migrations/10_initialize.js old mode 100644 new mode 100755 index 60a28eb..f912b7e --- a/migrations/10_initialize.js +++ b/migrations/10_initialize.js @@ -91,6 +91,12 @@ async function initializeLenderPool(accounts, network) { m.log("waiting controller create CAKE - WBNB market ......"); await intializeMarket(accounts, network, '0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82', '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c', 3000, '0x03'); break; + case utils.kccMainnet: + m.log("waiting controller create MJT - KSC market ......"); + await intializeMarket(accounts, network, '0x2ca48b4eea5a731c2b54e7c3944dbdb87c0cfb6f', '0x4446fc4eb47f2f6586f9faab68b3498f86c07521', 3000, '0x0d00000002'); + m.log("waiting controller create KSC - KUS market ......"); + await intializeMarket(accounts, network, '0x4446fc4eb47f2f6586f9faab68b3498f86c07521', '0x4a81704d8c16d9fb0d7f61b747d0b5a272badf14', 3000, '0x0e00000002'); + break; } } diff --git a/migrations/2_deploy_basic.js b/migrations/2_deploy_basic.js old mode 100644 new mode 100755 index 53a435d..dfec501 --- a/migrations/2_deploy_basic.js +++ b/migrations/2_deploy_basic.js @@ -3,6 +3,7 @@ const xOLE = artifacts.require("XOLE"); const xOLEDelegator = artifacts.require("XOLEDelegator"); const EthDexAggregatorV1 = artifacts.require("EthDexAggregatorV1"); const BscDexAggregatorV1 = artifacts.require("BscDexAggregatorV1"); +const KccDexAggregatorV1 = artifacts.require("KccDexAggregatorV1"); const DexAggregatorDelegator = artifacts.require("DexAggregatorDelegator"); const Gov = artifacts.require("GovernorAlpha"); const Timelock = artifacts.require("Timelock"); @@ -42,7 +43,7 @@ module.exports = async function (deployer, network, accounts) { } //airdrop - await deployer.deploy(Airdrop, oleAddr, utils.deployOption(accounts)); + //await deployer.deploy(Airdrop, oleAddr, utils.deployOption(accounts)); //dexAgg switch (network) { case utils.bscIntegrationTest: @@ -50,6 +51,10 @@ module.exports = async function (deployer, network, accounts) { await deployer.deploy(BscDexAggregatorV1, utils.deployOption(accounts)); await deployer.deploy(DexAggregatorDelegator, utils.uniswapV2Address(network), utils.uniswapV3Address(network), adminCtr, BscDexAggregatorV1.address, utils.deployOption(accounts)); break; + case utils.kccMainnet: + await deployer.deploy(KccDexAggregatorV1, utils.deployOption(accounts)); + await deployer.deploy(DexAggregatorDelegator, utils.uniswapV2Address(network), utils.uniswapV3Address(network), adminCtr, KccDexAggregatorV1.address, utils.deployOption(accounts)); + break; default: await deployer.deploy(EthDexAggregatorV1, utils.deployOption(accounts)); await deployer.deploy(DexAggregatorDelegator, utils.uniswapV2Address(network), utils.uniswapV3Address(network), adminCtr, EthDexAggregatorV1.address, utils.deployOption(accounts)); @@ -59,9 +64,9 @@ module.exports = async function (deployer, network, accounts) { await deployer.deploy(xOLE, utils.deployOption(accounts)); await deployer.deploy(xOLEDelegator, oleAddr, DexAggregatorDelegator.address, 3000, dev, adminCtr, xOLE.address, utils.deployOption(accounts)); //gov - await deployer.deploy(Gov, Timelock.address, xOLEDelegator.address, adminAccount, utils.deployOption(accounts)); + //await deployer.deploy(Gov, Timelock.address, xOLEDelegator.address, adminAccount, utils.deployOption(accounts)); //reserve - await deployer.deploy(Reserve, adminCtr, oleAddr, utils.deployOption(accounts)); + //await deployer.deploy(Reserve, adminCtr, oleAddr, utils.deployOption(accounts)); //controller await deployer.deploy(LPool, utils.deployOption(accounts)); await deployer.deploy(ControllerV1, utils.deployOption(accounts)); @@ -70,6 +75,9 @@ module.exports = async function (deployer, network, accounts) { case utils.bscTestnet: await deployer.deploy(ControllerDelegator, oleAddr, xOLEDelegator.address, weth9, LPool.address, utils.zeroAddress, DexAggregatorDelegator.address, '0x03', adminCtr, ControllerV1.address, utils.deployOption(accounts)); break; + case utils.kccMainnet: + await deployer.deploy(ControllerDelegator, oleAddr, xOLEDelegator.address, weth9, LPool.address, utils.zeroAddress, DexAggregatorDelegator.address, '0x0d', adminCtr, ControllerV1.address, utils.deployOption(accounts)); + break; default: await deployer.deploy(ControllerDelegator, oleAddr, xOLEDelegator.address, weth9, LPool.address, utils.zeroAddress, DexAggregatorDelegator.address, '0x02000bb8', adminCtr, ControllerV1.address, utils.deployOption(accounts)); } @@ -82,6 +90,9 @@ module.exports = async function (deployer, network, accounts) { case utils.bscTestnet: await deployer.deploy(OpenLevDelegator, ControllerDelegator.address, DexAggregatorDelegator.address, utils.getDepositTokens(network), weth9, xOLEDelegator.address, [3, 11, 12], adminCtr, OpenLevV1.address, utils.deployOption(accounts)); break; + case utils.kccMainnet: + await deployer.deploy(OpenLevDelegator, ControllerDelegator.address, DexAggregatorDelegator.address, utils.getDepositTokens(network), weth9, xOLEDelegator.address, [13, 14], adminCtr, OpenLevV1.address, utils.deployOption(accounts)); + break; default: await deployer.deploy(OpenLevDelegator, ControllerDelegator.address, DexAggregatorDelegator.address, utils.getDepositTokens(network), weth9, xOLEDelegator.address, [1, 2], adminCtr, OpenLevV1.address, utils.deployOption(accounts)); } @@ -98,6 +109,11 @@ module.exports = async function (deployer, network, accounts) { await (await Timelock.at(Timelock.address)).executeTransaction(DexAggregatorDelegator.address, 0, 'setDexInfo(uint8[],address[],uint16[])', encodeParameters(['uint8[]', 'address[]', 'uint16[]'], [[11, 12], ['0xbcfccbde45ce874adcb698cc183debcf17952812', '0x86407bea2078ea5f5eb5a52b2caa963bc1f889da'], [20, 20]]), 0); + }else if (network == utils.kccMainnet) { + m.log("Waiting dexAgg set factory ......"); + await (await Timelock.at(Timelock.address)).executeTransaction(DexAggregatorDelegator.address, 0, 'setDexInfo(uint8[],address[],uint16[])', + encodeParameters(['uint8[]', 'address[]', 'uint16[]'], + [[14], ['0xAE46cBBCDFBa3bE0F02F463Ec5486eBB4e2e65Ae'], [10]]), 0); } }; From bbaf53dd3d2ae6627ffbf50f828f05a8d7232e69 Mon Sep 17 00:00:00 2001 From: Paul-D-Ant <1611768193@qq.com> Date: Thu, 24 Mar 2022 15:32:52 +0800 Subject: [PATCH 2/7] add kcc chain --- contracts/dex/kcc/KccDexAggregatorV1.sol | 198 +++++++++++++++++ contracts/dex/kcc/KccUniV2Dex.sol | 268 +++++++++++++++++++++++ 2 files changed, 466 insertions(+) create mode 100755 contracts/dex/kcc/KccDexAggregatorV1.sol create mode 100755 contracts/dex/kcc/KccUniV2Dex.sol diff --git a/contracts/dex/kcc/KccDexAggregatorV1.sol b/contracts/dex/kcc/KccDexAggregatorV1.sol new file mode 100755 index 0000000..cc4c58a --- /dev/null +++ b/contracts/dex/kcc/KccDexAggregatorV1.sol @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.7.6; +pragma experimental ABIEncoderV2; + +import "./KccUniV2Dex.sol"; +import "../DexAggregatorInterface.sol"; +import "../../lib/DexData.sol"; +import "../../lib/Utils.sol"; +import "@openzeppelin/contracts/math/SafeMath.sol"; +import "../../DelegateInterface.sol"; +import "../../Adminable.sol"; + +/// @title Swap logic on KCC +/// @author OpenLeverage +/// @notice Use this contract to swap tokens. +/// @dev Routers for different swap requests. +contract KccDexAggregatorV1 is DelegateInterface, Adminable, DexAggregatorInterface, KccUniV2Dex { + using DexData for bytes; + using SafeMath for uint; + + mapping(IUniswapV2Pair => V2PriceOracle) public uniV2PriceOracle; + IUniswapV2Factory public mojitoFactory; + address public openLev; + uint8 private constant priceDecimals = 18; + + mapping(uint8 => DexInfo) public dexInfo; + + //mojitoSwapFactory: 0x79855A03426e15Ad120df77eFA623aF87bd54eF3 、kuSwapFactory: 0xAE46cBBCDFBa3bE0F02F463Ec5486eBB4e2e65Ae + function initialize( + IUniswapV2Factory _mojitoFactory, + address _unusedFactory + ) public { + require(msg.sender == admin, "Not admin"); + _unusedFactory; + mojitoFactory = _mojitoFactory; + dexInfo[DexData.DEX_MOJITO] = DexInfo(_mojitoFactory, 30); + } + + /// @notice Save factories of the dex. + /// @param dexName Index of Dex. find list of dex in contracts/lib/DexData.sol. + /// @param factoryAddr Factory address of Different dex forked from uniswap. + /// @param fees Swap fee collects by. + function setDexInfo(uint8[] memory dexName, IUniswapV2Factory[] memory factoryAddr, uint16[] memory fees) external override onlyAdmin { + require(dexName.length == factoryAddr.length && dexName.length == fees.length, 'EOR'); + for (uint i = 0; i < dexName.length; i++) { + dexInfo[dexName[i]] = DexInfo(factoryAddr[i], fees[i]); + } + } + + /// @dev SetOpenlev address to update dex price + function setOpenLev(address _openLev) external onlyAdmin { + require(address(0) != _openLev, '0x'); + openLev = _openLev; + } + + + /// @notice Sell tokens + /// @dev Sell exact amount of token with tax applied + /// @param buyToken Address of token transfer from Dex pair + /// @param sellToken Address of token transfer into Dex pair + /// @param sellAmount Exact amount to sell + /// @param minBuyAmount minmum amount of token to receive. + /// @param data Dex to use for swap + /// @return buyAmount Exact Amount bought + function sell(address buyToken, address sellToken, uint sellAmount, uint minBuyAmount, bytes memory data) external override returns (uint buyAmount){ + address payer = msg.sender; + buyAmount = uniClassSell(dexInfo[data.toDex()], buyToken, sellToken, sellAmount, minBuyAmount, payer, payer); + } + + /// @notice Sell tokens + /// @dev Sell exact amount of token through path + /// @param sellAmount Exact amount to sell + /// @param minBuyAmount minmum amount of token to receive. + /// @param data Dex to use for swap and path of the swap + /// @return buyAmount Exact amount bought + function sellMul(uint sellAmount, uint minBuyAmount, bytes memory data) external override returns (uint buyAmount){ + buyAmount = uniClassSellMul(dexInfo[data.toDex()], sellAmount, minBuyAmount, data.toUniV2Path()); + } + + /// @notice Buy tokens + /// @dev Buy exact amount of token with tax applied + /// @param buyToken Address of token transfer from Dex pair + /// @param sellToken Address of token transfer into Dex pair + /// @param buyTax Tax applyed by buyToken while transfer from Dex pair + /// @param sellTax Tax applyed by sellToken while transfer into Dex pair + /// @param buyAmount Exact amount to buy + /// @param maxSellAmount maximum amount of token to receive. + /// @param data Dex to use for swap + /// @return sellAmount Exact amount sold + function buy(address buyToken, address sellToken, uint24 buyTax, uint24 sellTax, uint buyAmount, uint maxSellAmount, bytes memory data) external override returns (uint sellAmount){ + sellAmount = uniClassBuy(dexInfo[data.toDex()], buyToken, sellToken, buyAmount, maxSellAmount, buyTax, sellTax); + } + + /// @notice Calculate amount of token to buy + /// @dev Calculate exact amount of token to buy with tax applied + /// @param buyToken Address of token transfer from Dex pair + /// @param sellToken Address of token transfer into Dex pair + /// @param buyTax Tax applyed by buyToken while transfer from Dex pair + /// @param sellTax Tax applyed by sellToken while transfer into Dex pair + /// @param sellAmount Exact amount to sell + /// @param data Dex to use for swap + /// @return buyAmount Amount of buyToken would bought + function calBuyAmount(address buyToken, address sellToken, uint24 buyTax, uint24 sellTax, uint sellAmount, bytes memory data) external view override returns (uint buyAmount) { + sellAmount = Utils.toAmountAfterTax(sellAmount, sellTax); + buyAmount = uniClassCalBuyAmount(dexInfo[data.toDex()], buyToken, sellToken, sellAmount); + buyAmount = Utils.toAmountAfterTax(buyAmount, buyTax); + } + + /// @notice Calculate amount of token to sell + /// @dev Calculate exact amount of token to sell with tax applied + /// @param buyToken Address of token transfer from Dex pair + /// @param sellToken Address of token transfer into Dex pair + /// @param buyTax Tax applyed by buyToken while transfer from Dex pair + /// @param sellTax Tax applyed by SellToken while transfer into Dex pair + /// @param buyAmount Exact amount to buy + /// @param data Dex to use for swap + /// @return sellAmount Amount of sellToken would sold + function calSellAmount(address buyToken, address sellToken, uint24 buyTax, uint24 sellTax, uint buyAmount, bytes memory data) external view override returns (uint sellAmount){ + sellAmount = uniClassCalSellAmount(dexInfo[data.toDex()], buyToken, sellToken, buyAmount, buyTax, sellTax); + } + + /// @notice Get price + /// @dev Get current price of desToken / quoteToken + /// @param desToken Token to be priced + /// @param quoteToken Token used for pricing + /// @param data Dex to use for swap + function getPrice(address desToken, address quoteToken, bytes memory data) external view override returns (uint256 price, uint8 decimals){ + decimals = priceDecimals; + price = uniClassGetPrice(dexInfo[data.toDex()].factory, desToken, quoteToken, decimals); + } + + /// @dev Get average price of desToken / quoteToken in the last period of time + /// @param desToken Token to be priced + /// @param quoteToken Token used for pricing + /// @param secondsAgo Time period of the average + /// @param data Dex to use for swap + function getAvgPrice(address desToken, address quoteToken, uint32 secondsAgo, bytes memory data) external view override returns (uint256 price, uint8 decimals, uint256 timestamp){ + require(data.isUniV2Class(), "unsupported dex"); + // Shh - currently unused + secondsAgo; + decimals = priceDecimals; + address pair = getUniClassPair(desToken, quoteToken, dexInfo[data.toDex()].factory); + V2PriceOracle memory priceOracle = uniV2PriceOracle[IUniswapV2Pair(pair)]; + (price, timestamp) = uniClassGetAvgPrice(desToken, quoteToken, priceOracle); + } + + /// @notice Fet current and history price + /// @param desToken Token to be priced + /// @param quoteToken Token used for pricing + /// @param secondsAgo not used on BSC + /// @param dexData dex parameters + /// @return price Real-time price + /// @return cAvgPrice Current TWAP price + /// @return hAvgPrice Historical TWAP price + /// @return decimals Token price decimal + /// @return timestamp Last TWAP price update timestamp + function getPriceCAvgPriceHAvgPrice( + address desToken, + address quoteToken, + uint32 secondsAgo, + bytes memory dexData + ) external view override returns (uint price, uint cAvgPrice, uint256 hAvgPrice, uint8 decimals, uint256 timestamp){ + require(dexData.isUniV2Class(), "unsupported dex"); + secondsAgo; + decimals = priceDecimals; + address pair = getUniClassPair(desToken, quoteToken, dexInfo[dexData.toDex()].factory); + V2PriceOracle memory priceOracle = uniV2PriceOracle[IUniswapV2Pair(pair)]; + (price, cAvgPrice, hAvgPrice, timestamp) = uniClassGetPriceCAvgPriceHAvgPrice(pair, priceOracle, desToken, quoteToken, decimals); + } + + /// @dev Update Dex price if not updated over time window + /// @param desToken Token to be priced + /// @param quoteToken Token used for pricing + /// @param timeWindow minmum time gap between two updates + /// @param data dex parameters + /// @return If updated + function updatePriceOracle(address desToken, address quoteToken, uint32 timeWindow, bytes memory data) external override returns (bool){ + require(msg.sender == openLev, "Only openLev can update price"); + require(data.isUniV2Class(), "unsupported dex"); + address pair = getUniClassPair(desToken, quoteToken, dexInfo[data.toDex()].factory); + V2PriceOracle memory priceOracle = uniV2PriceOracle[IUniswapV2Pair(pair)]; + (V2PriceOracle memory updatedPriceOracle, bool updated) = uniClassUpdatePriceOracle(pair, priceOracle, timeWindow, priceDecimals); + if (updated) { + uniV2PriceOracle[IUniswapV2Pair(pair)] = updatedPriceOracle; + } + return updated; + } + + /// @dev Update UniV3 observations + /// @param desToken Token to be priced + /// @param quoteToken Token used for pricing + /// @param data Dex parameters + function updateV3Observation(address desToken, address quoteToken, bytes memory data) external pure override { + // Shh - currently unused + (desToken,quoteToken, data); + revert("Not implemented"); + } +} diff --git a/contracts/dex/kcc/KccUniV2Dex.sol b/contracts/dex/kcc/KccUniV2Dex.sol new file mode 100755 index 0000000..e774fed --- /dev/null +++ b/contracts/dex/kcc/KccUniV2Dex.sol @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.7.6; +pragma experimental ABIEncoderV2; + +import "@openzeppelin/contracts/math/SafeMath.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol"; +import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol"; +import "../../lib/TransferHelper.sol"; +import "../../lib/DexData.sol"; +import "../../lib/Utils.sol"; + +contract KccUniV2Dex { + using SafeMath for uint; + using Utils for uint; + using TransferHelper for IERC20; + + struct V2PriceOracle { + uint32 blockTimestampLast; // Last block timestamp when price updated + uint price0; // recorded price for token0 + uint price1; // recorded price for token1 + uint price0CumulativeLast; // Cumulative TWAP for token0 + uint price1CumulativeLast; // Cumulative TWAP for token1 + } + + struct DexInfo { + IUniswapV2Factory factory; + uint16 fees; + } + + function uniClassSell(DexInfo memory dexInfo, + address buyToken, + address sellToken, + uint sellAmount, + uint minBuyAmount, + address payer, + address payee + ) internal returns (uint buyAmount){ + address pair = getUniClassPair(buyToken, sellToken, dexInfo.factory); + IUniswapV2Pair(pair).sync(); + sellAmount = transferOut(IERC20(sellToken), payer, pair, sellAmount); + (uint256 token0Reserves, uint256 token1Reserves,) = IUniswapV2Pair(pair).getReserves(); + sellAmount = buyToken < sellToken ? IERC20(sellToken).balanceOf(pair).sub(token1Reserves) : IERC20(sellToken).balanceOf(pair).sub(token0Reserves); + + uint balanceBefore = IERC20(buyToken).balanceOf(payee); + dexInfo.fees = getPairFees(dexInfo, pair); + + if (buyToken < sellToken) { + buyAmount = getAmountOut(sellAmount, token1Reserves, token0Reserves, dexInfo.fees); + IUniswapV2Pair(pair).swap(buyAmount, 0, payee, ""); + } else { + buyAmount = getAmountOut(sellAmount, token0Reserves, token1Reserves, dexInfo.fees); + IUniswapV2Pair(pair).swap(0, buyAmount, payee, ""); + } + buyAmount = IERC20(buyToken).balanceOf(payee).sub(balanceBefore); + require(buyAmount >= minBuyAmount, 'buy amount less than min'); + } + + function uniClassSellMul(DexInfo memory dexInfo, uint sellAmount, uint minBuyAmount, address[] memory tokens) + internal returns (uint buyAmount){ + for (uint i = 1; i < tokens.length; i++) { + address sellToken = tokens[i - 1]; + address buyToken = tokens[i]; + bool isLast = i == tokens.length - 1; + address payer = i == 1 ? msg.sender : address(this); + address payee = isLast ? msg.sender : address(this); + buyAmount = uniClassSell(dexInfo, buyToken, sellToken, sellAmount, 0, payer, payee); + if (!isLast) { + sellAmount = buyAmount; + } + } + require(buyAmount >= minBuyAmount, 'buy amount less than min'); + } + + function uniClassBuy( + DexInfo memory dexInfo, + address buyToken, + address sellToken, + uint buyAmount, + uint maxSellAmount, + uint24 buyTokenFeeRate, + uint24 sellTokenFeeRate) + internal returns (uint sellAmount){ + address pair = getUniClassPair(buyToken, sellToken, dexInfo.factory); + IUniswapV2Pair(pair).sync(); + (uint256 token0Reserves, uint256 token1Reserves,) = IUniswapV2Pair(pair).getReserves(); + uint balanceBefore = IERC20(buyToken).balanceOf(msg.sender); + dexInfo.fees = getPairFees(dexInfo, pair); + if (buyToken < sellToken) { + sellAmount = getAmountIn(buyAmount.toAmountBeforeTax(buyTokenFeeRate), token1Reserves, token0Reserves, dexInfo.fees); + sellAmount = sellAmount.toAmountBeforeTax(sellTokenFeeRate); + require(sellAmount <= maxSellAmount, 'sell amount not enough'); + transferOut(IERC20(sellToken), msg.sender, pair, sellAmount); + IUniswapV2Pair(pair).swap(buyAmount.toAmountBeforeTax(buyTokenFeeRate), 0, msg.sender, ""); + } else { + sellAmount = getAmountIn(buyAmount.toAmountBeforeTax(buyTokenFeeRate), token0Reserves, token1Reserves, dexInfo.fees); + sellAmount = sellAmount.toAmountBeforeTax(sellTokenFeeRate); + require(sellAmount <= maxSellAmount, 'sell amount not enough'); + transferOut(IERC20(sellToken), msg.sender, pair, sellAmount); + IUniswapV2Pair(pair).swap(0, buyAmount.toAmountBeforeTax(buyTokenFeeRate), msg.sender, ""); + } + + uint balanceAfter = IERC20(buyToken).balanceOf(msg.sender); + require(buyAmount <= balanceAfter.sub(balanceBefore), "wrong amount bought"); + } + + function uniClassCalBuyAmount(DexInfo memory dexInfo, address buyToken, address sellToken, uint sellAmount) internal view returns (uint) { + address pair = getUniClassPair(buyToken, sellToken, dexInfo.factory); + (uint256 token0Reserves, uint256 token1Reserves,) = IUniswapV2Pair(pair).getReserves(); + if (buyToken < sellToken) { + return getAmountOut(sellAmount, token1Reserves, token0Reserves, getPairFees(dexInfo, pair)); + } else { + return getAmountOut(sellAmount, token0Reserves, token1Reserves, getPairFees(dexInfo, pair)); + } + } + + function uniClassCalSellAmount( + DexInfo memory dexInfo, + address buyToken, + address sellToken, + uint buyAmount, + uint24 buyTokenFeeRate, + uint24 sellTokenFeeRate) internal view returns (uint sellAmount) { + address pair = getUniClassPair(buyToken, sellToken, dexInfo.factory); + (uint256 token0Reserves, uint256 token1Reserves,) = IUniswapV2Pair(pair).getReserves(); + sellAmount = buyToken < sellToken ? + getAmountIn(buyAmount.toAmountBeforeTax(buyTokenFeeRate), token1Reserves, token0Reserves, getPairFees(dexInfo, pair)) : + getAmountIn(buyAmount.toAmountBeforeTax(buyTokenFeeRate), token0Reserves, token1Reserves, getPairFees(dexInfo, pair)); + + return sellAmount.toAmountBeforeTax(sellTokenFeeRate); + } + + function uniClassGetPrice(IUniswapV2Factory factory, address desToken, address quoteToken, uint8 decimals) internal view returns (uint256){ + address pair = getUniClassPair(desToken, quoteToken, factory); + (uint256 token0Reserves, uint256 token1Reserves,) = IUniswapV2Pair(pair).getReserves(); + return desToken == IUniswapV2Pair(pair).token0() ? + token1Reserves.mul(10 ** decimals).div(token0Reserves) : + token0Reserves.mul(10 ** decimals).div(token1Reserves); + } + + function uniClassGetAvgPrice(address desToken, address quoteToken, V2PriceOracle memory priceOracle) internal pure returns (uint256 price, uint256 timestamp){ + timestamp = priceOracle.blockTimestampLast; + price = desToken < quoteToken ? uint(priceOracle.price0) : uint(priceOracle.price1); + } + + + function uniClassGetPriceCAvgPriceHAvgPrice(address pair, V2PriceOracle memory priceOracle, address desToken, address quoteToken, uint8 decimals) + internal view returns (uint price, uint cAvgPrice, uint256 hAvgPrice, uint256 timestamp){ + bool isToken0 = desToken < quoteToken; + (uint256 token0Reserves, uint256 token1Reserves, uint32 uniBlockTimeLast) = IUniswapV2Pair(pair).getReserves(); + price = isToken0 ? + token1Reserves.mul(10 ** decimals).div(token0Reserves) : + token0Reserves.mul(10 ** decimals).div(token1Reserves); + + hAvgPrice = isToken0 ? uint(priceOracle.price0) : uint(priceOracle.price1); + timestamp = priceOracle.blockTimestampLast; + + if (uniBlockTimeLast <= priceOracle.blockTimestampLast) { + cAvgPrice = hAvgPrice; + } else { + uint32 timeElapsed = uniBlockTimeLast - priceOracle.blockTimestampLast; + cAvgPrice = uint256(isToken0 ? + calTPrice(IUniswapV2Pair(pair).price0CumulativeLast(), priceOracle.price0CumulativeLast, timeElapsed, decimals) : + calTPrice(IUniswapV2Pair(pair).price1CumulativeLast(), priceOracle.price1CumulativeLast, timeElapsed, decimals)); + } + } + + function uniClassUpdatePriceOracle(address pair, V2PriceOracle memory priceOracle, uint32 timeWindow, uint8 decimals) internal returns (V2PriceOracle memory, bool updated) { + uint32 currentBlockTime = toUint32(block.timestamp); + if (currentBlockTime < (priceOracle.blockTimestampLast + timeWindow)) { + return (priceOracle, false); + } + IUniswapV2Pair(pair).sync(); + uint32 timeElapsed = currentBlockTime - priceOracle.blockTimestampLast; + uint currentPrice0CumulativeLast = IUniswapV2Pair(pair).price0CumulativeLast(); + uint currentPrice1CumulativeLast = IUniswapV2Pair(pair).price1CumulativeLast(); + if (priceOracle.blockTimestampLast != 0) { + priceOracle.price0 = calTPrice(currentPrice0CumulativeLast, priceOracle.price0CumulativeLast, timeElapsed, decimals); + priceOracle.price1 = calTPrice(currentPrice1CumulativeLast, priceOracle.price1CumulativeLast, timeElapsed, decimals); + } + priceOracle.price0CumulativeLast = currentPrice0CumulativeLast; + priceOracle.price1CumulativeLast = currentPrice1CumulativeLast; + priceOracle.blockTimestampLast = currentBlockTime; + return (priceOracle, true); + } + + function calTPrice(uint currentPriceCumulativeLast, uint historyPriceCumulativeLast, uint32 timeElapsed, uint8 decimals) + internal pure returns (uint){ + return ((currentPriceCumulativeLast.sub(historyPriceCumulativeLast).mul(10 ** decimals)) >> 112).div(timeElapsed); + } + + function toUint32(uint256 y) internal pure returns (uint32 z) { + require((z = uint32(y)) == y); + } + + function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut, uint16 fees) private pure returns (uint amountOut) + { + require(amountIn > 0, 'INSUFFICIENT_INPUT_AMOUNT'); + require(reserveIn > 0 && reserveOut > 0, 'INSUFFICIENT_LIQUIDITY'); + uint amountInWithFee = amountIn.mul(uint(10000).sub(fees)); + uint numerator = amountInWithFee.mul(reserveOut); + uint denominator = reserveIn.mul(10000).add(amountInWithFee); + amountOut = numerator / denominator; + } + + function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut, uint16 fees) private pure returns (uint amountIn) { + require(amountOut > 0, 'INSUFFICIENT_OUTPUT_AMOUNT'); + require(reserveIn > 0 && reserveOut > 0, 'INSUFFICIENT_LIQUIDITY'); + uint numerator = reserveIn.mul(amountOut).mul(10000); + uint denominator = reserveOut.sub(amountOut).mul(uint(10000).sub(fees)); + amountIn = (numerator / denominator).add(1); + } + + function transferOut(IERC20 token, address payer, address to, uint amount) private returns (uint256 amountReceived) { + if (payer == address(this)) { + amountReceived = token.safeTransfer(to, amount); + } else { + amountReceived = token.safeTransferFrom(payer, to, amount); + } + } + + function getUniClassPair(address tokenA, address tokenB, IUniswapV2Factory factory) internal view returns (address pair){ + (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); + if (address(factory) == 0x79855A03426e15Ad120df77eFA623aF87bd54eF3) { + // mojito + return address(uint(keccak256(abi.encodePacked( + hex'ff', + address(factory), + keccak256(abi.encodePacked(token0, token1)), + hex'3b58864b0ea7cc084fc3a5dc3ca7ea2fb5cedd9aac7f9fff0c3dd9a15713f1c7' + )))); + } else if (address(factory) == 0xAE46cBBCDFBa3bE0F02F463Ec5486eBB4e2e65Ae) { + // ku + return address(uint(keccak256(abi.encodePacked( + hex'ff', + address(factory), + keccak256(abi.encodePacked(token0, token1)), + hex'5d71f8561d80ed979ca73d5b742278f4719baab5f0dd78b6ca91bec31f0e2dbc' + )))); + } else { + return factory.getPair(tokenA, tokenB); + } + } + + function getPairFees(DexInfo memory dexInfo, address pair) private view returns (uint16){ + if (address(dexInfo.factory) == 0x79855A03426e15Ad120df77eFA623aF87bd54eF3) { + // mojito + return toUint16((IMojitoPair)(pair).swapFeeNumerator()); + } else if (address(dexInfo.factory) == 0xAE46cBBCDFBa3bE0F02F463Ec5486eBB4e2e65Ae) { + // ku + return toUint16((IKuswapPair)(pair).swapFee()); + } else { + return dexInfo.fees; + } + } + + function toUint16(uint256 y) internal pure returns (uint16 z) { + require((z = uint16(y)) == y); + } +} + +interface IMojitoPair { + function swapFeeNumerator() external view returns (uint); +} + +interface IKuswapPair { + function swapFee() external view returns (uint32); +} \ No newline at end of file From 606d45693a50113928ede3227cf79e084ec2db39 Mon Sep 17 00:00:00 2001 From: Paul-D-Ant <1611768193@qq.com> Date: Mon, 28 Mar 2022 16:29:17 +0800 Subject: [PATCH 3/7] add kcc chain --- contracts/dex/kcc/KccUniV2Dex.sol | 2 +- contracts/lib/DexData.sol | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/contracts/dex/kcc/KccUniV2Dex.sol b/contracts/dex/kcc/KccUniV2Dex.sol index e774fed..ecc8629 100755 --- a/contracts/dex/kcc/KccUniV2Dex.sol +++ b/contracts/dex/kcc/KccUniV2Dex.sol @@ -248,7 +248,7 @@ contract KccUniV2Dex { return toUint16((IMojitoPair)(pair).swapFeeNumerator()); } else if (address(dexInfo.factory) == 0xAE46cBBCDFBa3bE0F02F463Ec5486eBB4e2e65Ae) { // ku - return toUint16((IKuswapPair)(pair).swapFee()); + return toUint16((uint(10)).mul((IKuswapPair)(pair).swapFee())); } else { return dexInfo.fees; } diff --git a/contracts/lib/DexData.sol b/contracts/lib/DexData.sol index 5c511f6..2795351 100644 --- a/contracts/lib/DexData.sol +++ b/contracts/lib/DexData.sol @@ -31,6 +31,8 @@ library DexData { uint8 constant DEX_APE = 10; uint8 constant DEX_PANCAKEV1 = 11; uint8 constant DEX_BABY = 12; + uint8 constant DEX_MOJITO = 13; + uint8 constant DEX_KU = 14; struct V3PoolData { address tokenA; From 9c54b7cca49c13bee0c45f7a5d5a056daa7e091f Mon Sep 17 00:00:00 2001 From: Paul-D-Ant <1611768193@qq.com> Date: Mon, 28 Mar 2022 16:37:54 +0800 Subject: [PATCH 4/7] add kcc chain --- migrations/util.js | 234 ++++++++++++++++++++++++--------------------- 1 file changed, 123 insertions(+), 111 deletions(-) diff --git a/migrations/util.js b/migrations/util.js index 3b2df18..9ee9cf6 100644 --- a/migrations/util.js +++ b/migrations/util.js @@ -3,153 +3,165 @@ let kovan = exports.kovan = 'kovan'; let bscTestnet = exports.bscTestnet = 'bscTestnet'; let bscIntegrationTest = exports.bscIntegrationTest = 'bscIntegrationTest'; let mainnet = exports.mainnet = 'mainnet'; - +let kccMainnet = exports.kccMainnet = 'kccMainnet'; exports.isSkip = function (network) { - return network == ('development') || - network == ('soliditycoverage') || - network == ('local') || - network == ('huobiMainest') || - network == ('huobiTest'); + return network == ('development') || + network == ('soliditycoverage') || + network == ('local') || + network == ('huobiMainest') || + network == ('huobiTest'); } exports.deployOption = function (accounts) { - return {from: accounts[0], overwrite: false} + return {from: accounts[0], overwrite: false} } exports.getAdmin = function (accounts) { - return accounts[0]; + return accounts[0]; } exports.uniswapV2Address = function (network) { - switch (network){ - case bscIntegrationTest: - case bscTestnet: - return '0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73'; - case kovan: - case mainnet: - return '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f'; - default: - return zeroAddress; - } + switch (network){ + case bscIntegrationTest: + case bscTestnet: + return '0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73'; + case kovan: + case mainnet: + return '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f'; + case kccMainnet: + return "0x79855A03426e15Ad120df77eFA623aF87bd54eF3"; + default: + return zeroAddress; + } } exports.uniswapV3Address = function (network) { - switch (network){ - case kovan: - case mainnet: - return '0x1f98431c8ad98523631ae4a59f267346ea31f984'; - default: - return zeroAddress; - } + switch (network){ + case kovan: + case mainnet: + return '0x1f98431c8ad98523631ae4a59f267346ea31f984'; + default: + return zeroAddress; + } } exports.getDepositTokens = function (network) { - switch (network){ - case mainnet: - return [ - "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",//wbtc - "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",//usdc - "0xdac17f958d2ee523a2206206994597c13d831ec7",//usdt - "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",//weth - "0x6b175474e89094c44da98b954eedeac495271d0f",//dai - "0x956f47f50a910163d8bf957cf5846d573e7f87ca",//fei - "0xa47c8bf37f92abed4a126bda807a7b7498661acd",//ust - "0x853d955acef822db058eb8505911ed77f175b99e"//frax - ]; - case kovan: - return [ - "0xd0A1E359811322d97991E03f863a0C30C2cF029C",//weth9 - "0xc58854ce3a7d507b1ca97fa7b28a411956c07782",//weth(test) - "0xf894289f63b0b365485cee34aa50681f295f84b4",//usdt - "0x9278bf26744d3c98b8f24809fe8ea693b9aa4cf6",//wbtc - "0x5c95482b5962b6c3d2d47dc4a3fd7173e99853b0",//dai - "0x7a8bd2583a3d29241da12dd6f3ae88e92a538144"//usdc - ]; - case bscIntegrationTest: - return [ - "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",//WBNB - "0xe9e7cea3dedca5984780bafc599bd69add087d56"//BUSD - ]; - case bscTestnet: - return [ - "0x094616f0bdfb0b526bd735bf66eca0ad254ca81f",//WBNB - "0x8301f2213c0eed49a7e28ae4c3e91722919b8b47"//BUSD - ] - default: - return []; - } + switch (network){ + case mainnet: + return [ + "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",//wbtc + "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",//usdc + "0xdac17f958d2ee523a2206206994597c13d831ec7",//usdt + "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",//weth + "0x6b175474e89094c44da98b954eedeac495271d0f",//dai + "0x956f47f50a910163d8bf957cf5846d573e7f87ca",//fei + "0xa47c8bf37f92abed4a126bda807a7b7498661acd",//ust + "0x853d955acef822db058eb8505911ed77f175b99e"//frax + ]; + case kovan: + return [ + "0xd0A1E359811322d97991E03f863a0C30C2cF029C",//weth9 + "0xc58854ce3a7d507b1ca97fa7b28a411956c07782",//weth(test) + "0xf894289f63b0b365485cee34aa50681f295f84b4",//usdt + "0x9278bf26744d3c98b8f24809fe8ea693b9aa4cf6",//wbtc + "0x5c95482b5962b6c3d2d47dc4a3fd7173e99853b0",//dai + "0x7a8bd2583a3d29241da12dd6f3ae88e92a538144"//usdc + ]; + case bscIntegrationTest: + return [ + "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",//WBNB + "0xe9e7cea3dedca5984780bafc599bd69add087d56"//BUSD + ]; + case bscTestnet: + return [ + "0x094616f0bdfb0b526bd735bf66eca0ad254ca81f",//WBNB + "0x8301f2213c0eed49a7e28ae4c3e91722919b8b47"//BUSD + ] + default: + return []; + } } exports.blocksPerYear = function (network) { - switch (network){ - case kovan: - case mainnet: - return 2102400; - case bscIntegrationTest: - case bscTestnet: - return 10512000; - } + switch (network){ + case kovan: + case mainnet: + return 2102400; + case bscIntegrationTest: + case bscTestnet: + return 10512000; + case kccMainnet: + return 10512000; + } } exports.tokenName = function (network) { - switch (network){ - case bscIntegrationTest: - case bscTestnet: - return "ELO"; - default: - return "OpenLeverage"; - } + switch (network){ + case bscIntegrationTest: + case bscTestnet: + return "ELO"; + case kccMainnet: + return "ELO"; + default: + return "OpenLeverage"; + } } exports.tokenSymbol = function (network) { - switch (network){ - case bscIntegrationTest: - case bscTestnet: - return "ELO" - default: - return "OLE"; - } + switch (network){ + case bscIntegrationTest: + case bscTestnet: + return "ELO" + case kccMainnet: + return "ELO"; + default: + return "OLE"; + } } exports.getWChainToken = function (network) { - switch (network){ - case mainnet: - //WETH9 - return "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" - case kovan: - //WETH9 - return "0xd0A1E359811322d97991E03f863a0C30C2cF029C"; - case bscIntegrationTest: - return "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"; - case bscTestnet: - return "0x094616f0bdfb0b526bd735bf66eca0ad254ca81f"; - default: - return zeroAddress; - } + switch (network){ + case mainnet: + //WETH9 + return "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + case kovan: + //WETH9 + return "0xd0A1E359811322d97991E03f863a0C30C2cF029C"; + case bscIntegrationTest: + return "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"; + case bscTestnet: + return "0x094616f0bdfb0b526bd735bf66eca0ad254ca81f"; + case kccMainnet: + return "0x4446fc4eb47f2f6586f9faab68b3498f86c07521"; + default: + return zeroAddress; + } } exports.getLpoolStartTime = function () { - //now+120s - return parseInt((new Date().getTime() + 120 * 1000).toString().substr(0, 10)); + //now+120s + return parseInt((new Date().getTime() + 120 * 1000).toString().substr(0, 10)); } exports.getFarmingStartTime = function () { - //now+1h - return parseInt((new Date().getTime() + 60 * 60 * 1000).toString().substr(0, 10)); + //now+1h + return parseInt((new Date().getTime() + 60 * 60 * 1000).toString().substr(0, 10)); } exports.getFarmingDuration = function () { - //8 weeks - return 8 * 7 * 24 * 60 * 60; + //8 weeks + return 8 * 7 * 24 * 60 * 60; } exports.getUniV2DexData = function (network){ - switch (network){ - case kovan: - case mainnet: - return "0x01"; - case bscIntegrationTest: - case bscTestnet: - return "0x03"; - default: - return zeroAddress; - } + switch (network){ + case kovan: + case mainnet: + return "0x01"; + case bscIntegrationTest: + case bscTestnet: + return "0x03"; + case kccMainnet: + return "0x0d"; + default: + return zeroAddress; + } } // const UniswapV2Router = artifacts.require("IUniswapV2Router"); From a9593b66a046f970ce37321ba790561c394d5f5d Mon Sep 17 00:00:00 2001 From: Paul-D-Ant <1611768193@qq.com> Date: Thu, 31 Mar 2022 10:38:54 +0800 Subject: [PATCH 5/7] add kcc chain --- migrations/2_deploy_basic.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/migrations/2_deploy_basic.js b/migrations/2_deploy_basic.js index dfec501..ae7de94 100755 --- a/migrations/2_deploy_basic.js +++ b/migrations/2_deploy_basic.js @@ -37,13 +37,16 @@ module.exports = async function (deployer, network, accounts) { case utils.bscTestnet: oleAddr = '0xa865197a84e780957422237b5d152772654341f3'; break; + case utils.kccMainnet: + oleAddr = '0x1ccca1ce62c62f7be95d4a67722a8fdbed6eecb4'; + break; default: await deployer.deploy(OLEToken, adminAccount, adminCtr, utils.tokenName(network), utils.tokenSymbol(network), utils.deployOption(accounts)); oleAddr = OLEToken.address; } //airdrop - //await deployer.deploy(Airdrop, oleAddr, utils.deployOption(accounts)); + await deployer.deploy(Airdrop, oleAddr, utils.deployOption(accounts)); //dexAgg switch (network) { case utils.bscIntegrationTest: @@ -64,9 +67,9 @@ module.exports = async function (deployer, network, accounts) { await deployer.deploy(xOLE, utils.deployOption(accounts)); await deployer.deploy(xOLEDelegator, oleAddr, DexAggregatorDelegator.address, 3000, dev, adminCtr, xOLE.address, utils.deployOption(accounts)); //gov - //await deployer.deploy(Gov, Timelock.address, xOLEDelegator.address, adminAccount, utils.deployOption(accounts)); + await deployer.deploy(Gov, Timelock.address, xOLEDelegator.address, adminAccount, utils.deployOption(accounts)); //reserve - //await deployer.deploy(Reserve, adminCtr, oleAddr, utils.deployOption(accounts)); + await deployer.deploy(Reserve, adminCtr, oleAddr, utils.deployOption(accounts)); //controller await deployer.deploy(LPool, utils.deployOption(accounts)); await deployer.deploy(ControllerV1, utils.deployOption(accounts)); From 7a80b10cfbe814f87ddd92b1a8ec618c7d914dfd Mon Sep 17 00:00:00 2001 From: Paul-D-Ant <1611768193@qq.com> Date: Thu, 31 Mar 2022 10:39:01 +0800 Subject: [PATCH 6/7] add kcc chain --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 3439165..a5dc591 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ coverage.json .debugger/generated-sources/#utility.yul yarn.lock /eth-gas-report.md +/truffle-config.js +truffle-config.js From 0797be418fe932525ee85e0c24ed91ca9cc69163 Mon Sep 17 00:00:00 2001 From: Paul-D-Ant <1611768193@qq.com> Date: Thu, 7 Apr 2022 10:07:03 +0800 Subject: [PATCH 7/7] add kcc chain --- migrations/util.js | 4 ---- package.json | 1 + truffle-config.js | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/migrations/util.js b/migrations/util.js index 9ee9cf6..92785b4 100644 --- a/migrations/util.js +++ b/migrations/util.js @@ -98,8 +98,6 @@ exports.tokenName = function (network) { case bscIntegrationTest: case bscTestnet: return "ELO"; - case kccMainnet: - return "ELO"; default: return "OpenLeverage"; } @@ -110,8 +108,6 @@ exports.tokenSymbol = function (network) { case bscIntegrationTest: case bscTestnet: return "ELO" - case kccMainnet: - return "ELO"; default: return "OLE"; } diff --git a/package.json b/package.json index 4e5362a..82af22f 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "mocha-logger": "^1.0.7", "sol-merger": "^3.1.0", "solidity-coverage": "^0.7.16", + "truffle-plugin-stdjsonin": "github:mhrsalehi/truffle-plugin-stdjsonin", "truffle-plugin-verify": "^0.5.18" } } diff --git a/truffle-config.js b/truffle-config.js index b044634..e8a4d8f 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -119,7 +119,7 @@ module.exports = { * truffle run contract-size * truffle run verify Contract --network kovan */ - plugins: ["truffle-contract-size", 'truffle-plugin-verify', "solidity-coverage"], + plugins: ["truffle-contract-size", 'truffle-plugin-verify', "solidity-coverage", 'truffle-plugin-stdjsonin'], api_keys: { etherscan: 'EWC3B6KX47HBUD1HHANE6E9TJNTAAP2BAM' },