diff --git a/.env.example b/.env.example deleted file mode 100644 index 861d3b1..0000000 --- a/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -ETHERSCAN_API_KEY=ABC123ABC123ABC123ABC123ABC123ABC1 -ROPSTEN_URL=https://eth-ropsten.alchemyapi.io/v2/ -PRIVATE_KEY=0xabc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1 diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 85f5562..0000000 --- a/.eslintignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules -artifacts -cache -coverage diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 9a75213..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,22 +0,0 @@ -module.exports = { - env: { - browser: false, - es2021: true, - mocha: true, - node: true, - }, - extends: [ - "standard", - "plugin:prettier/recommended", - "plugin:node/recommended", - ], - parserOptions: { - ecmaVersion: 12, - }, - overrides: [ - { - files: ["hardhat.config.js"], - globals: { task: true }, - }, - ], -}; diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 37aade6..0000000 --- a/.gitignore +++ /dev/null @@ -1,47 +0,0 @@ -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage -coverage.json - -# production -/build -/tmp - -# misc -.env.local - -# log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - - -node_modules - -#Hardhat files -cache -artifacts - -yarn\.lock - -\.vscode/ - -package-lock\.json - -\.env - -node_modules -.env -coverage -coverage.json -typechain - -#Hardhat files -cache -artifacts - -\.openzeppelin/ diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 2631a49..0000000 --- a/.npmignore +++ /dev/null @@ -1,3 +0,0 @@ -hardhat.config.js -scripts -test diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index f268596..0000000 --- a/.prettierignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules -artifacts -cache -coverage* -gasReporterOutput.json diff --git a/.solhint.json b/.solhint.json deleted file mode 100644 index f3e31e8..0000000 --- a/.solhint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "solhint:recommended", - "rules": { - "compiler-version": ["error", "^0.8.0"], - "func-visibility": ["warn", { "ignoreConstructors": true }] - } -} diff --git a/.solhintignore b/.solhintignore deleted file mode 100644 index 3c3629e..0000000 --- a/.solhintignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/README.md b/README.md index 1836e16..cad7973 100644 --- a/README.md +++ b/README.md @@ -1,5 +1 @@ -# multifarming - -Staking by two tokens at once **ESW** token + **Emiswap LP** token - -Reward in **ESW** tokens \ No newline at end of file +# eswmultifarming diff --git a/contracts/IRewardDistributionRecipient.sol b/contracts/IRewardDistributionRecipient.sol deleted file mode 100644 index 074bb2f..0000000 --- a/contracts/IRewardDistributionRecipient.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.2; - -import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; - -abstract contract IRewardDistributionRecipient is OwnableUpgradeable { - address public rewardDistribution; - - function notifyRewardAmount(uint256 reward) external virtual; - - modifier onlyRewardDistribution() { - require(_msgSender() == rewardDistribution, "Caller is not reward distribution"); - _; - } - - function setRewardDistribution(address _rewardDistribution) - public - /* external */ - onlyOwner - { - rewardDistribution = _rewardDistribution; - } -} diff --git a/contracts/LPTokenWrapper.sol b/contracts/LPTokenWrapper.sol deleted file mode 100644 index a053233..0000000 --- a/contracts/LPTokenWrapper.sol +++ /dev/null @@ -1,324 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.2; - -//import "hardhat/console.sol"; -import "@openzeppelin/contracts/math/SafeMath.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import "./interfaces/IEmiswap.sol"; -import "./libraries/EmiswapLib.sol"; - -interface IERC20Extented is IERC20Upgradeable { - function decimals() external view returns (uint8); -} - -contract LPTokenWrapper { - using SafeMath for uint256; - using SafeERC20Upgradeable for IERC20Upgradeable; - - // minimal time to be passed from wallet first stake to allow exit - uint256 public exitTimeOut; - - mapping(address => uint256) public exitLimits; - - // Stable coin - address public stableCoin; - - // path to stable on Emiswap dex - struct path { - address[] route; - bool isActive; - } - - // Routs to stable coin - path[] public routeToStable; - - // save hash of path to check unique - mapping(bytes32 => bool) public availableRoutes; - - // EmiFactory - IEmiswapRegistry public emiFactory; - - // is the main token (ESW) - address public stakeToken; - - // main token (ESW) supply - uint256 private _totalSupply; - - // wallet -> lp -> amount - mapping(address => mapping(address => uint256)) private _balances; - - // 0 means the token address is not in the array - mapping(address => uint256) internal tokensIndex; - // used stake tokens - address[] public stakeTokens; - - event Staked(address indexed user, address lp, uint256 lpAmount, uint256 amount); - - function totalSupply() public view returns (uint256) { - return _totalSupply; - } - - function balanceOfStakeToken(address account) public view returns (uint256) { - return _balances[account][stakeToken]; - } - - function balanceOfLPToken(address account, address lp) public view returns (uint256) { - return _balances[account][lp]; - } - - /** - * @dev admin function to setup price routes to the "stableCoin" - * @param route - route must ends with "stableCoin", and can consist only of 1 element - "stableCoin" - */ - - function addRoutes(address[] memory route) public virtual { - require(route.length > 0 && route[route.length - 1] == stableCoin, "set route to stable"); - require(!availableRoutes[keccak256(abi.encodePacked(route))], "route already added"); - availableRoutes[keccak256(abi.encodePacked(route))] = true; - routeToStable.push(path(route, true)); - } - - /** - * @dev activate/deactivate route - * @param _route array of tokens - * @param _isActive change to true/false enable/disable - */ - - function activationRoute(address[] memory _route, bool _isActive) public virtual { - for (uint256 index = 0; index < routeToStable.length; index++) { - if ( - keccak256(abi.encodePacked(routeToStable[index].route)) == keccak256(abi.encodePacked(_route)) && - routeToStable[index].isActive != _isActive - ) { - routeToStable[index].isActive = _isActive; - return; - } - } - } - - /** - * @dev get route info - * @param _route input route - * @return routeRes stored route if found - * @return isActiveRes is active flag - */ - - function getRoute(address[] memory _route) public view returns (address[] memory routeRes, bool isActiveRes) { - for (uint256 index = 0; index < routeToStable.length; index++) { - if (keccak256(abi.encodePacked(routeToStable[index].route)) == keccak256(abi.encodePacked(_route))) { - routeRes = routeToStable[index].route; - isActiveRes = routeToStable[index].isActive; - } - } - } - - /** - * @dev get route info by routeID - * @param routeID input route - * @return routeRes stored route if found - * @return isActiveRes is active flag - */ - - function getRouteInfo(uint256 routeID) public view returns (address[] memory routeRes, bool isActiveRes) { - routeRes = routeToStable[routeID].route; - isActiveRes = routeToStable[routeID].isActive; - } - - /** - * @dev stake two tokens: lp and stake token (esw) - * @param lp lp token address - * @param lpAmount lp token amount - * @param amountMax stake token maximum amount to take in - */ - - function stake( - address lp, - uint256 lpAmount, - uint256 amountMax - ) public virtual { - require(emiFactory.isPool(lp), "token incorrect or not LP"); - - // calc needful stake token amount - uint256 stakeTokenAmount = getStakeValuebyLP(lp, lpAmount); - require(stakeTokenAmount > 0 && stakeTokenAmount <= amountMax, "not enough stake token amount"); - - IERC20Upgradeable(stakeToken).safeTransferFrom(msg.sender, address(this), stakeTokenAmount); - IERC20Upgradeable(lp).safeTransferFrom(msg.sender, address(this), lpAmount); - - // incease total supply in stake token - _totalSupply = _totalSupply.add(stakeTokenAmount); - - // set balances - _balances[msg.sender][stakeToken] = _balances[msg.sender][stakeToken].add(stakeTokenAmount); - _balances[msg.sender][lp] = _balances[msg.sender][lp].add(lpAmount); - // save lp token stake, and also stakeToken by default - if (tokensIndex[lp] == 0) { - stakeTokens.push(lp); - tokensIndex[lp] = stakeTokens.length; - } - // if first stake, save exit timeout - if (exitLimits[msg.sender] == 0) { - exitLimits[msg.sender] = block.timestamp + exitTimeOut; - } - emit Staked(msg.sender, lp, lpAmount, stakeTokenAmount); - } - - /** - * @dev calcilate stake value from LP token amount - * @param _lp LP token address - * @param _lpAmount LP token amount - * @return stakeValue LP amount value nominated in stake tokens - */ - - function getStakeValuebyLP(address _lp, uint256 _lpAmount) public view returns (uint256 stakeValue) { - uint256 lpInStable = getLPValueInStable(_lp, _lpAmount); - uint256 stakeTokenPrice = getTokenPrice(stakeToken); - uint256 oneStakeTokenValue = 10**uint256(IERC20Extented(stakeToken).decimals()); - stakeValue = oneStakeTokenValue.mul(lpInStable).div(stakeTokenPrice); - } - - /** - * @dev calcilate LP value from stake token amount, function is reverse to getStakeValuebyLP - * @param _lp LP token address - * @param _amount stake token amount - * @return lpValue stake amount value nominated in LP tokens - */ - - function getLPValuebyStake(address _lp, uint256 _amount) public view returns (uint256 lpValue) { - uint256 oneLpAmount = 10**uint256(IERC20Extented(_lp).decimals()); - uint256 oneStakeToken = 10**uint256(IERC20Extented(stakeToken).decimals()); - uint256 oneLPInStable = getLPValueInStable(_lp, oneLpAmount); - uint256 stakeTokenValueinStable = _amount.mul(getTokenPrice(stakeToken)).div(oneStakeToken); - lpValue = oneLpAmount.mul(stakeTokenValueinStable).div(oneLPInStable); - } - - /** - * @dev get LP value in stable coins - * @param _lp lp address - * @param _lpAmount lp tokens amount - */ - - function getLPValueInStable(address _lp, uint256 _lpAmount) public view returns (uint256 lpValueInStable) { - for (uint256 i = 0; i <= 1; i++) { - address componentToken = address(IEmiswap(_lp).tokens(i)); - uint256 oneTokenValue = 10**uint256(IERC20Extented(componentToken).decimals()); - uint256 tokenPrice = getTokenPrice(componentToken); - uint256 tokensInLP = getTokenAmountinLP(_lp, _lpAmount, componentToken); - // calc token value from one of parts and multiply 2 - lpValueInStable = tokensInLP.mul(tokenPrice).mul(2).div(oneTokenValue); - if (lpValueInStable > 0) { - break; - } - } - } - - /** - * @dev get token price using existing token routes - * @param token address of token - */ - function getTokenPrice(address token) public view returns (uint256 tokenPrice) { - require(IERC20Extented(token).decimals() > 0, "token must have decimals"); - uint256 oneTokenValue = 10**uint256(IERC20Extented(token).decimals()); - - // go throuout all path and find minimal token price > 0 - for (uint256 i = 0; i < routeToStable.length; i++) { - if (routeToStable[i].isActive) { - // route must not contain token - bool skipRoute; - for (uint256 k = 0; k < routeToStable[i].route.length; k++) { - if (routeToStable[i].route[k] == token) { - skipRoute = true; - break; - } - } - if (skipRoute) { - break; - } - - // prepare route to get price from token - address[] memory route = new address[](routeToStable[i].route.length + 1); - route[0] = token; - for (uint256 j = 1; j < route.length; j++) { - route[j] = routeToStable[i].route[j - 1]; - } - - // get price by route - uint256 _price = EmiswapLib.getAmountsOut(address(emiFactory), oneTokenValue, route)[route.length - 1]; - - // choose minimum not zero price - if (tokenPrice == 0) { - tokenPrice = _price; - } else { - if (_price > 0 && _price < tokenPrice) { - tokenPrice = _price; - } - } - } - } - } - - /** - * @dev get component token amount in passed amount of LP token - * @param lp addres of LP token - * @param lpAmount amount of LP token - * @param component component token address (of LP) - */ - function getTokenAmountinLP( - address lp, - uint256 lpAmount, - address component - ) public view returns (uint256 tokenAmount) { - tokenAmount = IERC20(component).balanceOf(lp).mul(lpAmount).div(IERC20(lp).totalSupply()); - } - - /** - * @dev get staked tokens by wallet - * @param wallet address - * @return tokens list of staked tokens - */ - - function getStakedTokens(address wallet) public view returns (address[] memory tokens) { - if (wallet == address(0)) { - return (stakeTokens); - } else { - // calc elems - uint8 count; - for (uint256 index = 0; index < stakeTokens.length; index++) { - if (_balances[wallet][stakeTokens[index]] > 0) { - count++; - } - } - // get token adresses - address[] memory _tokens = new address[](count); - for (uint256 index = stakeTokens.length; index > 0; index--) { - if (_balances[wallet][stakeTokens[index - 1]] > 0) { - _tokens[count - 1] = stakeTokens[index - 1]; - count--; - } - } - return (_tokens); - } - } - - /** - * @dev withdraw all staked tokens at once and reset exit date limits - */ - - function withdraw() internal virtual { - require(block.timestamp >= exitLimits[msg.sender], "withdraw blocked"); - uint256 amount = _balances[msg.sender][stakeToken]; - - // set balances - _totalSupply = _totalSupply.sub(amount); - - for (uint256 index = 0; index < stakeTokens.length; index++) { - IERC20Upgradeable(stakeTokens[index]).safeTransfer(msg.sender, _balances[msg.sender][stakeTokens[index]]); - _balances[msg.sender][stakeTokens[index]] = 0; - } - - // reset exit date limits - exitLimits[msg.sender] = 0; - } -} diff --git a/contracts/RewardPoolMulti.sol b/contracts/RewardPoolMulti.sol deleted file mode 100644 index a8ae1b1..0000000 --- a/contracts/RewardPoolMulti.sol +++ /dev/null @@ -1,193 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.2; - -import "@openzeppelin/contracts/math/Math.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; -import "./IRewardDistributionRecipient.sol"; -import "./LPTokenWrapper.sol"; - -contract RewardPoolMulti is LPTokenWrapper, IRewardDistributionRecipient, ReentrancyGuardUpgradeable { - uint256 public totalStakeLimit; // max value in USD coin (last in route), rememeber decimals! - address[] public route; - - IERC20Upgradeable public rewardToken; - uint256 public duration; - - uint256 public periodFinish; - uint256 public periodStop; - uint256 public rewardRate; - uint256 public lastUpdateTime; - uint256 public rewardPerTokenStored; - mapping(address => uint256) public userRewardPerTokenPaid; - mapping(address => uint256) public rewards; - - event RewardAdded(uint256 reward); - event Withdrawn(address indexed user, uint256 amount); - event RewardPaid(address indexed user, uint256 reward); - - /** - * @dev seting main farming config - * @param _rewardToken reward token, staketokeStakedss - * @param _stableCoin stable token contract addres - * @param _duration farming duration from start - * @param _exitTimeOut exit and withdraw stakes allowed only when time passed from first wallet stake - */ - - function initialize( - address _rewardToken, - address _rewardAdmin, - address _emiFactory, - address _stableCoin, - uint256 _duration, - uint256 _exitTimeOut - ) public virtual initializer { - __Ownable_init(); - transferOwnership(_rewardAdmin); - rewardDistribution = _rewardAdmin; - - rewardToken = IERC20Upgradeable(_rewardToken); - stakeToken = _rewardToken; - stakeTokens.push(_rewardToken); - emiFactory = IEmiswapRegistry(_emiFactory); - stableCoin = _stableCoin; - duration = _duration; - exitTimeOut = _exitTimeOut; - } - - modifier updateReward(address account) { - rewardPerTokenStored = rewardPerToken(); - lastUpdateTime = lastTimeRewardApplicable(); - if (account != address(0)) { - rewards[account] = earned(account); - userRewardPerTokenPaid[account] = rewardPerTokenStored; - } - _; - } - - function lastTimeRewardApplicable() public view returns (uint256) { - return Math.min(block.timestamp, periodFinish); - } - - function rewardPerToken() public view returns (uint256) { - if (totalSupply() == 0) { - return rewardPerTokenStored; - } - return - rewardPerTokenStored.add( - lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRate).mul(1e18).div(totalSupply()) - ); - } - - function earned(address account) public view returns (uint256) { - return - balanceOfStakeToken(account).mul(rewardPerToken().sub(userRewardPerTokenPaid[account])).div(1e18).add( - rewards[account] - ); - } - - /** - * @dev stake function, starts farming on stale, user stake two tokens: "Emiswap LP" + "ESW" - * @param lp address of Emiswap LP token - * @param lpAmount amount of Emiswap LP tokens - * @param amount stake token maximum amount to take in - */ - - function stake( - address lp, - uint256 lpAmount, - uint256 amount - ) public override nonReentrant updateReward(msg.sender) { - require(amount > 0, "Cannot stake 0"); - require(block.timestamp <= periodFinish && block.timestamp <= periodStop, "Cannot stake yet"); - super.stake(lp, lpAmount, amount); - } - - // TODO: т.к. общая ставка это ESW и множество LP то для простоты вывод делается только польностью - function withdrawAll() internal nonReentrant updateReward(msg.sender) { - require(balanceOfStakeToken(msg.sender) > 0, "no balance"); - super.withdraw(); - emit Withdrawn(msg.sender, balanceOfStakeToken(msg.sender)); - } - - function exit() external { - withdrawAll(); - getReward(); - } - - function getReward() internal nonReentrant updateReward(msg.sender) { - uint256 reward = earned(msg.sender); - if (reward > 0) { - rewards[msg.sender] = 0; - rewardToken.safeTransfer(msg.sender, reward); - emit RewardPaid(msg.sender, reward); - } - } - - // use it after create and approve of reward token - - function notifyRewardAmount(uint256 reward) external override onlyRewardDistribution updateReward(address(0)) { - if (block.timestamp >= periodFinish) { - rewardRate = reward.div(duration); - } else { - uint256 remaining = periodFinish.sub(block.timestamp); - uint256 leftover = remaining.mul(rewardRate); - rewardRate = reward.add(leftover).div(duration); - } - lastUpdateTime = block.timestamp; - periodFinish = block.timestamp.add(duration); - periodStop = periodFinish; - rewardToken.safeTransferFrom(msg.sender, address(this), reward); - emit RewardAdded(reward); - } - - function addRoutes(address[] memory _route) public override onlyOwner { - super.addRoutes(_route); - } - - function activationRoute(address[] memory _route, bool _isActive) public override onlyOwner { - super.activationRoute(_route, _isActive); - } - - function setPeriodStop(uint256 _periodStop) external onlyRewardDistribution { - require(periodStop <= periodFinish, "Incorrect stop"); - periodStop = _periodStop; - } - - /** - * @dev get staked values nominated in stable coin for the wallet and for all - * @param wallet wallet for getting staked value - * @return senderStake staked value for the wallet - * @return totalStake staked value for all wallet on the contract - */ - function getStakedValuesinUSD(address wallet) public view returns (uint256 senderStake, uint256 totalStake) { - uint256 oneStakeToken = 10**uint256(IERC20Extented(stakeToken).decimals()); - senderStake = balanceOfStakeToken(wallet).mul(getTokenPrice(stakeToken)).div(oneStakeToken); - totalStake = totalSupply().mul(getTokenPrice(stakeToken)).div(oneStakeToken); - } - - function getAmountOut(uint256 amountIn, address[] memory path) public view returns (uint256) { - return EmiswapLib.getAmountsOut(address(emiFactory), amountIn, path)[path.length - 1]; - } - - // ------------------------------------------------------------------------ - // - // ------------------------------------------------------------------------ - /** - * @dev Owner can transfer out any accidentally sent ERC20 tokens - * @param tokenAddress Address of ERC-20 token to transfer - * @param beneficiary Address to transfer to - * @param amount of tokens to transfer - */ - function transferAnyERC20Token( - address tokenAddress, - address beneficiary, - uint256 amount - ) public onlyOwner returns (bool success) { - require(tokenAddress != address(0), "address 0!"); - require(tokenAddress != address(stakeToken), "not staketoken"); - - return IERC20Upgradeable(tokenAddress).transfer(beneficiary, amount); - } -} diff --git a/contracts/interfaces/IEmiswap.sol b/contracts/interfaces/IEmiswap.sol deleted file mode 100644 index e701ef0..0000000 --- a/contracts/interfaces/IEmiswap.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED - -pragma solidity ^0.6.2; - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -interface IEmiswapRegistry { - function pools(IERC20 token1, IERC20 token2) external view returns (IEmiswap); - - function isPool(address addr) external view returns (bool); - - function deploy(IERC20 tokenA, IERC20 tokenB) external returns (IEmiswap); - - function getAllPools() external view returns (IEmiswap[] memory); -} - -interface IEmiswap { - function fee() external view returns (uint256); - - function tokens(uint256 i) external view returns (IERC20); - - function deposit( - uint256[] calldata amounts, - uint256[] calldata minAmounts, - address referral - ) external payable returns (uint256 fairSupply); - - function withdraw(uint256 amount, uint256[] calldata minReturns) external; - - function getBalanceForAddition(IERC20 token) external view returns (uint256); - - function getBalanceForRemoval(IERC20 token) external view returns (uint256); - - function getReturn( - IERC20 fromToken, - IERC20 destToken, - uint256 amount - ) external view returns (uint256, uint256); - - function swap( - IERC20 fromToken, - IERC20 destToken, - uint256 amount, - uint256 minReturn, - address to, - address referral - ) external payable returns (uint256 returnAmount); - - function initialize(IERC20[] calldata assets) external; -} diff --git a/contracts/interfaces/IWETH.sol b/contracts/interfaces/IWETH.sol deleted file mode 100644 index e1be4db..0000000 --- a/contracts/interfaces/IWETH.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED - -pragma solidity ^0.6.0; - -interface IWETH { - function deposit() external payable; - - function transfer(address to, uint256 value) external returns (bool); - - function withdraw(uint256) external; -} diff --git a/contracts/libraries/EmiswapLib.sol b/contracts/libraries/EmiswapLib.sol deleted file mode 100644 index 6118ad8..0000000 --- a/contracts/libraries/EmiswapLib.sol +++ /dev/null @@ -1,140 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED - -pragma solidity ^0.6.2; - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/math/SafeMath.sol"; -import "./../interfaces/IEmiswap.sol"; - -library EmiswapLib { - using SafeMath for uint256; - uint256 public constant FEE_DENOMINATOR = 1e18; - - function previewSwapExactTokenForToken( - address factory, - address tokenFrom, - address tokenTo, - uint256 ammountFrom - ) internal view returns (uint256 ammountTo) { - IEmiswap pairContract = IEmiswapRegistry(factory).pools(IERC20(tokenFrom), IERC20(tokenTo)); - - if (pairContract != IEmiswap(0)) { - (, ammountTo) = pairContract.getReturn(IERC20(tokenFrom), IERC20(tokenTo), ammountFrom); - } - } - - /************************************************************************************** - * get preview result of virtual swap by route of tokens - **************************************************************************************/ - function previewSwapbyRoute( - address factory, - address[] memory path, - uint256 ammountFrom - ) internal view returns (uint256 ammountTo) { - for (uint256 i = 0; i < path.length - 1; i++) { - if (path.length >= 2) { - ammountTo = previewSwapExactTokenForToken(factory, path[i], path[i + 1], ammountFrom); - - if (i == (path.length - 2)) { - return (ammountTo); - } else { - ammountFrom = ammountTo; - } - } - } - } - - function fee(address factory) internal view returns (uint256) { - return IEmiswap(factory).fee(); - } - - // given an output amount of an asset and pair reserves, returns a required input amount of the other asset - function getAmountIn( - address factory, - uint256 amountOut, - uint256 reserveIn, - uint256 reserveOut - ) internal view returns (uint256 amountIn) { - require(amountOut > 0, "EmiswapLibrary: INSUFFICIENT_OUTPUT_AMOUNT"); - require(reserveIn > 0 && reserveOut > 0, "EmiswapLibrary: INSUFFICIENT_LIQUIDITY"); - uint256 numerator = reserveIn.mul(amountOut).mul(1000); - uint256 denominator = reserveOut.sub(amountOut).mul(uint256(1000000000000000000).sub(fee(factory)).div(1e15)); // 997 - amountIn = (numerator / denominator).add(1); - } - - // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset - function getAmountOut( - address factory, - uint256 amountIn, - uint256 reserveIn, - uint256 reserveOut - ) internal view returns (uint256 amountOut) { - if (amountIn == 0 || reserveIn == 0 || reserveOut == 0) { - return (0); - } - - uint256 amountInWithFee = amountIn.mul(uint256(1000000000000000000).sub(fee(factory)).div(1e15)); //997 - uint256 numerator = amountInWithFee.mul(reserveOut); - uint256 denominator = reserveIn.mul(1000).add(amountInWithFee); - amountOut = (denominator == 0 ? 0 : amountOut = numerator / denominator); - } - - // performs chained getAmountIn calculations on any number of pairs - function getAmountsIn( - address factory, - uint256 amountOut, - address[] memory path - ) internal view returns (uint256[] memory amounts) { - require(path.length >= 2, "EmiswapLibrary: INVALID_PATH"); - amounts = new uint256[](path.length); - amounts[amounts.length - 1] = amountOut; - for (uint256 i = path.length - 1; i > 0; i--) { - IEmiswap pairContract = IEmiswapRegistry(factory).pools(IERC20(IERC20(path[i])), IERC20(path[i - 1])); - - uint256 reserveIn; - uint256 reserveOut; - - if (address(pairContract) != address(0)) { - reserveIn = IEmiswap(pairContract).getBalanceForAddition(IERC20(path[i - 1])); - reserveOut = IEmiswap(pairContract).getBalanceForRemoval(IERC20(path[i])); - } - - amounts[i - 1] = getAmountIn(factory, amounts[i], reserveIn, reserveOut); - } - } - - // performs chained getAmountOut calculations on any number of pairs - function getAmountsOut( - address factory, - uint256 amountIn, - address[] memory path - ) internal view returns (uint256[] memory amounts) { - require(path.length >= 2, "EmiswapLibrary: INVALID_PATH"); - amounts = new uint256[](path.length); - amounts[0] = amountIn; - for (uint256 i = 0; i < path.length - 1; i++) { - IEmiswap pairContract = IEmiswapRegistry(factory).pools(IERC20(IERC20(path[i])), IERC20(path[i + 1])); - - uint256 reserveIn; - uint256 reserveOut; - - if (address(pairContract) != address(0)) { - reserveIn = IEmiswap(pairContract).getBalanceForAddition(IERC20(path[i])); - reserveOut = IEmiswap(pairContract).getBalanceForRemoval(IERC20(path[i + 1])); - } - - amounts[i + 1] = getAmountOut(factory, amounts[i], reserveIn, reserveOut); - } - } - - // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset - function quote( - uint256 amountA, - uint256 reserveA, - uint256 reserveB - ) internal pure returns (uint256 amountB) { - require(amountA > 0, "EmiswapLibrary: INSUFFICIENT_AMOUNT"); - require(reserveA > 0 && reserveB > 0, "EmiswapLibrary: INSUFFICIENT_LIQUIDITY"); - amountB = amountA.mul(reserveB) / reserveA; - } -} diff --git a/contracts/libraries/Sqrt.sol b/contracts/libraries/Sqrt.sol deleted file mode 100644 index 4cd1152..0000000 --- a/contracts/libraries/Sqrt.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.0; - -library Sqrt { - // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method) - function sqrt(uint256 y) internal pure returns (uint256) { - if (y > 3) { - uint256 z = y; - uint256 x = y / 2 + 1; - while (x < z) { - z = x; - x = (y / x + x) / 2; - } - return z; - } else if (y != 0) { - return 1; - } else { - return 0; - } - } -} diff --git a/contracts/libraries/TransferHelper.sol b/contracts/libraries/TransferHelper.sol deleted file mode 100644 index fb8d846..0000000 --- a/contracts/libraries/TransferHelper.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -pragma solidity >=0.6.0; - -// 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, - uint256 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, - uint256 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, - uint256 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, uint256 value) internal { - (bool success, ) = to.call{value: value}(new bytes(0)); - require(success, "TransferHelper: ETH_TRANSFER_FAILED"); - } -} diff --git a/contracts/libraries/UniERC20.sol b/contracts/libraries/UniERC20.sol deleted file mode 100644 index 4fefe50..0000000 --- a/contracts/libraries/UniERC20.sol +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.0; - -import "@openzeppelin/contracts/math/SafeMath.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; - -library UniERC20 { - using SafeMath for uint256; - using SafeERC20 for IERC20; - - function isETH(IERC20 token) internal pure returns (bool) { - return (address(token) == address(0)); - } - - function uniBalanceOf(IERC20 token, address account) internal view returns (uint256) { - if (isETH(token)) { - return account.balance; - } else { - return token.balanceOf(account); - } - } - - function uniTransfer( - IERC20 token, - address payable to, - uint256 amount - ) internal { - if (amount > 0) { - if (isETH(token)) { - to.transfer(amount); - } else { - token.safeTransfer(to, amount); - } - } - } - - function uniTransferFromSenderToThis(IERC20 token, uint256 amount) internal { - if (amount > 0) { - if (isETH(token)) { - require(msg.value >= amount, "UniERC20: not enough value"); - if (msg.value > amount) { - // Return remainder if exist - msg.sender.transfer(msg.value.sub(amount)); - } - } else { - token.safeTransferFrom(msg.sender, address(this), amount); - } - } - } - - function uniSymbol(IERC20 token) internal view returns (string memory) { - if (isETH(token)) { - return "ETH"; - } - - (bool success, bytes memory data) = address(token).staticcall{gas: 20000}(abi.encodeWithSignature("symbol()")); - if (!success) { - (success, data) = address(token).staticcall{gas: 20000}(abi.encodeWithSignature("SYMBOL()")); - } - - if (success && data.length >= 96) { - (uint256 offset, uint256 len) = abi.decode(data, (uint256, uint256)); - if (offset == 0x20 && len > 0 && len <= 256) { - return string(abi.decode(data, (bytes))); - } - } - - if (success && data.length == 32) { - uint256 len = 0; - while (len < data.length && data[len] >= 0x20 && data[len] <= 0x7E) { - len++; - } - - if (len > 0) { - bytes memory result = new bytes(len); - for (uint256 i = 0; i < len; i++) { - result[i] = data[i]; - } - return string(result); - } - } - - return _toHex(address(token)); - } - - function _toHex(address account) private pure returns (string memory) { - return _toHex(abi.encodePacked(account)); - } - - function _toHex(bytes memory data) private pure returns (string memory) { - bytes memory str = new bytes(2 + data.length * 2); - str[0] = "0"; - str[1] = "x"; - uint256 j = 2; - for (uint256 i = 0; i < data.length; i++) { - uint256 a = uint8(data[i]) >> 4; - uint256 b = uint8(data[i]) & 0x0f; - str[j++] = bytes1(uint8(a + 48 + (a / 10) * 39)); - str[j++] = bytes1(uint8(b + 48 + (b / 10) * 39)); - } - - return string(str); - } -} diff --git a/contracts/mocks/EmiFactory.sol b/contracts/mocks/EmiFactory.sol deleted file mode 100644 index 23e3de5..0000000 --- a/contracts/mocks/EmiFactory.sol +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.0; - -import "@openzeppelin/contracts/access/Ownable.sol"; -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "./../libraries/UniERC20.sol"; -import "./../interfaces/IEmiswap.sol"; -import "./Emiswap.sol"; - -contract EmiFactory is Ownable { - using UniERC20 for IERC20; - - mapping(address => bool) private _adminTable; - - modifier onlyAdmin() { - require(_adminTable[msg.sender], "Admin: caller is not admin"); - _; - } - - event Deployed(address indexed emiswap, address indexed token1, address indexed token2); - - event adminGranted(address indexed admin, bool isGranted); - - uint256 public constant MAX_FEE = 0.003e18; // 0.3% - - uint256 public fee; - uint256 public feeVault; - address public addressVault; - Emiswap[] public allPools; - mapping(Emiswap => bool) public isPool; - mapping(IERC20 => mapping(IERC20 => Emiswap)) public pools; - - function getAllPools() external view returns (Emiswap[] memory) { - return allPools; - } - - function setFee(uint256 newFee) external onlyAdmin { - require(newFee <= MAX_FEE, "Factory: fee should be <= 0.3%"); - fee = newFee; - } - - function setFeeVault(uint256 newFeeVault) external onlyAdmin { - require(newFeeVault < fee, "Factory: vault fee"); - feeVault = newFeeVault; - } - - function setaddressVault(address newAddressVault) external onlyAdmin { - require(newAddressVault != address(0), "Factory: vault"); - addressVault = newAddressVault; - } - - function setAdminGrant(address newAdmin, bool isGranted) external onlyOwner { - require(newAdmin != address(0), "Admin address 0"); - _adminTable[newAdmin] = isGranted; - emit adminGranted(newAdmin, isGranted); - } - - function deploy(IERC20 tokenA, IERC20 tokenB) external returns (Emiswap pool) { - require(address(tokenA) != address(0) && address(tokenB) != address(0), "Factory:no 0x address"); - require(tokenA != tokenB, "Factory:no same tokens"); - require(pools[tokenA][tokenB] == Emiswap(0), "Factory:pool already exists"); - - (IERC20 token1, IERC20 token2) = sortTokens(tokenA, tokenB); - IERC20[] memory tokens = new IERC20[](2); - tokens[0] = token1; - tokens[1] = token2; - - /////////// abi.encodePacked(bytecode, abi.encode(arg1, arg2)) - bytes memory bytecode = abi.encodePacked(type(Emiswap).creationCode); - - bytes32 salt = keccak256(abi.encodePacked(token1, token2)); - assembly { - pool := create2(0, add(bytecode, 32), mload(bytecode), salt) - } - Emiswap(pool).initialize(tokens); - - pool.transferOwnership(owner()); - pools[token1][token2] = pool; - pools[token2][token1] = pool; - allPools.push(pool); - isPool[pool] = true; - - emit Deployed(address(pool), address(token1), address(token2)); - } - - function sortTokens(IERC20 tokenA, IERC20 tokenB) public pure returns (IERC20, IERC20) { - if (tokenA < tokenB) { - return (tokenA, tokenB); - } - return (tokenB, tokenA); - } -} diff --git a/contracts/mocks/EmiRouter.sol b/contracts/mocks/EmiRouter.sol deleted file mode 100644 index cfffb12..0000000 --- a/contracts/mocks/EmiRouter.sol +++ /dev/null @@ -1,573 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED - -pragma solidity ^0.6.2; -pragma experimental ABIEncoderV2; - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/math/SafeMath.sol"; -import "./../interfaces/IEmiswap.sol"; -import "./../libraries/EmiswapLib.sol"; -import "./../libraries/TransferHelper.sol"; -import "./../interfaces/IWETH.sol"; - -contract EmiRouter { - using SafeMath for uint256; - - address public factory; - address public WETH; - - struct PoolData { - IEmiswap pool; - uint256 balanceA; - uint256 balanceB; - } - - event Log(uint256 a, uint256 b); - - constructor(address _factory, address _wEth) public { - factory = _factory; - WETH = _wEth; - } - - receive() external payable { - assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract - } - - // **** Pool Info **** - - function tokenToIERC(IERC20 _token) public view returns (IERC20) { - return (address(_token) == address(0) ? IERC20(WETH) : _token); - } - - function getPoolDataList(IERC20[] memory tokenAList, IERC20[] memory tokenBList) - public - view - returns (PoolData[] memory dataList) - { - if (tokenAList.length > 0 && tokenAList.length == tokenBList.length) { - dataList = new PoolData[](tokenAList.length); - for (uint256 i = 0; i < tokenAList.length; i++) { - if ( - address( - IEmiswapRegistry(address(factory)).pools(tokenToIERC(tokenAList[i]), tokenToIERC(tokenBList[i])) - ) != address(0) - ) { - dataList[i].pool = IEmiswapRegistry(address(factory)).pools( - tokenToIERC(tokenAList[i]), - tokenToIERC(tokenBList[i]) - ); - dataList[i].balanceA = IEmiswap(address(dataList[i].pool)).getBalanceForAddition( - tokenToIERC(tokenAList[i]) - ); - dataList[i].balanceB = IEmiswap(address(dataList[i].pool)).getBalanceForAddition( - tokenToIERC(tokenBList[i]) - ); - } - } - } else { - dataList = new PoolData[](1); - } - } - - function getReservesByPool(address pool) public view returns (uint256 _reserve0, uint256 _reserve1) { - _reserve0 = IEmiswap(pool).getBalanceForAddition(IEmiswap(pool).tokens(0)); - _reserve1 = IEmiswap(pool).getBalanceForAddition(IEmiswap(pool).tokens(1)); - } - - function getReserves(IERC20 token0, IERC20 token1) - public - view - returns ( - uint256 _reserve0, - uint256 _reserve1, - address poolAddresss - ) - { - if (address(IEmiswapRegistry(address(factory)).pools(tokenToIERC(token0), tokenToIERC(token1))) != address(0)) { - _reserve0 = IEmiswapRegistry(address(factory)) - .pools(tokenToIERC(token0), tokenToIERC(token1)) - .getBalanceForAddition(tokenToIERC(token0)); - _reserve1 = IEmiswapRegistry(address(factory)) - .pools(tokenToIERC(token0), tokenToIERC(token1)) - .getBalanceForAddition(tokenToIERC(token1)); - poolAddresss = address(IEmiswapRegistry(address(factory)).pools(tokenToIERC(token0), tokenToIERC(token1))); - } - } - - function getExpectedReturn( - IERC20 fromToken, - IERC20 destToken, - uint256 amount - ) public view returns (uint256 returnAmount, uint256[] memory distribution) { - address[] memory path; - path = new address[](2); - path[0] = address(tokenToIERC(fromToken)); - path[1] = address(tokenToIERC(destToken)); - - returnAmount = getAmountsOut(amount, path)[1]; - uint256[] memory _distribution; - _distribution = new uint256[](34); - _distribution[12] = 1; - distribution = _distribution; - } - - function resetAllowance(address token, address pairContract) public { - if (IERC20(token).allowance(address(this), pairContract) > 0) { - TransferHelper.safeApprove(token, pairContract, 0); - } - } - - // **** Liquidity **** - /** - * @param tokenA address of first token in pair - * @param tokenB address of second token in pair - * @return LP balance - */ - function getLiquidity(address tokenA, address tokenB) external view returns (uint256) { - return (IERC20(address(IEmiswapRegistry(factory).pools(IERC20(tokenA), IERC20(tokenB)))).balanceOf(msg.sender)); - } - - // **** ADD LIQUIDITY **** - function _addLiquidity( - address tokenA, - address tokenB, - uint256 amountADesired, - uint256 amountBDesired, - uint256 amountAMin, - uint256 amountBMin - ) internal returns (uint256 amountA, uint256 amountB) { - IERC20 ERC20tokenA = IERC20(tokenA); - IERC20 ERC20tokenB = IERC20(tokenB); - IEmiswap pairContract = IEmiswapRegistry(factory).pools(ERC20tokenA, ERC20tokenB); - // create the pair if it doesn't exist yet - if (pairContract == IEmiswap(0)) { - pairContract = IEmiswapRegistry(factory).deploy(ERC20tokenA, ERC20tokenB); - } - - uint256 reserveA = pairContract.getBalanceForAddition(ERC20tokenA); - uint256 reserveB = pairContract.getBalanceForRemoval(ERC20tokenB); - - if (reserveA == 0 && reserveB == 0) { - (amountA, amountB) = (amountADesired, amountBDesired); - } else { - uint256 amountBOptimal = EmiswapLib.quote(amountADesired, reserveA, reserveB); - if (amountBOptimal <= amountBDesired) { - require(amountBOptimal >= amountBMin, "EmiRouter:INSUFFICIENT_B_AMOUNT"); - (amountA, amountB) = (amountADesired, amountBOptimal); - } else { - uint256 amountAOptimal = EmiswapLib.quote(amountBDesired, reserveB, reserveA); - assert(amountAOptimal <= amountADesired); - require(amountAOptimal >= amountAMin, "EmiRouter:INSUFFICIENT_A_AMOUNT"); - (amountA, amountB) = (amountAOptimal, amountBDesired); - } - } - } - - /** - * @param tokenA address of first token in pair - * @param tokenB address of second token in pair - * @param amountADesired desired amount of first token - * @param amountBDesired desired amount of second token - * @param amountAMin minimum amount of first token - * @param amountBMin minimum amount of second token - * @param ref referral address - * @return amountA added liquidity of first token - * @return amountB added liquidity of second token - * @return liquidity - */ - - function addLiquidity( - address tokenA, - address tokenB, - uint256 amountADesired, - uint256 amountBDesired, - uint256 amountAMin, - uint256 amountBMin, - address ref - ) - external - returns ( - uint256 amountA, - uint256 amountB, - uint256 liquidity - ) - { - (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin); - IEmiswap pairContract = IEmiswapRegistry(factory).pools(IERC20(tokenA), IERC20(tokenB)); - - TransferHelper.safeTransferFrom(tokenA, msg.sender, address(this), amountA); - TransferHelper.safeTransferFrom(tokenB, msg.sender, address(this), amountB); - - resetAllowance(tokenA, address(pairContract)); - resetAllowance(tokenB, address(pairContract)); - TransferHelper.safeApprove(tokenA, address(pairContract), amountA); - TransferHelper.safeApprove(tokenB, address(pairContract), amountB); - - uint256[] memory amounts; - amounts = new uint256[](2); - uint256[] memory minAmounts; - minAmounts = new uint256[](2); - - if (tokenA < tokenB) { - amounts[0] = amountA; - amounts[1] = amountB; - minAmounts[0] = amountAMin; - minAmounts[1] = amountBMin; - } else { - amounts[0] = amountB; - amounts[1] = amountA; - minAmounts[0] = amountBMin; - minAmounts[1] = amountAMin; - } - - //emit Log(amounts[0], amounts[1]); - liquidity = IEmiswap(pairContract).deposit(amounts, minAmounts, ref); - - TransferHelper.safeTransfer(address(pairContract), msg.sender, liquidity); - } - - /** - * @param token address of token - * @param amountTokenDesired desired amount of token - * @param amountTokenMin minimum amount of token - * @param amountETHMin minimum amount of ETH - * @param ref referral address - * @return amountToken added liquidity of token - * @return amountETH added liquidity of ETH - * @return liquidity - */ - function addLiquidityETH( - address token, - uint256 amountTokenDesired, - uint256 amountTokenMin, - uint256 amountETHMin, - address ref - ) - external - payable - returns ( - uint256 amountToken, - uint256 amountETH, - uint256 liquidity - ) - { - (amountToken, amountETH) = _addLiquidity( - token, - WETH, - amountTokenDesired, - msg.value, - amountTokenMin, - amountETHMin - ); - IEmiswap pairContract = IEmiswapRegistry(factory).pools(IERC20(token), IERC20(WETH)); - TransferHelper.safeTransferFrom(token, msg.sender, address(this), amountToken); - // set allowance to 0 - resetAllowance(token, address(pairContract)); - TransferHelper.safeApprove(token, address(pairContract), amountToken); - IWETH(WETH).deposit{value: amountETH}(); - resetAllowance(WETH, address(pairContract)); - TransferHelper.safeApprove(WETH, address(pairContract), amountETH); - - uint256[] memory amounts; - amounts = new uint256[](2); - uint256[] memory minAmounts; - minAmounts = new uint256[](2); - - if (token < WETH) { - amounts[0] = amountToken; - amounts[1] = amountETH; - minAmounts[0] = amountTokenMin; - minAmounts[1] = amountETHMin; - } else { - amounts[0] = amountETH; - amounts[1] = amountToken; - minAmounts[0] = amountETHMin; - minAmounts[1] = amountTokenMin; - } - liquidity = IEmiswap(pairContract).deposit(amounts, minAmounts, ref); - TransferHelper.safeTransfer(address(pairContract), msg.sender, liquidity); - } - - // **** REMOVE LIQUIDITY **** - /** - * @param tokenA address of first token in pair - * @param tokenB address of second token in pair - * @param liquidity LP token - * @param amountAMin minimum amount of first token - * @param amountBMin minimum amount of second token - */ - function removeLiquidity( - address tokenA, - address tokenB, - uint256 liquidity, - uint256 amountAMin, - uint256 amountBMin - ) public { - IEmiswap pairContract = IEmiswapRegistry(factory).pools(IERC20(tokenA), IERC20(tokenB)); - TransferHelper.safeTransferFrom(address(pairContract), msg.sender, address(this), liquidity); // send liquidity to this - - uint256[] memory minReturns; - minReturns = new uint256[](2); - - if (tokenA < tokenB) { - minReturns[0] = amountAMin; - minReturns[1] = amountBMin; - } else { - minReturns[0] = amountBMin; - minReturns[1] = amountAMin; - } - uint256 tokenAbalance = IERC20(tokenA).balanceOf(address(this)); - uint256 tokenBbalance = IERC20(tokenB).balanceOf(address(this)); - - pairContract.withdraw(liquidity, minReturns); - - tokenAbalance = IERC20(tokenA).balanceOf(address(this)).sub(tokenAbalance); - tokenBbalance = IERC20(tokenB).balanceOf(address(this)).sub(tokenBbalance); - - TransferHelper.safeTransfer(tokenA, msg.sender, tokenAbalance); - TransferHelper.safeTransfer(tokenB, msg.sender, tokenBbalance); - } - - /** - * @param token address of token - * @param liquidity LP token amount - * @param amountTokenMin minimum amount of token - * @param amountETHMin minimum amount of ETH - */ - function removeLiquidityETH( - address token, - uint256 liquidity, - uint256 amountTokenMin, - uint256 amountETHMin - ) public { - IEmiswap pairContract = IEmiswapRegistry(factory).pools(IERC20(token), IERC20(WETH)); - TransferHelper.safeTransferFrom(address(pairContract), msg.sender, address(this), liquidity); // send liquidity to this - - uint256[] memory minReturns; - minReturns = new uint256[](2); - - if (token < WETH) { - minReturns[0] = amountTokenMin; - minReturns[1] = amountETHMin; - } else { - minReturns[0] = amountETHMin; - minReturns[1] = amountTokenMin; - } - - uint256 tokenbalance = IERC20(token).balanceOf(address(this)); - uint256 WETHbalance = IERC20(WETH).balanceOf(address(this)); - - pairContract.withdraw(liquidity, minReturns); - - tokenbalance = IERC20(token).balanceOf(address(this)).sub(tokenbalance); - WETHbalance = IERC20(WETH).balanceOf(address(this)).sub(WETHbalance); - - TransferHelper.safeTransfer(token, msg.sender, tokenbalance); - - // convert WETH and send back raw ETH - IWETH(WETH).withdraw(WETHbalance); - TransferHelper.safeTransferETH(msg.sender, WETHbalance); - } - - // **** SWAP **** - - function _swap_( - address tokenFrom, - address tokenTo, - uint256 ammountFrom, - address to, - address ref - ) internal returns (uint256 ammountTo) { - IEmiswap pairContract = IEmiswapRegistry(factory).pools(IERC20(tokenFrom), IERC20(tokenTo)); - - (, uint256 amt1) = pairContract.getReturn(IERC20(tokenFrom), IERC20(tokenTo), ammountFrom); - if (amt1 > 0) { - resetAllowance(tokenFrom, address(pairContract)); - TransferHelper.safeApprove(tokenFrom, address(pairContract), ammountFrom); - ammountTo = pairContract.swap(IERC20(tokenFrom), IERC20(tokenTo), ammountFrom, 0, to, ref); - } - } - - function _swapbyRoute( - address[] memory path, - uint256 ammountFrom, - address to, - address ref - ) internal returns (uint256 ammountTo) { - for (uint256 i = 0; i < path.length - 1; i++) { - if (path.length >= 2) { - uint256 _ammountTo = _swap_( - path[i], - path[i + 1], - ammountFrom, - (i == (path.length - 2) ? to : address(this)), - ref - ); - if (i == (path.length - 2)) { - return (_ammountTo); - } else { - ammountFrom = _ammountTo; - } - } - } - } - - /** - * @param amountIn exact in value of source token - * @param amountOutMin minimum amount value of result token - * @param path array of token addresses, represent the path for swaps - * @param to send result token to - * @param ref referral - * @return amounts result amount - */ - - function swapExactTokensForTokens( - uint256 amountIn, - uint256 amountOutMin, - address[] calldata path, - address to, - address ref - ) external returns (uint256[] memory amounts) { - amounts = getAmountsOut(amountIn, path); - require(amounts[amounts.length - 1] >= amountOutMin, "EmiRouter:INSUFFICIENT_OUTPUT_AMOUNT"); - - TransferHelper.safeTransferFrom(path[0], msg.sender, address(this), amountIn); - _swapbyRoute(path, amountIn, to, ref); - } - - /** - * @param amountOut exact in value of result token - * @param amountInMax maximum amount value of source token - * @param path array of token addresses, represent the path for swaps - * @param to send result token to - * @param ref referral - * @return amounts result amount values - */ - - function swapTokensForExactTokens( - uint256 amountOut, - uint256 amountInMax, - address[] calldata path, - address to, - address ref - ) external returns (uint256[] memory amounts) { - amounts = getAmountsIn(amountOut, path); - require(amounts[0] <= amountInMax, "EmiRouter:EXCESSIVE_INPUT_AMOUNT"); - - TransferHelper.safeTransferFrom(path[0], msg.sender, address(this), amounts[0]); - _swapbyRoute(path, amounts[0], to, ref); - } - - /** - * @param amountOutMin minimum amount value of result token - * @param path array of token addresses, represent the path for swaps - * @param to send result token to - * @param ref referral - * @return amounts result token amount values - */ - - function swapExactETHForTokens( - uint256 amountOutMin, - address[] calldata path, - address to, - address ref - ) external payable returns (uint256[] memory amounts) { - require(path[0] == WETH, "EmiRouter:INVALID_PATH"); - amounts = getAmountsOut(msg.value, path); - require(amounts[amounts.length - 1] >= amountOutMin, "EmiRouter:INSUFFICIENT_OUTPUT_AMOUNT"); - IWETH(WETH).deposit{value: amounts[0]}(); - _swapbyRoute(path, amounts[0], to, ref); - } - - /** - * @param amountOut amount value of result ETH - * @param amountInMax maximum amount of source token - * @param path array of token addresses, represent the path for swaps, (WETH for ETH) - * @param to send result token to - * @param ref referral - * @return amounts result token amount values - */ - - function swapTokensForExactETH( - uint256 amountOut, - uint256 amountInMax, - address[] calldata path, - address to, - address ref - ) external returns (uint256[] memory amounts) { - require(path[path.length - 1] == WETH, "EmiRouter:INVALID_PATH"); - amounts = getAmountsIn(amountOut, path); - require(amounts[0] <= amountInMax, "EmiRouter:EXCESSIVE_AMOUNT"); - - TransferHelper.safeTransferFrom(path[0], msg.sender, address(this), amounts[0]); - - uint256 result = _swapbyRoute(path, amounts[0], address(this), ref); - - IWETH(WETH).withdraw(result); - TransferHelper.safeTransferETH(to, result); - } - - /** - * @param amountIn amount value of source token - * @param path array of token addresses, represent the path for swaps, (WETH for ETH) - * @param to send result token to - * @param ref referral - */ - - function swapExactTokensForETH( - uint256 amountIn, - address[] calldata path, - address to, - address ref - ) external { - require(path[path.length - 1] == WETH, "EmiRouter:INVALID_PATH"); - TransferHelper.safeTransferFrom(path[0], msg.sender, address(this), amountIn); - - uint256 result = _swapbyRoute(path, amountIn, address(this), ref); - - IWETH(WETH).withdraw(result); - TransferHelper.safeTransferETH(to, result); - } - - /** - * @param amountOut amount of result tokens - * @param path array of token addresses, represent the path for swaps, (WETH for ETH) - * @param to send result token to - * @param ref referral - * @return amounts result token amount values - */ - - function swapETHForExactTokens( - uint256 amountOut, - address[] calldata path, - address to, - address ref - ) external payable returns (uint256[] memory amounts) { - require(path[0] == WETH, "EmiRouter:INVALID_PATH"); - amounts = getAmountsIn(amountOut, path); - require(amounts[0] <= msg.value, "EmiRouter:EXCESSIVE_INPUT_AMOUNT"); - - IWETH(WETH).deposit{value: amounts[0]}(); - - _swapbyRoute(path, amounts[0], to, ref); - } - - // **** LIBRARY FUNCTIONS **** - /** - * @param amountIn amount of source token - * @param path array of token addresses, represent the path for swaps, (WETH for ETH) - * @return amounts result token amount values - */ - function getAmountsOut(uint256 amountIn, address[] memory path) public view returns (uint256[] memory amounts) { - return EmiswapLib.getAmountsOut(factory, amountIn, path); - } - - /** - * @param amountOut amount of result token - * @param path array of token addresses, represent the path for swaps, (WETH for ETH) - * @return amounts result token amount values - */ - function getAmountsIn(uint256 amountOut, address[] memory path) public view returns (uint256[] memory amounts) { - return EmiswapLib.getAmountsIn(factory, amountOut, path); - } -} diff --git a/contracts/mocks/Emiswap.sol b/contracts/mocks/Emiswap.sol deleted file mode 100644 index 0d3fd2e..0000000 --- a/contracts/mocks/Emiswap.sol +++ /dev/null @@ -1,323 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED - -pragma solidity ^0.6.0; - -import "@openzeppelin/contracts/access/Ownable.sol"; -import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; -import "@openzeppelin/contracts/math/Math.sol"; -import "@openzeppelin/contracts/math/SafeMath.sol"; -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "./../libraries/UniERC20.sol"; -import "./../libraries/Sqrt.sol"; - -interface IFactory { - function fee() external view returns (uint256); - - function feeVault() external view returns (uint256); - - function addressVault() external view returns (address); -} - -library VirtualBalance { - using SafeMath for uint256; - - struct Data { - uint216 balance; - uint40 time; - } - - uint256 public constant DECAY_PERIOD = 5 minutes; - - function set(VirtualBalance.Data storage self, uint256 balance) internal { - self.balance = uint216(balance); - self.time = uint40(block.timestamp); - } - - function update(VirtualBalance.Data storage self, uint256 realBalance) internal { - set(self, current(self, realBalance)); - } - - function scale( - VirtualBalance.Data storage self, - uint256 realBalance, - uint256 num, - uint256 denom - ) internal { - set(self, current(self, realBalance).mul(num).add(denom.sub(1)).div(denom)); - } - - function current(VirtualBalance.Data memory self, uint256 realBalance) internal view returns (uint256) { - uint256 timePassed = Math.min(DECAY_PERIOD, block.timestamp.sub(self.time)); - uint256 timeRemain = DECAY_PERIOD.sub(timePassed); - return uint256(self.balance).mul(timeRemain).add(realBalance.mul(timePassed)).div(DECAY_PERIOD); - } -} - -contract Emiswap is ERC20, ReentrancyGuard, Ownable { - using Sqrt for uint256; - using SafeMath for uint256; - using UniERC20 for IERC20; - using VirtualBalance for VirtualBalance.Data; - - struct Balances { - uint256 src; - uint256 dst; - } - - struct SwapVolumes { - uint128 confirmed; - uint128 result; - } - - event Deposited(address indexed account, uint256 amount, address referral); - - event Withdrawn(address indexed account, uint256 amount); - - event Swapped( - address indexed account, - address indexed src, - address indexed dst, - uint256 amount, - uint256 result, - uint256 srcBalance, - uint256 dstBalance, - uint256 totalSupply, - address referral - ); - - event Swapped2(address indexed account, address indexed to, uint256 resultVault); - - uint256 public constant REFERRAL_SHARE = 20; // 1/share = 5% of LPs revenue - uint256 public constant BASE_SUPPLY = 1000; // Total supply on first deposit - uint256 public constant FEE_DENOMINATOR = 1e18; - - IFactory public factory; - IERC20[] public tokens; - mapping(IERC20 => bool) public isToken; - mapping(IERC20 => SwapVolumes) public volumes; - mapping(IERC20 => VirtualBalance.Data) public virtualBalancesForAddition; - mapping(IERC20 => VirtualBalance.Data) public virtualBalancesForRemoval; - - constructor() public ERC20("Emiswap LP token", "EMI LP") { - factory = IFactory(msg.sender); - } - - // called once by the factory at time of deployment - function initialize(IERC20[] calldata assets) external { - require(msg.sender == address(factory), "Emiswap: FORBIDDEN"); // sufficient check - require(assets.length == 2, "Emiswap: only 2 tokens allowed"); - - tokens = assets; - for (uint256 i = 0; i < assets.length; i++) { - require(!isToken[assets[i]], "Emiswap: duplicate tokens"); - isToken[assets[i]] = true; - } - } - - function fee() public view returns (uint256) { - return factory.fee(); - } - - function feeVault() public view returns (uint256) { - return factory.feeVault(); - } - - function addressVault() public view returns (address) { - return factory.addressVault(); - } - - function getTokens() external view returns (IERC20[] memory) { - return tokens; - } - - function decayPeriod() external pure returns (uint256) { - return VirtualBalance.DECAY_PERIOD; - } - - function getBalanceForAddition(IERC20 token) public view returns (uint256) { - uint256 balance = token.uniBalanceOf(address(this)); - return Math.max(virtualBalancesForAddition[token].current(balance), balance); - } - - function getBalanceForRemoval(IERC20 token) public view returns (uint256) { - uint256 balance = token.uniBalanceOf(address(this)); - return Math.min(virtualBalancesForRemoval[token].current(balance), balance); - } - - function getReturn( - IERC20 src, - IERC20 dst, - uint256 amount - ) external view returns (uint256, uint256) { - return _getReturn(src, dst, amount, getBalanceForAddition(src), getBalanceForRemoval(dst)); - } - - function deposit( - uint256[] calldata amounts, - uint256[] calldata minAmounts, - address referral - ) external nonReentrant returns (uint256 fairSupply) { - IERC20[] memory _tokens = tokens; - require((amounts.length > 0) && (amounts.length == _tokens.length), "Emiswap: wrong amounts length"); - require((amounts[0] > 0) && (amounts[1] > 0), "Emiswap: wrong value usage"); - - uint256[] memory realBalances = new uint256[](amounts.length); - for (uint256 i = 0; i < realBalances.length; i++) { - realBalances[i] = _tokens[i].uniBalanceOf(address(this)); - } - - uint256 totalSupply = totalSupply(); - if (totalSupply == 0) { - fairSupply = BASE_SUPPLY.mul(99); - _mint(address(this), BASE_SUPPLY); // Donate up to 1% - - // Use the greatest token amount but not less than 99k for the initial supply - for (uint256 i = 0; i < amounts.length; i++) { - fairSupply = Math.max(fairSupply, amounts[i]); - } - } else { - // Pre-compute fair supply - fairSupply = type(uint256).max; - for (uint256 i = 0; i < amounts.length; i++) { - fairSupply = Math.min(fairSupply, totalSupply.mul(amounts[i]).div(realBalances[i])); - } - } - - uint256 fairSupplyCached = fairSupply; - for (uint256 i = 0; i < amounts.length; i++) { - require(amounts[i] > 0, "Emiswap: amount is zero"); - uint256 amount = (totalSupply == 0) - ? amounts[i] - : realBalances[i].mul(fairSupplyCached).add(totalSupply - 1).div(totalSupply); - require(amount >= minAmounts[i], "Emiswap: minAmount not reached"); - - _tokens[i].uniTransferFromSenderToThis(amount); - if (totalSupply > 0) { - uint256 confirmed = _tokens[i].uniBalanceOf(address(this)).sub(realBalances[i]); - fairSupply = Math.min(fairSupply, totalSupply.mul(confirmed).div(realBalances[i])); - } - } - - if (totalSupply > 0) { - for (uint256 i = 0; i < amounts.length; i++) { - virtualBalancesForRemoval[_tokens[i]].scale(realBalances[i], totalSupply.add(fairSupply), totalSupply); - virtualBalancesForAddition[_tokens[i]].scale(realBalances[i], totalSupply.add(fairSupply), totalSupply); - } - } - - require(fairSupply > 0, "Emniswap: result is not enough"); - _mint(msg.sender, fairSupply); - - emit Deposited(msg.sender, fairSupply, referral); - } - - function withdraw(uint256 amount, uint256[] calldata minReturns) external nonReentrant { - uint256 totalSupply = totalSupply(); - _burn(msg.sender, amount); - - for (uint256 i = 0; i < tokens.length; i++) { - IERC20 token = tokens[i]; - - uint256 preBalance = token.uniBalanceOf(address(this)); - uint256 value = preBalance.mul(amount).div(totalSupply); - token.uniTransfer(msg.sender, value); - require(i >= minReturns.length || value >= minReturns[i], "Emiswap: result is not enough"); - - virtualBalancesForAddition[token].scale(preBalance, totalSupply.sub(amount), totalSupply); - virtualBalancesForRemoval[token].scale(preBalance, totalSupply.sub(amount), totalSupply); - } - - emit Withdrawn(msg.sender, amount); - } - - function swap( - IERC20 src, - IERC20 dst, - uint256 amount, - uint256 minReturn, - address to, - address referral - ) external nonReentrant returns (uint256 result) { - require(address(src) != address(0), "Emiswap: only tokens allowed"); - - Balances memory balances = Balances({ - src: src.uniBalanceOf(address(this)), - dst: dst.uniBalanceOf(address(this)) - }); - - // catch possible airdrops and external balance changes for deflationary tokens - uint256 srcAdditionBalance = Math.max(virtualBalancesForAddition[src].current(balances.src), balances.src); - uint256 dstRemovalBalance = Math.min(virtualBalancesForRemoval[dst].current(balances.dst), balances.dst); - - src.uniTransferFromSenderToThis(amount); - uint256 confirmed = src.uniBalanceOf(address(this)).sub(balances.src); - - uint256 resultVault; - (result, resultVault) = _getReturn(src, dst, confirmed, srcAdditionBalance, dstRemovalBalance); - require(result > 0 && result >= minReturn, "Emiswap: return is not enough"); - dst.uniTransfer(payable(to), result); - if (resultVault > 0) { - dst.uniTransfer(payable(addressVault()), resultVault); - } - - // Update virtual balances to the same direction only at imbalanced state - if (srcAdditionBalance != balances.src) { - virtualBalancesForAddition[src].set(srcAdditionBalance.add(confirmed)); - } - if (dstRemovalBalance != balances.dst) { - virtualBalancesForRemoval[dst].set(dstRemovalBalance.sub(result)); - } - - // Update virtual balances to the opposite direction - virtualBalancesForRemoval[src].update(balances.src); - virtualBalancesForAddition[dst].update(balances.dst); - - emit Swapped( - msg.sender, - address(src), - address(dst), - confirmed, - result, - balances.src, - balances.dst, - totalSupply(), - referral - ); - emit Swapped2(msg.sender, to, resultVault); - - // Overflow of uint128 is desired - volumes[src].confirmed += uint128(confirmed); - volumes[src].result += uint128(result); - } - - function rescueFunds(IERC20 token, uint256 amount) external nonReentrant onlyOwner { - uint256[] memory balances = new uint256[](tokens.length); - for (uint256 i = 0; i < balances.length; i++) { - balances[i] = tokens[i].uniBalanceOf(address(this)); - } - - token.uniTransfer(msg.sender, amount); - - for (uint256 i = 0; i < balances.length; i++) { - require(tokens[i].uniBalanceOf(address(this)) >= balances[i], "Emiswap: access denied"); - } - require(balanceOf(address(this)) >= BASE_SUPPLY, "Emiswap: access denied"); - } - - function _getReturn( - IERC20 src, - IERC20 dst, - uint256 amount, - uint256 srcBalance, - uint256 dstBalance - ) internal view returns (uint256, uint256) { - if (isToken[src] && isToken[dst] && src != dst && amount > 0) { - uint256 taxedAmount = amount.sub(amount.mul(fee()).div(FEE_DENOMINATOR)); - uint256 resWFee = taxedAmount.mul(dstBalance).div(srcBalance.add(taxedAmount)); - uint256 resWOFee = amount.mul(dstBalance).div(srcBalance.add(amount)); - uint256 resVault = (feeVault() == 0 ? 0 : resWOFee.sub(resWFee).div(fee().div(feeVault()))); - - return (resWFee, resVault); - } - } -} diff --git a/contracts/mocks/esw.sol b/contracts/mocks/esw.sol deleted file mode 100644 index 4d7a866..0000000 --- a/contracts/mocks/esw.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED - -pragma solidity ^0.6.2; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -contract MockESW is ERC20 { - uint256 private constant _INITIAL_SUPPLY = 10_000_000_000 * (10**18); - - constructor() public ERC20("ESW token", "ESW") { - _mint(msg.sender, _INITIAL_SUPPLY); - } -} diff --git a/contracts/mocks/lp.sol b/contracts/mocks/lp.sol deleted file mode 100644 index d02ea5c..0000000 --- a/contracts/mocks/lp.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED - -pragma solidity ^0.6.2; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -contract MockLP is ERC20 { - uint256 private constant _INITIAL_SUPPLY = 10_000_000_000 * (10**18); - - constructor() public ERC20("LP token", "LP") { - _mint(msg.sender, _INITIAL_SUPPLY); - } -} diff --git a/contracts/mocks/usdt.sol b/contracts/mocks/usdt.sol deleted file mode 100644 index c30b4e3..0000000 --- a/contracts/mocks/usdt.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED - -pragma solidity ^0.6.2; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -contract MockUSDT is ERC20 { - uint256 private constant _INITIAL_SUPPLY = 10000000000 * (10**6); - - constructor() public ERC20("USDT token", "USDT") { - _mint(msg.sender, _INITIAL_SUPPLY); - _setupDecimals(6); - } -} diff --git a/contracts/mocks/wbtc.sol b/contracts/mocks/wbtc.sol deleted file mode 100644 index cba69fa..0000000 --- a/contracts/mocks/wbtc.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED - -pragma solidity ^0.6.2; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -contract MockWBTC is ERC20 { - uint256 private constant _INITIAL_SUPPLY = 21000000 * (10**8); - - constructor() public ERC20("WBTC token", "WBTC") { - _mint(msg.sender, _INITIAL_SUPPLY); - _setupDecimals(8); - } -} diff --git a/contracts/mocks/weth.sol b/contracts/mocks/weth.sol deleted file mode 100644 index 1d59bc6..0000000 --- a/contracts/mocks/weth.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED - -pragma solidity ^0.6.2; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -contract MockWETH is ERC20 { - uint256 private constant _INITIAL_SUPPLY = 10000000000 * (10**18); - - constructor() public ERC20("WETH token", "WETH") { - _mint(msg.sender, _INITIAL_SUPPLY); - _setupDecimals(18); - } -} diff --git a/flat/RewardPoolMulti.flat.sol b/flat/RewardPoolMulti.flat.sol deleted file mode 100644 index cad6ad6..0000000 --- a/flat/RewardPoolMulti.flat.sol +++ /dev/null @@ -1,1862 +0,0 @@ -// Sources flattened with hardhat v2.6.8 https://hardhat.org - -// File @openzeppelin/contracts/math/Math.sol@v3.4.2 - -// SPDX-License-Identifier: MIT - -pragma solidity >=0.6.0 <0.8.0; - -/** - * @dev Standard math utilities missing in the Solidity language. - */ -library Math { - /** - * @dev Returns the largest of two numbers. - */ - function max(uint256 a, uint256 b) internal pure returns (uint256) { - return a >= b ? a : b; - } - - /** - * @dev Returns the smallest of two numbers. - */ - function min(uint256 a, uint256 b) internal pure returns (uint256) { - return a < b ? a : b; - } - - /** - * @dev Returns the average of two numbers. The result is rounded towards - * zero. - */ - function average(uint256 a, uint256 b) internal pure returns (uint256) { - // (a + b) / 2 can overflow, so we distribute - return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); - } -} - - -// File @openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol@v3.4.2 - -// SPDX-License-Identifier: MIT - -pragma solidity >=0.6.0 <0.8.0; - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20Upgradeable { - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `recipient`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address recipient, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `sender` to `recipient` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); - - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); -} - - -// File @openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol@v3.4.2 - -// SPDX-License-Identifier: MIT - -pragma solidity >=0.6.2 <0.8.0; - -/** - * @dev Collection of functions related to the address type - */ -library AddressUpgradeable { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize, which returns 0 for contracts in - // construction, since the code is only stored at the end of the - // constructor execution. - - uint256 size; - // solhint-disable-next-line no-inline-assembly - assembly { size := extcodesize(account) } - return size > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - // solhint-disable-next-line avoid-low-level-calls, avoid-call-value - (bool success, ) = recipient.call{ value: amount }(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain`call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory returndata) = target.call{ value: value }(data); - return _verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory returndata) = target.staticcall(data); - return _verifyCallResult(success, returndata, errorMessage); - } - - function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - - // solhint-disable-next-line no-inline-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - - -// File @openzeppelin/contracts-upgradeable/proxy/Initializable.sol@v3.4.2 - -// SPDX-License-Identifier: MIT - -// solhint-disable-next-line compiler-version -pragma solidity >=0.4.24 <0.8.0; - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - */ -abstract contract Initializable { - - /** - * @dev Indicates that the contract has been initialized. - */ - bool private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Modifier to protect an initializer function from being invoked twice. - */ - modifier initializer() { - require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized"); - - bool isTopLevelCall = !_initializing; - if (isTopLevelCall) { - _initializing = true; - _initialized = true; - } - - _; - - if (isTopLevelCall) { - _initializing = false; - } - } - - /// @dev Returns true if and only if the function is running in the constructor - function _isConstructor() private view returns (bool) { - return !AddressUpgradeable.isContract(address(this)); - } -} - - -// File @openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol@v3.4.2 - -// SPDX-License-Identifier: MIT - -pragma solidity >=0.6.0 <0.8.0; - -/** - * @dev Contract module that helps prevent reentrant calls to a function. - * - * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier - * available, which can be applied to functions to make sure there are no nested - * (reentrant) calls to them. - * - * Note that because there is a single `nonReentrant` guard, functions marked as - * `nonReentrant` may not call one another. This can be worked around by making - * those functions `private`, and then adding `external` `nonReentrant` entry - * points to them. - * - * TIP: If you would like to learn more about reentrancy and alternative ways - * to protect against it, check out our blog post - * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. - */ -abstract contract ReentrancyGuardUpgradeable is Initializable { - // Booleans are more expensive than uint256 or any type that takes up a full - // word because each write operation emits an extra SLOAD to first read the - // slot's contents, replace the bits taken up by the boolean, and then write - // back. This is the compiler's defense against contract upgrades and - // pointer aliasing, and it cannot be disabled. - - // The values being non-zero value makes deployment a bit more expensive, - // but in exchange the refund on every call to nonReentrant will be lower in - // amount. Since refunds are capped to a percentage of the total - // transaction's gas, it is best to keep them low in cases like this one, to - // increase the likelihood of the full refund coming into effect. - uint256 private constant _NOT_ENTERED = 1; - uint256 private constant _ENTERED = 2; - - uint256 private _status; - - function __ReentrancyGuard_init() internal initializer { - __ReentrancyGuard_init_unchained(); - } - - function __ReentrancyGuard_init_unchained() internal initializer { - _status = _NOT_ENTERED; - } - - /** - * @dev Prevents a contract from calling itself, directly or indirectly. - * Calling a `nonReentrant` function from another `nonReentrant` - * function is not supported. It is possible to prevent this from happening - * by making the `nonReentrant` function external, and make it call a - * `private` function that does the actual work. - */ - modifier nonReentrant() { - // On the first call to nonReentrant, _notEntered will be true - require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); - - // Any calls to nonReentrant after this point will fail - _status = _ENTERED; - - _; - - // By storing the original value once again, a refund is triggered (see - // https://eips.ethereum.org/EIPS/eip-2200) - _status = _NOT_ENTERED; - } - uint256[49] private __gap; -} - - -// File @openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol@v3.4.2 - -// SPDX-License-Identifier: MIT - -pragma solidity >=0.6.0 <0.8.0; - -/* - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with GSN meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract ContextUpgradeable is Initializable { - function __Context_init() internal initializer { - __Context_init_unchained(); - } - - function __Context_init_unchained() internal initializer { - } - function _msgSender() internal view virtual returns (address payable) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes memory) { - this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 - return msg.data; - } - uint256[50] private __gap; -} - - -// File @openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol@v3.4.2 - -// SPDX-License-Identifier: MIT - -pragma solidity >=0.6.0 <0.8.0; - - -/** - * @dev Contract module which provides a basic access control mechanism, where - * there is an account (an owner) that can be granted exclusive access to - * specific functions. - * - * By default, the owner account will be the one that deploys the contract. This - * can later be changed with {transferOwnership}. - * - * This module is used through inheritance. It will make available the modifier - * `onlyOwner`, which can be applied to your functions to restrict their use to - * the owner. - */ -abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { - address private _owner; - - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - /** - * @dev Initializes the contract setting the deployer as the initial owner. - */ - function __Ownable_init() internal initializer { - __Context_init_unchained(); - __Ownable_init_unchained(); - } - - function __Ownable_init_unchained() internal initializer { - address msgSender = _msgSender(); - _owner = msgSender; - emit OwnershipTransferred(address(0), msgSender); - } - - /** - * @dev Returns the address of the current owner. - */ - function owner() public view virtual returns (address) { - return _owner; - } - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - require(owner() == _msgSender(), "Ownable: caller is not the owner"); - _; - } - - /** - * @dev Leaves the contract without owner. It will not be possible to call - * `onlyOwner` functions anymore. Can only be called by the current owner. - * - * NOTE: Renouncing ownership will leave the contract without an owner, - * thereby removing any functionality that is only available to the owner. - */ - function renounceOwnership() public virtual onlyOwner { - emit OwnershipTransferred(_owner, address(0)); - _owner = address(0); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Can only be called by the current owner. - */ - function transferOwnership(address newOwner) public virtual onlyOwner { - require(newOwner != address(0), "Ownable: new owner is the zero address"); - emit OwnershipTransferred(_owner, newOwner); - _owner = newOwner; - } - uint256[49] private __gap; -} - - -// File contracts/IRewardDistributionRecipient.sol - -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.2; - -abstract contract IRewardDistributionRecipient is OwnableUpgradeable { - address public rewardDistribution; - - function notifyRewardAmount(uint256 reward) external virtual; - - modifier onlyRewardDistribution() { - require(_msgSender() == rewardDistribution, "Caller is not reward distribution"); - _; - } - - function setRewardDistribution(address _rewardDistribution) - public - /* external */ - onlyOwner - { - rewardDistribution = _rewardDistribution; - } -} - - -// File @openzeppelin/contracts/math/SafeMath.sol@v3.4.2 - -// SPDX-License-Identifier: MIT - -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 @openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol@v3.4.2 - -// SPDX-License-Identifier: MIT - -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 SafeMathUpgradeable { - /** - * @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 @openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol@v3.4.2 - -// SPDX-License-Identifier: MIT - -pragma solidity >=0.6.0 <0.8.0; - - - -/** - * @title SafeERC20 - * @dev Wrappers around ERC20 operations that throw on failure (when the token - * contract returns false). Tokens that return no value (and instead revert or - * throw on failure) are also supported, non-reverting calls are assumed to be - * successful. - * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, - * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. - */ -library SafeERC20Upgradeable { - using SafeMathUpgradeable for uint256; - using AddressUpgradeable for address; - - function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal { - _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); - } - - function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal { - _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); - } - - /** - * @dev Deprecated. This function has issues similar to the ones found in - * {IERC20-approve}, and its usage is discouraged. - * - * Whenever possible, use {safeIncreaseAllowance} and - * {safeDecreaseAllowance} instead. - */ - function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal { - // safeApprove should only be called when setting an initial allowance, - // or when resetting it to zero. To increase and decrease it, use - // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' - // solhint-disable-next-line max-line-length - require((value == 0) || (token.allowance(address(this), spender) == 0), - "SafeERC20: approve from non-zero to non-zero allowance" - ); - _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); - } - - function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal { - uint256 newAllowance = token.allowance(address(this), spender).add(value); - _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); - } - - function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal { - uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); - _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); - } - - /** - * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement - * on the return value: the return value is optional (but if data is returned, it must not be false). - * @param token The token targeted by the call. - * @param data The call data (encoded using abi.encode or one of its variants). - */ - function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private { - // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since - // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that - // the target address contains contract code and also asserts for success in the low-level call. - - bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); - if (returndata.length > 0) { // Return data is optional - // solhint-disable-next-line max-line-length - require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); - } - } -} - - -// File @openzeppelin/contracts/token/ERC20/IERC20.sol@v3.4.2 - -// SPDX-License-Identifier: MIT - -pragma solidity >=0.6.0 <0.8.0; - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `recipient`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address recipient, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `sender` to `recipient` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); - - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); -} - - -// File contracts/interfaces/IEmiswap.sol - -// SPDX-License-Identifier: UNLICENSED - -pragma solidity ^0.6.2; - -interface IEmiswapRegistry { - function pools(IERC20 token1, IERC20 token2) external view returns (IEmiswap); - - function isPool(address addr) external view returns (bool); - - function deploy(IERC20 tokenA, IERC20 tokenB) external returns (IEmiswap); - - function getAllPools() external view returns (IEmiswap[] memory); -} - -interface IEmiswap { - function fee() external view returns (uint256); - - function tokens(uint256 i) external view returns (IERC20); - - function deposit( - uint256[] calldata amounts, - uint256[] calldata minAmounts, - address referral - ) external payable returns (uint256 fairSupply); - - function withdraw(uint256 amount, uint256[] calldata minReturns) external; - - function getBalanceForAddition(IERC20 token) external view returns (uint256); - - function getBalanceForRemoval(IERC20 token) external view returns (uint256); - - function getReturn( - IERC20 fromToken, - IERC20 destToken, - uint256 amount - ) external view returns (uint256, uint256); - - function swap( - IERC20 fromToken, - IERC20 destToken, - uint256 amount, - uint256 minReturn, - address to, - address referral - ) external payable returns (uint256 returnAmount); - - function initialize(IERC20[] calldata assets) external; -} - - -// File contracts/libraries/EmiswapLib.sol - -// SPDX-License-Identifier: UNLICENSED - -pragma solidity ^0.6.2; - - - -library EmiswapLib { - using SafeMath for uint256; - uint256 public constant FEE_DENOMINATOR = 1e18; - - function previewSwapExactTokenForToken( - address factory, - address tokenFrom, - address tokenTo, - uint256 ammountFrom - ) internal view returns (uint256 ammountTo) { - IEmiswap pairContract = IEmiswapRegistry(factory).pools(IERC20(tokenFrom), IERC20(tokenTo)); - - if (pairContract != IEmiswap(0)) { - (, ammountTo) = pairContract.getReturn(IERC20(tokenFrom), IERC20(tokenTo), ammountFrom); - } - } - - /************************************************************************************** - * get preview result of virtual swap by route of tokens - **************************************************************************************/ - function previewSwapbyRoute( - address factory, - address[] memory path, - uint256 ammountFrom - ) internal view returns (uint256 ammountTo) { - for (uint256 i = 0; i < path.length - 1; i++) { - if (path.length >= 2) { - ammountTo = previewSwapExactTokenForToken(factory, path[i], path[i + 1], ammountFrom); - - if (i == (path.length - 2)) { - return (ammountTo); - } else { - ammountFrom = ammountTo; - } - } - } - } - - function fee(address factory) internal view returns (uint256) { - return IEmiswap(factory).fee(); - } - - // given an output amount of an asset and pair reserves, returns a required input amount of the other asset - function getAmountIn( - address factory, - uint256 amountOut, - uint256 reserveIn, - uint256 reserveOut - ) internal view returns (uint256 amountIn) { - require(amountOut > 0, "EmiswapLibrary: INSUFFICIENT_OUTPUT_AMOUNT"); - require(reserveIn > 0 && reserveOut > 0, "EmiswapLibrary: INSUFFICIENT_LIQUIDITY"); - uint256 numerator = reserveIn.mul(amountOut).mul(1000); - uint256 denominator = reserveOut.sub(amountOut).mul(uint256(1000000000000000000).sub(fee(factory)).div(1e15)); // 997 - amountIn = (numerator / denominator).add(1); - } - - // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset - function getAmountOut( - address factory, - uint256 amountIn, - uint256 reserveIn, - uint256 reserveOut - ) internal view returns (uint256 amountOut) { - if (amountIn == 0 || reserveIn == 0 || reserveOut == 0) { - return (0); - } - - uint256 amountInWithFee = amountIn.mul(uint256(1000000000000000000).sub(fee(factory)).div(1e15)); //997 - uint256 numerator = amountInWithFee.mul(reserveOut); - uint256 denominator = reserveIn.mul(1000).add(amountInWithFee); - amountOut = (denominator == 0 ? 0 : amountOut = numerator / denominator); - } - - // performs chained getAmountIn calculations on any number of pairs - function getAmountsIn( - address factory, - uint256 amountOut, - address[] memory path - ) internal view returns (uint256[] memory amounts) { - require(path.length >= 2, "EmiswapLibrary: INVALID_PATH"); - amounts = new uint256[](path.length); - amounts[amounts.length - 1] = amountOut; - for (uint256 i = path.length - 1; i > 0; i--) { - IEmiswap pairContract = IEmiswapRegistry(factory).pools(IERC20(IERC20(path[i])), IERC20(path[i - 1])); - - uint256 reserveIn; - uint256 reserveOut; - - if (address(pairContract) != address(0)) { - reserveIn = IEmiswap(pairContract).getBalanceForAddition(IERC20(path[i - 1])); - reserveOut = IEmiswap(pairContract).getBalanceForRemoval(IERC20(path[i])); - } - - amounts[i - 1] = getAmountIn(factory, amounts[i], reserveIn, reserveOut); - } - } - - // performs chained getAmountOut calculations on any number of pairs - function getAmountsOut( - address factory, - uint256 amountIn, - address[] memory path - ) internal view returns (uint256[] memory amounts) { - require(path.length >= 2, "EmiswapLibrary: INVALID_PATH"); - amounts = new uint256[](path.length); - amounts[0] = amountIn; - for (uint256 i = 0; i < path.length - 1; i++) { - IEmiswap pairContract = IEmiswapRegistry(factory).pools(IERC20(IERC20(path[i])), IERC20(path[i + 1])); - - uint256 reserveIn; - uint256 reserveOut; - - if (address(pairContract) != address(0)) { - reserveIn = IEmiswap(pairContract).getBalanceForAddition(IERC20(path[i])); - reserveOut = IEmiswap(pairContract).getBalanceForRemoval(IERC20(path[i + 1])); - } - - amounts[i + 1] = getAmountOut(factory, amounts[i], reserveIn, reserveOut); - } - } - - // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset - function quote( - uint256 amountA, - uint256 reserveA, - uint256 reserveB - ) internal pure returns (uint256 amountB) { - require(amountA > 0, "EmiswapLibrary: INSUFFICIENT_AMOUNT"); - require(reserveA > 0 && reserveB > 0, "EmiswapLibrary: INSUFFICIENT_LIQUIDITY"); - amountB = amountA.mul(reserveB) / reserveA; - } -} - - -// File contracts/LPTokenWrapper.sol - -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.2; - -//import "hardhat/console.sol"; - - - - - -interface IERC20Extented is IERC20Upgradeable { - function decimals() external view returns (uint8); -} - -contract LPTokenWrapper { - using SafeMath for uint256; - using SafeERC20Upgradeable for IERC20Upgradeable; - - // minimal time to be passed from wallet first stake to allow exit - uint256 public exitTimeOut; - - mapping(address => uint256) public exitLimits; - - // Stable coin - address public stableCoin; - - // path to stable on Emiswap dex - struct path { - address[] route; - bool isActive; - } - - // Routs to stable coin - path[] public routeToStable; - - // save hash of path to check unique - mapping(bytes32 => bool) public availableRoutes; - - // EmiFactory - IEmiswapRegistry public emiFactory; - - // is the main token (ESW) - address public stakeToken; - - // main token (ESW) supply - uint256 private _totalSupply; - - // wallet -> lp -> amount - mapping(address => mapping(address => uint256)) private _balances; - - // 0 means the token address is not in the array - mapping(address => uint256) internal tokensIndex; - // used stake tokens - address[] public stakeTokens; - - event Staked(address indexed user, address lp, uint256 lpAmount, uint256 amount); - - function totalSupply() public view returns (uint256) { - return _totalSupply; - } - - function balanceOfStakeToken(address account) public view returns (uint256) { - return _balances[account][stakeToken]; - } - - function balanceOfLPToken(address account, address lp) public view returns (uint256) { - return _balances[account][lp]; - } - - /** - * @dev admin function to setup price routes to the "stableCoin" - * @param route - route must ends with "stableCoin", and can consist only of 1 element - "stableCoin" - */ - - function addRoutes(address[] memory route) public virtual { - require(route.length > 0 && route[route.length - 1] == stableCoin, "set route to stable"); - require(!availableRoutes[keccak256(abi.encodePacked(route))], "route already added"); - availableRoutes[keccak256(abi.encodePacked(route))] = true; - routeToStable.push(path(route, true)); - } - - /** - * @dev activate/deactivate route - * @param _route array of tokens - * @param _isActive change to true/false enable/disable - */ - - function activationRoute(address[] memory _route, bool _isActive) public virtual { - for (uint256 index = 0; index < routeToStable.length; index++) { - if ( - keccak256(abi.encodePacked(routeToStable[index].route)) == keccak256(abi.encodePacked(_route)) && - routeToStable[index].isActive != _isActive - ) { - routeToStable[index].isActive = _isActive; - return; - } - } - } - - /** - * @dev get route info - * @param _route input route - * @return routeRes stored route if found - * @return isActiveRes is active flag - */ - - function getRoute(address[] memory _route) public view returns (address[] memory routeRes, bool isActiveRes) { - for (uint256 index = 0; index < routeToStable.length; index++) { - if (keccak256(abi.encodePacked(routeToStable[index].route)) == keccak256(abi.encodePacked(_route))) { - routeRes = routeToStable[index].route; - isActiveRes = routeToStable[index].isActive; - } - } - } - - /** - * @dev get route info by routeID - * @param routeID input route - * @return routeRes stored route if found - * @return isActiveRes is active flag - */ - - function getRouteInfo(uint256 routeID) public view returns (address[] memory routeRes, bool isActiveRes) { - routeRes = routeToStable[routeID].route; - isActiveRes = routeToStable[routeID].isActive; - } - - /** - * @dev stake two tokens: lp and stake token (esw) - * @param lp lp token address - * @param lpAmount lp token amount - * @param amountMax stake token maximum amount to take in - */ - - function stake( - address lp, - uint256 lpAmount, - uint256 amountMax - ) public virtual { - require(emiFactory.isPool(lp), "token incorrect or not LP"); - - // calc needful stake token amount - uint256 stakeTokenAmount = getStakeValuebyLP(lp, lpAmount); - require(stakeTokenAmount > 0 && stakeTokenAmount <= amountMax, "not enough stake token amount"); - - IERC20Upgradeable(stakeToken).safeTransferFrom(msg.sender, address(this), stakeTokenAmount); - IERC20Upgradeable(lp).safeTransferFrom(msg.sender, address(this), lpAmount); - - // incease total supply in stake token - _totalSupply = _totalSupply.add(stakeTokenAmount); - - // set balances - _balances[msg.sender][stakeToken] = _balances[msg.sender][stakeToken].add(stakeTokenAmount); - _balances[msg.sender][lp] = _balances[msg.sender][lp].add(lpAmount); - // save lp token stake, and also stakeToken by default - if (tokensIndex[lp] == 0) { - stakeTokens.push(lp); - tokensIndex[lp] = stakeTokens.length; - } - // if first stake, save exit timeout - if (exitLimits[msg.sender] == 0) { - exitLimits[msg.sender] = block.timestamp + exitTimeOut; - } - emit Staked(msg.sender, lp, lpAmount, stakeTokenAmount); - } - - /** - * @dev calcilate stake value from LP token amount - * @param _lp LP token address - * @param _lpAmount LP token amount - * @return stakeValue LP amount value nominated in stake tokens - */ - - function getStakeValuebyLP(address _lp, uint256 _lpAmount) public view returns (uint256 stakeValue) { - uint256 lpInStable = getLPValueInStable(_lp, _lpAmount); - uint256 stakeTokenPrice = getTokenPrice(stakeToken); - uint256 oneStakeTokenValue = 10**uint256(IERC20Extented(stakeToken).decimals()); - stakeValue = oneStakeTokenValue.mul(lpInStable).div(stakeTokenPrice); - } - - /** - * @dev calcilate LP value from stake token amount, function is reverse to getStakeValuebyLP - * @param _lp LP token address - * @param _amount stake token amount - * @return lpValue stake amount value nominated in LP tokens - */ - - function getLPValuebyStake(address _lp, uint256 _amount) public view returns (uint256 lpValue) { - uint256 oneLpAmount = 10**uint256(IERC20Extented(_lp).decimals()); - uint256 oneStakeToken = 10**uint256(IERC20Extented(stakeToken).decimals()); - uint256 oneLPInStable = getLPValueInStable(_lp, oneLpAmount); - uint256 stakeTokenValueinStable = _amount.mul(getTokenPrice(stakeToken)).div(oneStakeToken); - lpValue = oneLpAmount.mul(stakeTokenValueinStable).div(oneLPInStable); - } - - /** - * @dev get LP value in stable coins - * @param _lp lp address - * @param _lpAmount lp tokens amount - */ - - function getLPValueInStable(address _lp, uint256 _lpAmount) public view returns (uint256 lpValueInStable) { - for (uint256 i = 0; i <= 1; i++) { - address componentToken = address(IEmiswap(_lp).tokens(i)); - uint256 oneTokenValue = 10**uint256(IERC20Extented(componentToken).decimals()); - uint256 tokenPrice = getTokenPrice(componentToken); - uint256 tokensInLP = getTokenAmountinLP(_lp, _lpAmount, componentToken); - // calc token value from one of parts and multiply 2 - lpValueInStable = tokensInLP.mul(tokenPrice).mul(2).div(oneTokenValue); - if (lpValueInStable > 0) { - break; - } - } - } - - /** - * @dev get token price using existing token routes - * @param token address of token - */ - function getTokenPrice(address token) public view returns (uint256 tokenPrice) { - require(IERC20Extented(token).decimals() > 0, "token must have decimals"); - uint256 oneTokenValue = 10**uint256(IERC20Extented(token).decimals()); - - // go throuout all path and find minimal token price > 0 - for (uint256 i = 0; i < routeToStable.length; i++) { - if (routeToStable[i].isActive) { - // route must not contain token - bool skipRoute; - for (uint256 k = 0; k < routeToStable[i].route.length; k++) { - if (routeToStable[i].route[k] == token) { - skipRoute = true; - break; - } - } - if (skipRoute) { - break; - } - - // prepare route to get price from token - address[] memory route = new address[](routeToStable[i].route.length + 1); - route[0] = token; - for (uint256 j = 1; j < route.length; j++) { - route[j] = routeToStable[i].route[j - 1]; - } - - // get price by route - uint256 _price = EmiswapLib.getAmountsOut(address(emiFactory), oneTokenValue, route)[route.length - 1]; - - // choose minimum not zero price - if (tokenPrice == 0) { - tokenPrice = _price; - } else { - if (_price > 0 && _price < tokenPrice) { - tokenPrice = _price; - } - } - } - } - } - - /** - * @dev get component token amount in passed amount of LP token - * @param lp addres of LP token - * @param lpAmount amount of LP token - * @param component component token address (of LP) - */ - function getTokenAmountinLP( - address lp, - uint256 lpAmount, - address component - ) public view returns (uint256 tokenAmount) { - tokenAmount = IERC20(component).balanceOf(lp).mul(lpAmount).div(IERC20(lp).totalSupply()); - } - - /** - * @dev get staked tokens by wallet - * @param wallet address - * @return tokens list of staked tokens - */ - - function getStakedTokens(address wallet) public view returns (address[] memory tokens) { - if (wallet == address(0)) { - return (stakeTokens); - } else { - // calc elems - uint8 count; - for (uint256 index = 0; index < stakeTokens.length; index++) { - if (_balances[wallet][stakeTokens[index]] > 0) { - count++; - } - } - // get token adresses - address[] memory _tokens = new address[](count); - for (uint256 index = stakeTokens.length; index > 0; index--) { - if (_balances[wallet][stakeTokens[index - 1]] > 0) { - _tokens[count - 1] = stakeTokens[index - 1]; - count--; - } - } - return (_tokens); - } - } - - /** - * @dev withdraw all staked tokens at once and reset exit date limits - */ - - function withdraw() internal virtual { - require(block.timestamp >= exitLimits[msg.sender], "withdraw blocked"); - uint256 amount = _balances[msg.sender][stakeToken]; - - // set balances - _totalSupply = _totalSupply.sub(amount); - - for (uint256 index = 0; index < stakeTokens.length; index++) { - IERC20Upgradeable(stakeTokens[index]).safeTransfer(msg.sender, _balances[msg.sender][stakeTokens[index]]); - _balances[msg.sender][stakeTokens[index]] = 0; - } - - // reset exit date limits - exitLimits[msg.sender] = 0; - } -} - - -// File contracts/RewardPoolMulti.sol - -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.2; - - - - - -contract RewardPoolMulti is LPTokenWrapper, IRewardDistributionRecipient, ReentrancyGuardUpgradeable { - uint256 public totalStakeLimit; // max value in USD coin (last in route), rememeber decimals! - address[] public route; - - IERC20Upgradeable public rewardToken; - uint256 public duration; - - uint256 public periodFinish; - uint256 public periodStop; - uint256 public rewardRate; - uint256 public lastUpdateTime; - uint256 public rewardPerTokenStored; - mapping(address => uint256) public userRewardPerTokenPaid; - mapping(address => uint256) public rewards; - - event RewardAdded(uint256 reward); - event Withdrawn(address indexed user, uint256 amount); - event RewardPaid(address indexed user, uint256 reward); - - /** - * @dev seting main farming config - * @param _rewardToken reward token, staketokeStakedss - * @param _stableCoin stable token contract addres - * @param _duration farming duration from start - * @param _exitTimeOut exit and withdraw stakes allowed only when time passed from first wallet stake - */ - - function initialize( - address _rewardToken, - address _rewardAdmin, - address _emiFactory, - address _stableCoin, - uint256 _duration, - uint256 _exitTimeOut - ) public virtual initializer { - __Ownable_init(); - transferOwnership(_rewardAdmin); - rewardDistribution = _rewardAdmin; - - rewardToken = IERC20Upgradeable(_rewardToken); - stakeToken = _rewardToken; - stakeTokens.push(_rewardToken); - emiFactory = IEmiswapRegistry(_emiFactory); - stableCoin = _stableCoin; - duration = _duration; - exitTimeOut = _exitTimeOut; - } - - modifier updateReward(address account) { - rewardPerTokenStored = rewardPerToken(); - lastUpdateTime = lastTimeRewardApplicable(); - if (account != address(0)) { - rewards[account] = earned(account); - userRewardPerTokenPaid[account] = rewardPerTokenStored; - } - _; - } - - function lastTimeRewardApplicable() public view returns (uint256) { - return Math.min(block.timestamp, periodFinish); - } - - function rewardPerToken() public view returns (uint256) { - if (totalSupply() == 0) { - return rewardPerTokenStored; - } - return - rewardPerTokenStored.add( - lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRate).mul(1e18).div(totalSupply()) - ); - } - - function earned(address account) public view returns (uint256) { - return - balanceOfStakeToken(account).mul(rewardPerToken().sub(userRewardPerTokenPaid[account])).div(1e18).add( - rewards[account] - ); - } - - /** - * @dev stake function, starts farming on stale, user stake two tokens: "Emiswap LP" + "ESW" - * @param lp address of Emiswap LP token - * @param lpAmount amount of Emiswap LP tokens - * @param amount stake token maximum amount to take in - */ - - function stake( - address lp, - uint256 lpAmount, - uint256 amount - ) public override nonReentrant updateReward(msg.sender) { - require(amount > 0, "Cannot stake 0"); - require(block.timestamp <= periodFinish && block.timestamp <= periodStop, "Cannot stake yet"); - super.stake(lp, lpAmount, amount); - } - - // TODO: т.к. общая ставка это ESW и множество LP то для простоты вывод делается только польностью - function withdrawAll() internal nonReentrant updateReward(msg.sender) { - require(balanceOfStakeToken(msg.sender) > 0, "no balance"); - super.withdraw(); - emit Withdrawn(msg.sender, balanceOfStakeToken(msg.sender)); - } - - function exit() external { - withdrawAll(); - getReward(); - } - - function getReward() internal nonReentrant updateReward(msg.sender) { - uint256 reward = earned(msg.sender); - if (reward > 0) { - rewards[msg.sender] = 0; - rewardToken.safeTransfer(msg.sender, reward); - emit RewardPaid(msg.sender, reward); - } - } - - // use it after create and approve of reward token - - function notifyRewardAmount(uint256 reward) external override onlyRewardDistribution updateReward(address(0)) { - if (block.timestamp >= periodFinish) { - rewardRate = reward.div(duration); - } else { - uint256 remaining = periodFinish.sub(block.timestamp); - uint256 leftover = remaining.mul(rewardRate); - rewardRate = reward.add(leftover).div(duration); - } - lastUpdateTime = block.timestamp; - periodFinish = block.timestamp.add(duration); - periodStop = periodFinish; - rewardToken.safeTransferFrom(msg.sender, address(this), reward); - emit RewardAdded(reward); - } - - function addRoutes(address[] memory _route) public override onlyOwner { - super.addRoutes(_route); - } - - function activationRoute(address[] memory _route, bool _isActive) public override onlyOwner { - super.activationRoute(_route, _isActive); - } - - function setPeriodStop(uint256 _periodStop) external onlyRewardDistribution { - require(periodStop <= periodFinish, "Incorrect stop"); - periodStop = _periodStop; - } - - /** - * @dev get staked values nominated in stable coin for the wallet and for all - * @param wallet wallet for getting staked value - * @return senderStake staked value for the wallet - * @return totalStake staked value for all wallet on the contract - */ - function getStakedValuesinUSD(address wallet) public view returns (uint256 senderStake, uint256 totalStake) { - uint256 oneStakeToken = 10**uint256(IERC20Extented(stakeToken).decimals()); - senderStake = balanceOfStakeToken(wallet).mul(getTokenPrice(stakeToken)).div(oneStakeToken); - totalStake = totalSupply().mul(getTokenPrice(stakeToken)).div(oneStakeToken); - } - - function getAmountOut(uint256 amountIn, address[] memory path) public view returns (uint256) { - return EmiswapLib.getAmountsOut(address(emiFactory), amountIn, path)[path.length - 1]; - } - - // ------------------------------------------------------------------------ - // - // ------------------------------------------------------------------------ - /** - * @dev Owner can transfer out any accidentally sent ERC20 tokens - * @param tokenAddress Address of ERC-20 token to transfer - * @param beneficiary Address to transfer to - * @param amount of tokens to transfer - */ - function transferAnyERC20Token( - address tokenAddress, - address beneficiary, - uint256 amount - ) public onlyOwner returns (bool success) { - require(tokenAddress != address(0), "address 0!"); - require(tokenAddress != address(stakeToken), "not staketoken"); - - return IERC20Upgradeable(tokenAddress).transfer(beneficiary, amount); - } -} diff --git a/hardhat.config.js b/hardhat.config.js deleted file mode 100644 index 9ce40a6..0000000 --- a/hardhat.config.js +++ /dev/null @@ -1,83 +0,0 @@ -require("dotenv").config(); -require("@nomiclabs/hardhat-etherscan"); -require("@nomiclabs/hardhat-waffle"); -require("hardhat-gas-reporter"); -require("solidity-coverage"); -require('@openzeppelin/hardhat-upgrades'); - -const ALCHEMY_API_KEY = process.env.ALCHEMY_API_KEY || ""; -const KOVAN_PRIVATE_KEY = process.env.KOVAN_PRIVATE_KEY || ""; -const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY || ""; - -// This is a sample Hardhat task. To learn how to create your own go to -// https://hardhat.org/guides/create-task.html -task("accounts", "Prints the list of accounts", async () => { - const accounts = await ethers.getSigners(); - - for (const account of accounts) { - console.log(account.address); - } -}); - -// You need to export an object to set up your config -// Go to https://hardhat.org/config/ to learn more - -/** - * @type import('hardhat/config').HardhatUserConfig - */ -module.exports = { - solidity: { - compilers: [ - { - version: "0.5.16", - settings: { - optimizer: { - enabled: true, - runs: 200 - } - } - }, - { - version: "0.6.0", - settings: { - optimizer: { - enabled: true, - runs: 200 - } - } - }, - { - version: "0.6.12", - settings: { - optimizer: { - enabled: true, - runs: 200 - } - } - } - ] - }, - defaultNetwork: "hardhat", - networks: { - hardhat: { - }, - kovan: { - url: `https://eth-kovan.alchemyapi.io/v2/${ALCHEMY_API_KEY}`, - accounts: [`${KOVAN_PRIVATE_KEY}`] - }, - ganache: { - url: "http://127.0.0.1:8545", - gasLimit: 6000000000, - defaultBalanceEther: 10 - } - }, - gasReporter: { - enabled: false, - currency: "USD", - }, - etherscan: { - // Your API key for Etherscan - // Obtain one at https://etherscan.io/ - apiKey: ETHERSCAN_API_KEY - } -} \ No newline at end of file diff --git a/package.json b/package.json deleted file mode 100644 index b59cbfa..0000000 --- a/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "multifarming", - "version": "1.0.0", - "private": true, - "scripts": { - "compile": "npx hardhat compile", - "test": "npx hardhat test", - "prettier-test": "npx prettier --write test --print-width 120 --tab-width 4", - "prettier-scripts": "npx prettier --write scripts --print-width 120 --tab-width 4", - "prettier-sol": "prettier --write \"contracts/**/*.sol\" --print-width 120 --tab-width 4", - "contract-flatten": "npx hardhat flatten contracts/RewardPoolMulti.sol > flat/RewardPoolMulti.flat.sol" - }, - "dependencies": { - "@openzeppelin/contracts": "^3.4.0", - "@openzeppelin/contracts-upgradeable": "^3.3.0", - "@openzeppelin/hardhat-upgrades": "^1.10.0", - "hardhat": "^2.6.7" - }, - "devDependencies": { - "@nomiclabs/hardhat-ethers": "^2.0.0", - "@nomiclabs/hardhat-etherscan": "^2.1.3", - "@nomiclabs/hardhat-waffle": "^2.0.0", - "chai": "^4.2.0", - "dotenv": "^10.0.0", - "eslint": "^7.29.0", - "eslint-config-prettier": "^8.3.0", - "eslint-config-standard": "^16.0.3", - "eslint-plugin-import": "^2.23.4", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prettier": "^3.4.0", - "eslint-plugin-promise": "^5.1.0", - "ethereum-waffle": "^3.0.0", - "ethers": "^5.0.0", - "hardhat-gas-reporter": "^1.0.4", - "prettier": "^2.3.2", - "prettier-plugin-solidity": "^1.0.0-beta.13", - "solhint": "^3.3.6", - "solidity-coverage": "^0.7.16" - } -} diff --git a/test/main-test.js b/test/main-test.js deleted file mode 100644 index 1e48db1..0000000 --- a/test/main-test.js +++ /dev/null @@ -1,402 +0,0 @@ -const { expect, assert } = require("chai"); -const { constants, utils, BigNumber } = require("ethers"); -const { ethers, network } = require("hardhat"); -const { tokens, tokensDec } = require("../utils/utils"); - -const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; - -describe("Farming", function () { - let MockLP, esw, usdt, weth, RewardPoolMulti, emiRouter, emiFactory, routes; - before(async () => { - [deployer, owner, Alice, Bob, Clarc] = await ethers.getSigners(); - }); - - beforeEach("Contracts created", async function () { - const MOCKLP = await ethers.getContractFactory("MockLP"); - lpInstance = await MOCKLP.deploy(); - await lpInstance.deployed(); - - const MOCKESW = await ethers.getContractFactory("MockESW"); - esw = await MOCKESW.deploy(); - await esw.deployed(); - - const MOCKUSDT = await ethers.getContractFactory("MockUSDT"); - usdt = await MOCKUSDT.deploy(); - await usdt.deployed(); - - const MOCKUSDC = await ethers.getContractFactory("MockUSDT"); - usdc = await MOCKUSDC.deploy(); - await usdc.deployed(); - - const MOCKDAI = await ethers.getContractFactory("MockWETH"); - dai = await MOCKDAI.deploy(); - await dai.deployed(); - - const MOCKWETH = await ethers.getContractFactory("MockWETH"); - weth = await MOCKWETH.deploy(); - await weth.deployed(); - - const MOCKWBTC = await ethers.getContractFactory("MockWBTC"); - wbtc = await MOCKWBTC.deploy(); - await wbtc.deployed(); - - const MOCKUNI = await ethers.getContractFactory("MockWETH"); - uni = await MOCKUNI.deploy(); - await uni.deployed(); - - const MOCKWMATIC = await ethers.getContractFactory("MockWETH"); - wmatic = await MOCKWMATIC.deploy(); - await wmatic.deployed(); - - const EMIFACTORY = await ethers.getContractFactory("EmiFactory"); - emiFactory = await EMIFACTORY.deploy(); - await emiFactory.deployed(); - - const EMIROUTER = await ethers.getContractFactory("EmiRouter"); - emiRouter = await EMIROUTER.deploy(emiFactory.address, weth.address); - await emiRouter.deployed(); - /** - available pairs - wbtc-weth - wbtc-uni - esw-weth - weth-usdt - wmatic-esw - dai-usdc - - routes to usdt: - wbtc-weth-usdt - esw-weth-usdt - uni-wbtc-weth-usdt - wmatic-esw-weth-usdt - - no routes to usdt: - dai-usdc - */ - - // wbtc-weth Add liquidity (100:10000) - await wbtc.approve(emiRouter.address, tokensDec("100", 8)); - await weth.approve(emiRouter.address, tokensDec("10000", 18)); - await emiRouter.addLiquidity( - wbtc.address, - weth.address, - tokensDec("100", 8), - tokensDec("10000", 18), - tokens("0"), - tokens("0"), - ZERO_ADDRESS - ); - - // wbtc-uni Add liquidity (40:100000) - await wbtc.approve(emiRouter.address, tokensDec("40", 8)); - await uni.approve(emiRouter.address, tokensDec("100000", 18)); - await emiRouter.addLiquidity( - wbtc.address, - uni.address, - tokensDec("40", 8), - tokensDec("100000", 18), - tokens("0"), - tokens("0"), - ZERO_ADDRESS - ); - - // esw-weth Add liquidity (10000:1) - await esw.approve(emiRouter.address, tokensDec("100000000", 18)); - await weth.approve(emiRouter.address, tokensDec("10000", 18)); - await emiRouter.addLiquidity( - esw.address, - weth.address, - tokensDec("100000000", 18), - tokensDec("10000", 18), - tokens("0"), - tokens("0"), - ZERO_ADDRESS - ); - - // weth-usdt Add liquidity (1:2000) - await weth.approve(emiRouter.address, tokensDec("10000", 18)); - await usdt.approve(emiRouter.address, tokensDec("20000000", 6)); - await emiRouter.addLiquidity( - weth.address, - usdt.address, - tokensDec("10000", 18), - tokensDec("20000000", 6), - tokens("0"), - tokens("0"), - ZERO_ADDRESS - ); - - // wmatic-esw Add liquidity (10000:250000) - await wmatic.approve(emiRouter.address, tokensDec("10000", 18)); - await esw.approve(emiRouter.address, tokensDec("250000", 18)); - await emiRouter.addLiquidity( - wmatic.address, - esw.address, - tokensDec("10000", 18), - tokensDec("250000", 18), - tokens("0"), - tokens("0"), - ZERO_ADDRESS - ); - - // dai-usdc Add liquidity (100000:100000) - await dai.approve(emiRouter.address, tokensDec("100000", 18)); - await usdc.approve(emiRouter.address, tokensDec("100000", 6)); - await emiRouter.addLiquidity( - dai.address, - usdc.address, - tokensDec("100000", 18), - tokensDec("100000", 6), - tokens("0"), - tokens("0"), - ZERO_ADDRESS - ); - - /* const REWARDPOOLMULTI = await ethers.getContractFactory("RewardPoolMulti"); - RewardPoolMulti = await REWARDPOOLMULTI.deploy( - esw.address, - owner.address, - emiFactory.address, - usdt.address, - 90 * 24 * 60 * 60, - 30 * 24 * 60 * 60 - ); - await RewardPoolMulti.deployed(); */ - - // Router - REWARDPOOLMULTI = await ethers.getContractFactory("RewardPoolMulti"); - RewardPoolMulti = await upgrades.deployProxy(REWARDPOOLMULTI, [ - esw.address, - owner.address, - emiFactory.address, - usdt.address, - 90 * 24 * 60 * 60, - 30 * 24 * 60 * 60, - ]); - await RewardPoolMulti.deployed(); - - /* add routes - (weth)-usdt - wbtc-weth-usdt - esw-weth-usdt - uni-wbtc-weth-usdt - wmatic-esw-weth-usdt - */ - - routes = [ - [usdt.address], - [weth.address, usdt.address], - [wbtc.address, weth.address, usdt.address], - [esw.address, weth.address, usdt.address], - [uni.address, wbtc.address, weth.address, usdt.address], - [wmatic.address, esw.address, weth.address, usdt.address], - ]; - - for (const i of routes.keys()) { - await RewardPoolMulti.connect(owner).addRoutes(routes[i]); - } - - // start farming - await esw.transfer(owner.address, tokens(1_000_000)); - await esw.connect(owner).approve(RewardPoolMulti.address, tokensDec(1_000_000, 18)); - await RewardPoolMulti.connect(owner).notifyRewardAmount(tokensDec(1_000_000, 18)); - }); - - it("check routes", async function () { - for (const i of routes.keys()) { - route = await RewardPoolMulti.getRouteInfo(i); - for (const r of route.routeRes.keys()) { - expect(route.routeRes[r]).to.be.equals(routes[i][r]); - } - } - }); - - it("run reward simple ERC-20", async function () { - // try to deactivate route - let routeArr = [wmatic.address, esw.address, weth.address, usdt.address]; - let resgetRouteBefore = await RewardPoolMulti.connect(Alice).getRoute(routeArr); - await RewardPoolMulti.connect(owner).activationRoute(routeArr, false); - let resgetRouteAfter = await RewardPoolMulti.connect(Alice).getRoute(routeArr); - - // check route correctness - for (const iterator of routeArr.keys()) { - expect(routeArr[iterator]).to.be.equal(resgetRouteBefore.routeRes[iterator]); - } - - // check isActive parameter changed - expect(resgetRouteBefore.isActiveRes).to.be.equal(true); - expect(resgetRouteAfter.isActiveRes).to.be.equal(false); - - // try to duplicate - await expect(RewardPoolMulti.connect(owner).addRoutes([weth.address, usdt.address])).to.be.revertedWith( - "route already added" - ); - - // try to add route not to usdt - await expect(RewardPoolMulti.connect(owner).addRoutes([weth.address, uni.address])).to.be.revertedWith( - "set route to stable" - ); - - // try to add route from not owner - await expect(RewardPoolMulti.connect(Alice).addRoutes([weth.address, usdt.address])).to.be.revertedWith( - "Ownable: caller is not the owner" - ); - - // owner send by 1_000_000 to Alice and Bob - - await esw.transfer(Alice.address, tokens(1_000_000)); - await esw.transfer(Bob.address, tokens(1_000_000)); - await esw.connect(Alice).approve(RewardPoolMulti.address, tokens(100)); - await esw.connect(Bob).approve(RewardPoolMulti.address, tokens(200)); - - let pools = await emiRouter.getPoolDataList([wbtc.address, wbtc.address], [uni.address, weth.address]); - let wbtc_weth_pool = await lpInstance.attach(pools[1].pool); - - let resComponentWETH = await RewardPoolMulti.getTokenAmountinLP( - wbtc_weth_pool.address, - "10000000000000000000000", - weth.address - ); - expect(resComponentWETH).to.be.equal("9999999999999999999000"); - - // send 10 LP to Alice, 1 LP to Bob - await wbtc_weth_pool.transfer(Alice.address, tokens("10")); - await wbtc_weth_pool.transfer(Bob.address, tokens("1")); - - // prepare for staking - await wbtc_weth_pool.connect(Alice).approve(RewardPoolMulti.address, tokens("10")); - await wbtc_weth_pool.connect(Bob).approve(RewardPoolMulti.address, tokens("1")); - // prepare for incorrect stake - await wbtc.transfer(Alice.address, tokensDec("10", 8)); - await wbtc.connect(Alice).approve(RewardPoolMulti.address, tokens("10")); - - // incorrect stake - await expect(RewardPoolMulti.connect(Alice).stake(wbtc.address, tokens(10), tokens(10))).to.be.revertedWith( - "token incorrect or not LP" - ); - - // get WETH tokens in some LP tokens for LP wbtc-weth Add liquidity (100:10000) - // 10000000000000000001000 LP has 100e8 WBTC and 10000e18 WETH - // 100000000000000000 LP has 99999 WBTC 99999999999999999 WETH - // 10000000000000000000000 WETH on LP * 100000000000000000 LP / 10000000000000000001000 LP = 99999999999999999 WETH - resComponentWETH = await RewardPoolMulti.getTokenAmountinLP( - wbtc_weth_pool.address, - "100000000000000000", - weth.address - ); - // 10000000000 WBTC on LP * 100000000000000000 LP / 10000000000000000001000 LP = 99999 WBTC - let resComponentWBTC = await RewardPoolMulti.getTokenAmountinLP( - wbtc_weth_pool.address, - "100000000000000000", - wbtc.address - ); - - expect( - await RewardPoolMulti.getTokenAmountinLP(wbtc_weth_pool.address, tokensDec(10000, 18), weth.address) - ).to.be.equal("9999999999999999999000"); - expect( - await RewardPoolMulti.getTokenAmountinLP(wbtc_weth_pool.address, tokensDec(10000, 18), wbtc.address) - ).to.be.equal("9999999999"); - expect( - await RewardPoolMulti.getTokenAmountinLP(wbtc_weth_pool.address, tokensDec(1, 18), weth.address) - ).to.be.equal("999999999999999999"); - expect( - await RewardPoolMulti.getTokenAmountinLP(wbtc_weth_pool.address, tokensDec(1, 18), wbtc.address) - ).to.be.equal("999999"); - expect( - await RewardPoolMulti.getTokenAmountinLP(wbtc_weth_pool.address, "100000000000000000", weth.address) - ).to.be.equal("99999999999999999"); - expect( - await RewardPoolMulti.getTokenAmountinLP(wbtc_weth_pool.address, "100000000000000000", wbtc.address) - ).to.be.equal("99999"); - expect( - await RewardPoolMulti.getTokenAmountinLP(wbtc_weth_pool.address, "10000000000000", wbtc.address) - ).to.be.equal("9"); - expect( - await RewardPoolMulti.getTokenAmountinLP(wbtc_weth_pool.address, "1000000000000", wbtc.address) - ).to.be.equal("0"); - expect( - await RewardPoolMulti.getTokenAmountinLP(wbtc_weth_pool.address, "1000000000000", weth.address) - ).to.be.equal("999999999999"); - expect(await RewardPoolMulti.getTokenAmountinLP(wbtc_weth_pool.address, "100", weth.address)).to.be.equal("99"); - - let resTokenPrice = await RewardPoolMulti.getTokenPrice(weth.address); - - let resTokenPriceArr = []; - for (const i of routes.keys()) { - resTokenPriceArr.push(await RewardPoolMulti.getAmountOut(tokens("1"), [weth.address].concat(routes[i]))); - } - - // weth.address -> usdt.address is 1999800019 - expect(resTokenPriceArr[0]).to.be.equal(resTokenPrice); - - //3999600037 via WBTC - expect(await RewardPoolMulti.getLPValueInStable(wbtc_weth_pool.address, tokens("1"))).to.be.equal("3999600037"); - - let resESW = await RewardPoolMulti.getStakeValuebyLP(wbtc_weth_pool.address, tokens("1")); - let resLP = await RewardPoolMulti.getLPValuebyStake(wbtc_weth_pool.address, resESW); - // differ between 1LP and reversed LP must be equal or lower than 0.000000000255000256 - expect(BigNumber.from(tokens("1")).sub(resLP)).to.be.at.most("255000256"); - - // correct stake - let resESWfor10LP = await RewardPoolMulti.getStakeValuebyLP(wbtc_weth_pool.address, tokens("10")); - //console.log("resESWfor10LP", resESWfor10LP.toString()); - - await esw.connect(Alice).approve(RewardPoolMulti.address, resESWfor10LP); - await RewardPoolMulti.connect(Alice).stake(wbtc_weth_pool.address, tokens(10), resESWfor10LP); - - let resESWfor1LP = await RewardPoolMulti.getStakeValuebyLP(wbtc_weth_pool.address, tokens("1")); - await esw.connect(Bob).approve(RewardPoolMulti.address, resESWfor1LP); - await RewardPoolMulti.connect(Bob).stake(wbtc_weth_pool.address, tokens(1), resESWfor1LP); - - await network.provider.send("evm_increaseTime", [60 * 60]); // 60 secs to pass - await network.provider.send("evm_mine"); - - await expect(RewardPoolMulti.connect(Alice).exit()).to.be.revertedWith("withdraw blocked"); - - await network.provider.send("evm_increaseTime", [90 * 24 * 60 * 60]); // 30 days to pass - await network.provider.send("evm_mine"); - - let eswAliceBeforeExit = await esw.balanceOf(Alice.address); - let eswBobBeforeExit = await esw.balanceOf(Bob.address); - - await RewardPoolMulti.connect(Alice).exit(); - await RewardPoolMulti.connect(Bob).exit(); - - let eswAliceAfterExit = await esw.balanceOf(Alice.address); - let eswBobAfterExit = await esw.balanceOf(Bob.address); - - //console.log("totalSupply", (await RewardPoolMulti.totalSupply()).toString()); - expect(await RewardPoolMulti.totalSupply()).to.be.equal("0"); - console.log("ESW on farming", (await esw.balanceOf(RewardPoolMulti.address)).toString()); - console.log("Alice total earned", eswAliceAfterExit.sub(eswAliceBeforeExit).toString()); - console.log("Bob total earned", eswBobAfterExit.sub(eswBobBeforeExit).toString()); - }); - - it("stake minimal values", async () => { - // owner send by 1_000_000 to Alice - await esw.transfer(Alice.address, tokens(1_000_000)); - await esw.connect(Alice).approve(RewardPoolMulti.address, tokens(100)); - - let pools = await emiRouter.getPoolDataList([wbtc.address, wbtc.address], [uni.address, weth.address]); - let wbtc_weth_pool = await lpInstance.attach(pools[1].pool); - - // expect 0.000000001 LP wbtc_weth_pool = 0.000003 USDT - expect(await RewardPoolMulti.getLPValueInStable(wbtc_weth_pool.address, "1000000000")).to.be.equal("3"); - - // expect 0.0000000001 LP wbtc_weth_pool = 0.000000 USDT - expect(await RewardPoolMulti.getLPValueInStable(wbtc_weth_pool.address, "100000000")).to.be.equal("0"); - - // prepare wbtc_weth_pool LP for staking - await wbtc_weth_pool.transfer(Alice.address, tokens("1")); - await wbtc_weth_pool.connect(Alice).approve(RewardPoolMulti.address, tokens("1")); - - // stake 0.000000001 LP wbtc_weth_pool = 0.000003 USDT of LP that greater than 0.000001 USDT - await RewardPoolMulti.connect(Alice).stake(wbtc_weth_pool.address, "1000000000", tokens(1)); - - // stake 0.0000000001 LP wbtc_weth_pool = 0.000000 USDT of LP smaller that smaller than 0.000001 USDT - await expect( - RewardPoolMulti.connect(Alice).stake(wbtc_weth_pool.address, "100000000", tokens(1)) - ).to.be.revertedWith("not enough stake token amount"); - }); -}); diff --git a/utils/utils.js b/utils/utils.js deleted file mode 100644 index b035595..0000000 --- a/utils/utils.js +++ /dev/null @@ -1,26 +0,0 @@ -const { BigNumber } = require("@ethersproject/bignumber"); - -function tokens(val) { - return BigNumber.from(val).mul(BigNumber.from("10").pow(18)).toString(); -} - -function tokensDec(val, dec) { - return BigNumber.from(val).mul(BigNumber.from("10").pow(dec)).toString(); -} - -async function shiftBlocks(network, shiftValue) { - for (const iterator of [...Array(shiftValue).keys()]) { - await network.provider.send("evm_mine"); - } -} - -function timeout(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -module.exports = { - tokens, - tokensDec, - shiftBlocks, - timeout -};