Overview
AVAX Balance
AVAX Value
$780.77 (@ $22.80/AVAX)More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 918 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Swap | 13607857 | 1100 days ago | IN | 0 AVAX | 0.02096347 | ||||
Swap | 13605059 | 1100 days ago | IN | 0 AVAX | 0.01651939 | ||||
Swap | 13600838 | 1100 days ago | IN | 0 AVAX | 0.02738877 | ||||
Swap | 13600805 | 1100 days ago | IN | 0 AVAX | 0.0291596 | ||||
Swap | 13600778 | 1100 days ago | IN | 0 AVAX | 0.02824734 | ||||
Swap | 13600758 | 1100 days ago | IN | 0 AVAX | 0.03049453 | ||||
Swap | 13600335 | 1100 days ago | IN | 0 AVAX | 0.017985 | ||||
Swap | 13571730 | 1101 days ago | IN | 0 AVAX | 0.02287347 | ||||
Swap | 13566835 | 1101 days ago | IN | 0 AVAX | 0.0247723 | ||||
External Swap | 13561181 | 1101 days ago | IN | 0 AVAX | 0.03581065 | ||||
Swap | 13489493 | 1103 days ago | IN | 0 AVAX | 0.01590943 | ||||
Swap | 13485291 | 1103 days ago | IN | 0 AVAX | 0.01845943 | ||||
Swap | 13481664 | 1103 days ago | IN | 0 AVAX | 0.00172828 | ||||
Swap | 13465184 | 1103 days ago | IN | 0 AVAX | 0.02064602 | ||||
External Swap | 13451490 | 1104 days ago | IN | 0 AVAX | 0.028996 | ||||
Swap | 13389975 | 1105 days ago | IN | 0 AVAX | 0.01699817 | ||||
Swap | 13352569 | 1106 days ago | IN | 0 AVAX | 0.03511031 | ||||
Swap | 13339858 | 1106 days ago | IN | 0 AVAX | 0.02775092 | ||||
External Swap | 13329782 | 1106 days ago | IN | 0 AVAX | 0.02435525 | ||||
External Swap | 13319967 | 1107 days ago | IN | 0.33198593 AVAX | 0.01697367 | ||||
Swap | 13317469 | 1107 days ago | IN | 0 AVAX | 0.00765769 | ||||
Swap | 13317433 | 1107 days ago | IN | 30 AVAX | 0.0077442 | ||||
External Swap | 13289709 | 1107 days ago | IN | 0 AVAX | 0.06398404 | ||||
External Swap | 13289675 | 1107 days ago | IN | 0 AVAX | 0.05868242 | ||||
External Swap | 13289662 | 1107 days ago | IN | 0 AVAX | 0.05876769 |
Latest 25 internal transactions (View All)
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
13607857 | 1100 days ago | 4.12577629 AVAX | ||||
13607857 | 1100 days ago | 4.12577629 AVAX | ||||
13571730 | 1101 days ago | 0.17861335 AVAX | ||||
13571730 | 1101 days ago | 0.17861335 AVAX | ||||
13489493 | 1103 days ago | 5.81729254 AVAX | ||||
13489493 | 1103 days ago | 5.81729254 AVAX | ||||
13352569 | 1106 days ago | 2.86453198 AVAX | ||||
13352569 | 1106 days ago | 2.86453198 AVAX | ||||
13329782 | 1106 days ago | 0.37620779 AVAX | ||||
13329782 | 1106 days ago | 0.37620779 AVAX | ||||
13319967 | 1107 days ago | 0.33198593 AVAX | ||||
13317433 | 1107 days ago | 30 AVAX | ||||
13284779 | 1107 days ago | 0.43193181 AVAX | ||||
13284779 | 1107 days ago | 0.43193181 AVAX | ||||
13264935 | 1108 days ago | 6 AVAX | ||||
13241183 | 1108 days ago | 0.16134069 AVAX | ||||
13241183 | 1108 days ago | 0.16134069 AVAX | ||||
13241147 | 1108 days ago | 0.26233561 AVAX | ||||
13241147 | 1108 days ago | 0.26233561 AVAX | ||||
13197809 | 1109 days ago | 5.71410577 AVAX | ||||
13197809 | 1109 days ago | 5.71410577 AVAX | ||||
13179175 | 1110 days ago | 1,068 AVAX | ||||
12996934 | 1114 days ago | 2 AVAX | ||||
12948475 | 1115 days ago | 2.3151116 AVAX | ||||
12931361 | 1116 days ago | 0.32922856 AVAX |
Loading...
Loading
Contract Name:
WooRouter
Compiler Version
v0.6.12+commit.27d51765
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import './interfaces/IWooPP.sol'; import './interfaces/IWETH.sol'; import './interfaces/IWooRouter.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/utils/Address.sol'; import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; /// @title Woo Router implementation. /// @notice Router for stateless execution of swaps against Woo private pool. contract WooRouter is IWooRouter, Ownable, ReentrancyGuard { using SafeMath for uint256; using SafeERC20 for IERC20; /* ----- Constant variables ----- */ // Erc20 placeholder address for native tokens (e.g. eth, bnb, matic, etc) address constant ETH_PLACEHOLDER_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; /* ----- State variables ----- */ // Wrapper for native tokens (e.g. eth, bnb, matic, etc) // BSC WBNB: 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c address public immutable override WETH; IWooPP public override wooPool; mapping(address => bool) public isWhitelisted; address public quoteToken; /* ----- Callback Function ----- */ receive() external payable { // only accept ETH from WETH or whitelisted external swaps. assert(msg.sender == WETH || isWhitelisted[msg.sender]); } /* ----- Query & swap APIs ----- */ constructor(address weth, address newPool) public { require(weth != address(0), 'WooRouter: weth_ZERO_ADDR'); WETH = weth; setPool(newPool); } /// @inheritdoc IWooRouter function querySwap( address fromToken, address toToken, uint256 fromAmount ) external view override returns (uint256 toAmount) { require(fromToken != address(0), 'WooRouter: fromToken_ADDR_ZERO'); require(toToken != address(0), 'WooRouter: toToken_ADDR_ZERO'); fromToken = (fromToken == ETH_PLACEHOLDER_ADDR) ? WETH : fromToken; toToken = (toToken == ETH_PLACEHOLDER_ADDR) ? WETH : toToken; if (fromToken == quoteToken) { toAmount = wooPool.querySellQuote(toToken, fromAmount); } else if (toToken == quoteToken) { toAmount = wooPool.querySellBase(fromToken, fromAmount); } else { uint256 quoteAmount = wooPool.querySellBase(fromToken, fromAmount); toAmount = wooPool.querySellQuote(toToken, quoteAmount); } } /// @inheritdoc IWooRouter function swap( address fromToken, address toToken, uint256 fromAmount, uint256 minToAmount, address payable to, address rebateTo ) external payable override nonReentrant returns (uint256 realToAmount) { require(fromToken != address(0), 'WooRouter: fromToken_ADDR_ZERO'); require(toToken != address(0), 'WooRouter: toToken_ADDR_ZERO'); require(to != address(0), 'WooRouter: to_ADDR_ZERO'); bool isFromETH = fromToken == ETH_PLACEHOLDER_ADDR; bool isToETH = toToken == ETH_PLACEHOLDER_ADDR; fromToken = isFromETH ? WETH : fromToken; toToken = isToETH ? WETH : toToken; // Step 1: transfer the source tokens to WooRouter if (isFromETH) { require(fromAmount <= msg.value, 'WooRouter: fromAmount_INVALID'); IWETH(WETH).deposit{value: msg.value}(); } else { TransferHelper.safeTransferFrom(fromToken, msg.sender, address(this), fromAmount); } // Step 2: swap and transfer TransferHelper.safeApprove(fromToken, address(wooPool), fromAmount); if (fromToken == quoteToken) { // case 1: quoteToken --> baseToken realToAmount = _sellQuoteAndTransfer(isToETH, toToken, fromAmount, minToAmount, to, rebateTo); } else if (toToken == quoteToken) { // case 2: fromToken --> quoteToken realToAmount = wooPool.sellBase(fromToken, fromAmount, minToAmount, to, rebateTo); } else { // case 3: fromToken --> quoteToken --> toToken uint256 quoteAmount = wooPool.sellBase(fromToken, fromAmount, 0, address(this), rebateTo); TransferHelper.safeApprove(quoteToken, address(wooPool), quoteAmount); realToAmount = _sellQuoteAndTransfer(isToETH, toToken, quoteAmount, minToAmount, to, rebateTo); } // Step 3: firing event emit WooRouterSwap( SwapType.WooSwap, isFromETH ? ETH_PLACEHOLDER_ADDR : fromToken, isToETH ? ETH_PLACEHOLDER_ADDR : toToken, fromAmount, realToAmount, msg.sender, to, rebateTo ); } /// @inheritdoc IWooRouter function externalSwap( address approveTarget, address swapTarget, address fromToken, address toToken, uint256 fromAmount, address payable to, bytes calldata data ) external payable override { externalSwap(approveTarget, swapTarget, fromToken, toToken, fromAmount, 0, to, data); } /// @inheritdoc IWooRouter function externalSwap( address approveTarget, address swapTarget, address fromToken, address toToken, uint256 fromAmount, uint256 minToAmount, address payable to, bytes calldata data ) public payable override nonReentrant returns (uint256 realToAmount) { require(approveTarget != address(0), 'WooRouter: approveTarget_ADDR_ZERO'); require(swapTarget != address(0), 'WooRouter: swapTarget_ADDR_ZERO'); require(fromToken != address(0), 'WooRouter: fromToken_ADDR_ZERO'); require(toToken != address(0), 'WooRouter: toToken_ADDR_ZERO'); require(to != address(0), 'WooRouter: to_ADDR_ZERO'); require(isWhitelisted[approveTarget], 'WooRouter: APPROVE_TARGET_NOT_ALLOWED'); require(isWhitelisted[swapTarget], 'WooRouter: SWAP_TARGET_NOT_ALLOWED'); uint256 preBalance = _generalBalanceOf(toToken, address(this)); _internalFallbackSwap(approveTarget, swapTarget, fromToken, fromAmount, data); uint256 postBalance = _generalBalanceOf(toToken, address(this)); require(preBalance <= postBalance, 'WooRouter: balance_ERROR'); realToAmount = postBalance.sub(preBalance); require(realToAmount >= minToAmount, 'WooRouter: realToAmount_NOT_ENOUGH'); _generalTransfer(toToken, to, realToAmount); emit WooRouterSwap(SwapType.DodoSwap, fromToken, toToken, fromAmount, realToAmount, msg.sender, to, address(0)); } /* ----- External Functions ---- */ /// @dev query the swap price for baseToken -> quoteToken. /// @param baseToken the base token to sell /// @param baseAmount the amout of base token to sell /// @return quoteAmount the amount of swapped quote token function querySellBase(address baseToken, uint256 baseAmount) external view returns (uint256 quoteAmount) { require(baseToken != address(0), 'WooRouter: baseToken_ADDR_ZERO'); baseToken = (baseToken == ETH_PLACEHOLDER_ADDR) ? WETH : baseToken; quoteAmount = wooPool.querySellBase(baseToken, baseAmount); } /// @dev query the swap price for quoteToken -> baseToken. /// @param baseToken the base token to swap /// @param quoteAmount the amount of quote token to swap /// @return baseAmount the amount of base token after swap function querySellQuote(address baseToken, uint256 quoteAmount) external view returns (uint256 baseAmount) { require(baseToken != address(0), 'WooRouter: baseToken_ADDR_ZERO'); baseToken = (baseToken == ETH_PLACEHOLDER_ADDR) ? WETH : baseToken; baseAmount = wooPool.querySellQuote(baseToken, quoteAmount); } /// @dev swap baseToken -> quoteToken /// @param baseToken the base token /// @param baseAmount the amount of base token to sell /// @param minQuoteAmount the minimum quote amount to receive /// @param to the destination address /// @param rebateTo the rebate address /// @return realQuoteAmount the exact received amount of quote token function sellBase( address baseToken, uint256 baseAmount, uint256 minQuoteAmount, address to, address rebateTo ) external nonReentrant returns (uint256 realQuoteAmount) { require(baseToken != address(0), 'WooRouter: baseToken_ADDR_ZERO'); require(to != address(0), 'WooRouter: to_ADDR_ZERO'); TransferHelper.safeTransferFrom(baseToken, msg.sender, address(this), baseAmount); TransferHelper.safeApprove(baseToken, address(wooPool), baseAmount); realQuoteAmount = wooPool.sellBase(baseToken, baseAmount, minQuoteAmount, to, rebateTo); emit WooRouterSwap( SwapType.WooSwap, baseToken, quoteToken, baseAmount, realQuoteAmount, msg.sender, to, rebateTo ); } /// @dev swap quoteToken -> baseToken /// @param baseToken the base token to receive /// @param quoteAmount the amount of quote token to sell /// @param minBaseAmount the minimum amount of base token for swap /// @param to the destination address /// @param rebateTo the address for the rebate /// @return realBaseAmount the exact received amount of base token to receive function sellQuote( address baseToken, uint256 quoteAmount, uint256 minBaseAmount, address to, address rebateTo ) external nonReentrant returns (uint256 realBaseAmount) { require(baseToken != address(0), 'WooRouter: baseToken_ADDR_ZERO'); require(to != address(0), 'WooRouter: to_ADDR_ZERO'); TransferHelper.safeTransferFrom(quoteToken, msg.sender, address(this), quoteAmount); TransferHelper.safeApprove(quoteToken, address(wooPool), quoteAmount); realBaseAmount = wooPool.sellQuote(baseToken, quoteAmount, minBaseAmount, to, rebateTo); emit WooRouterSwap( SwapType.WooSwap, quoteToken, baseToken, quoteAmount, realBaseAmount, msg.sender, to, rebateTo ); } /* ----- Admin functions ----- */ /// @dev Rescue the specified funds when stuck happen /// @param token token address /// @param amount amount of token to rescue function rescueFunds(address token, uint256 amount) external nonReentrant onlyOwner { require(token != address(0), 'WooRouter: token_ADDR_ZERO'); TransferHelper.safeTransfer(token, msg.sender, amount); } /// @dev Set wooPool from newPool /// @param newPool Wooracle address function setPool(address newPool) public nonReentrant onlyOwner { require(newPool != address(0), 'WooRouter: newPool_ADDR_ZERO'); wooPool = IWooPP(newPool); quoteToken = wooPool.quoteToken(); require(quoteToken != address(0), 'WooRouter: quoteToken_ADDR_ZERO'); emit WooPoolChanged(newPool); } /// @dev Add target address into whitelist /// @param target address that approved by WooRouter /// @param whitelisted approve to using WooRouter or not function setWhitelisted(address target, bool whitelisted) external nonReentrant onlyOwner { require(target != address(0), 'WooRouter: target_ADDR_ZERO'); isWhitelisted[target] = whitelisted; } /* ----- Private Function ----- */ function _sellQuoteAndTransfer( bool isToETH, address toToken, uint256 quoteAmount, uint256 minToAmount, address payable to, address rebateTo ) private returns (uint256 realToAmount) { if (isToETH) { realToAmount = wooPool.sellQuote(toToken, quoteAmount, minToAmount, address(this), rebateTo); IWETH(WETH).withdraw(realToAmount); require(to != address(0), 'WooRouter: to_ZERO_ADDR'); TransferHelper.safeTransferETH(to, realToAmount); } else { realToAmount = wooPool.sellQuote(toToken, quoteAmount, minToAmount, to, rebateTo); } } function _internalFallbackSwap( address approveTarget, address swapTarget, address fromToken, uint256 fromAmount, bytes calldata data ) private { require(isWhitelisted[approveTarget], 'WooRouter: APPROVE_TARGET_NOT_ALLOWED'); require(isWhitelisted[swapTarget], 'WooRouter: SWAP_TARGET_NOT_ALLOWED'); if (fromToken != ETH_PLACEHOLDER_ADDR) { TransferHelper.safeTransferFrom(fromToken, msg.sender, address(this), fromAmount); TransferHelper.safeApprove(fromToken, approveTarget, fromAmount); } else { require(fromAmount <= msg.value, 'WooRouter: fromAmount_INVALID'); } (bool success, ) = swapTarget.call{value: fromToken == ETH_PLACEHOLDER_ADDR ? fromAmount : 0}(data); require(success, 'WooRouter: FALLBACK_SWAP_FAILED'); } function _generalTransfer( address token, address payable to, uint256 amount ) private { if (amount > 0) { if (token == ETH_PLACEHOLDER_ADDR) { TransferHelper.safeTransferETH(to, amount); } else { TransferHelper.safeTransfer(token, to, amount); } } } function _generalBalanceOf(address token, address who) private view returns (uint256) { return token == ETH_PLACEHOLDER_ADDR ? who.balance : IERC20(token).balanceOf(who); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/utils/Pausable.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; import '../../../interfaces/Alpaca/IAlpacaVault.sol'; import '../../../interfaces/Alpaca/IFairLaunch.sol'; import '../../../interfaces/PancakeSwap/IPancakeRouter.sol'; import '../../../interfaces/IWooAccessManager.sol'; import '../../../interfaces/IStrategy.sol'; import '../../../interfaces/IWETH.sol'; import '../BaseStrategy.sol'; contract StrategyAlpaca is BaseStrategy { using SafeERC20 for IERC20; using SafeMath for uint256; /* ----- State Variables ----- */ address public alpacaVault; address public fairLaunch; uint256 public immutable pid; address[] public rewardToWantRoute; /* ----- Constant Variables ----- */ address public constant wrappedEther = address(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); address public constant reward = address(0x8F0528cE5eF7B51152A59745bEfDD91D97091d2F); address public constant uniRouter = address(0x10ED43C718714eb63d5aA57B78B54704E256024E); constructor( address initVault, address initAccessManager, address initAlpacaVault, address initFairLaunch, uint256 initPid, address[] memory initRewardToWantRoute ) public BaseStrategy(initVault, initAccessManager) { (address stakeToken, , , , ) = IFairLaunch(initFairLaunch).poolInfo(initPid); require(stakeToken == initAlpacaVault, 'StrategyAlpaca: wrong_initPid'); alpacaVault = initAlpacaVault; fairLaunch = initFairLaunch; pid = initPid; rewardToWantRoute = initRewardToWantRoute; _giveAllowances(); } /* ----- Public Functions ----- */ function harvest() public override whenNotPaused { require(msg.sender == tx.origin || msg.sender == address(vault), 'StrategyAlpaca: EOA_or_vault'); (uint256 amount, , , ) = IFairLaunch(fairLaunch).userInfo(pid, address(this)); if (amount > 0) { IFairLaunch(fairLaunch).harvest(pid); } uint256 rewardBalance = IERC20(reward).balanceOf(address(this)); if (rewardBalance > 0) { uint256 wantBalBefore = IERC20(want).balanceOf(address(this)); IPancakeRouter(uniRouter).swapExactTokensForTokens( rewardBalance, 0, rewardToWantRoute, address(this), now.add(600) ); uint256 wantBalAfter = IERC20(want).balanceOf(address(this)); uint256 perfAmount = wantBalAfter.sub(wantBalBefore); chargePerformanceFee(perfAmount); } deposit(); } function deposit() public override whenNotPaused nonReentrant { uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { if (want == wrappedEther) { IWETH(wrappedEther).withdraw(wantBalance); IAlpacaVault(alpacaVault).deposit{value: wantBalance}(wantBalance); } else { IAlpacaVault(alpacaVault).deposit(wantBalance); } IFairLaunch(fairLaunch).deposit(address(this), pid, IAlpacaVault(alpacaVault).balanceOf(address(this))); } } function withdraw(uint256 amount) public override nonReentrant { require(msg.sender == address(vault), 'StrategyAlpaca: not_vault'); require(amount > 0, 'StrategyAlpaca: amount_ZERO'); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance < amount) { uint256 ibAmount = amount.mul(IAlpacaVault(alpacaVault).totalSupply()).div( IAlpacaVault(alpacaVault).totalToken() ); IFairLaunch(fairLaunch).withdraw(address(this), pid, ibAmount); IAlpacaVault(alpacaVault).withdraw(IERC20(alpacaVault).balanceOf(address(this))); if (want == wrappedEther) { _wrapEther(); } wantBalance = IERC20(want).balanceOf(address(this)); } // just in case the decimal precision for the very left staking amount uint256 withdrawAmount = amount < wantBalance ? amount : wantBalance; uint256 fee = chargeWithdrawalFee(withdrawAmount); if (withdrawAmount > fee) { TransferHelper.safeTransfer(want, vault, withdrawAmount.sub(fee)); } } function balanceOfPool() public view override returns (uint256) { (uint256 amount, , , ) = IFairLaunch(fairLaunch).userInfo(pid, address(this)); return amount.mul(IAlpacaVault(alpacaVault).totalToken()).div(IAlpacaVault(alpacaVault).totalSupply()); } /* ----- Private Functions ----- */ function _giveAllowances() internal override { TransferHelper.safeApprove(reward, uniRouter, 0); TransferHelper.safeApprove(reward, uniRouter, uint256(-1)); TransferHelper.safeApprove(want, alpacaVault, 0); TransferHelper.safeApprove(want, alpacaVault, uint256(-1)); TransferHelper.safeApprove(alpacaVault, fairLaunch, 0); TransferHelper.safeApprove(alpacaVault, fairLaunch, uint256(-1)); } function _removeAllowances() internal override { TransferHelper.safeApprove(reward, uniRouter, 0); TransferHelper.safeApprove(want, alpacaVault, 0); TransferHelper.safeApprove(alpacaVault, fairLaunch, 0); } function _withdrawAll() private { uint256 amount = balanceOfPool(); uint256 ibAmount = amount.mul(IAlpacaVault(alpacaVault).totalSupply()).div( IAlpacaVault(alpacaVault).totalToken() ); IFairLaunch(fairLaunch).withdraw(address(this), pid, ibAmount); IAlpacaVault(alpacaVault).withdraw(IERC20(alpacaVault).balanceOf(address(this))); if (want == wrappedEther) { _wrapEther(); } } function _wrapEther() private { // NOTE: alpaca vault withdrawal returns the native BNB token; so wrapEther is required. uint256 etherBalance = address(this).balance; if (etherBalance > 0) { IWETH(wrappedEther).deposit{value: etherBalance}(); } } /* ----- Admin Functions ----- */ function retireStrat() external override { require(msg.sender == vault, '!vault'); _withdrawAll(); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { TransferHelper.safeTransfer(want, vault, wantBalance); } } function emergencyExit() external override onlyAdmin { _withdrawAll(); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { TransferHelper.safeTransfer(want, vault, wantBalance); } } receive() external payable {} }
// 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); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./IERC20.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; /** * @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 SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 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(IERC20 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(IERC20 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(IERC20 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(IERC20 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"); } } }
// 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; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../utils/Context.sol"; /** * @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 Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { 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; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor () internal { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { require(!paused(), "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { require(paused(), "Pausable: not paused"); _; } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// 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::safeApprove: 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::safeTransfer: 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::transferFrom: transferFrom failed' ); } function safeTransferETH(address to, uint256 value) internal { (bool success, ) = to.call{value: value}(new bytes(0)); require(success, 'TransferHelper::safeTransferETH: ETH transfer failed'); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IAlpacaVault { function balanceOf(address account) external view returns (uint256); function totalToken() external view returns (uint256); function totalSupply() external view returns (uint256); function deposit(uint256 amountToken) external payable; function withdraw(uint256 share) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IFairLaunch { function deposit( address _for, uint256 pid, uint256 _amount ) external; // staking function withdraw( address _for, uint256 _pid, uint256 _amount ) external; // unstaking function harvest(uint256 _pid) external; function pendingAlpaca(uint256 _pid, address _user) external returns (uint256); function userInfo(uint256, address) external view returns ( uint256 amount, uint256 rewardDebt, uint256 bonusDebt, uint256 fundedBy ); function poolInfo(uint256) external view returns ( address stakeToken, uint256 allocPoint, uint256 lastRewardBlock, uint256 accAlpacaPerShare, uint256 accAlpacaPerShareTilBonusEnd ); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IPancakeRouter { function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns ( uint256 amountA, uint256 amountB, uint256 liquidity ); }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /// @title Reward manager interface for WooFi Swap. /// @notice this is for swap rebate or potential incentive program interface IWooAccessManager { /* ----- Events ----- */ event FeeAdminUpdated(address indexed feeAdmin, bool flag); event VaultAdminUpdated(address indexed vaultAdmin, bool flag); event RebateAdminUpdated(address indexed rebateAdmin, bool flag); event ZeroFeeVaultUpdated(address indexed vault, bool flag); /* ----- External Functions ----- */ function isFeeAdmin(address feeAdmin) external returns (bool); function isVaultAdmin(address vaultAdmin) external returns (bool); function isRebateAdmin(address rebateAdmin) external returns (bool); function isZeroFeeVault(address vault) external returns (bool); /* ----- Admin Functions ----- */ /// @notice Sets feeAdmin function setFeeAdmin(address feeAdmin, bool flag) external; /// @notice Batch sets feeAdmin function batchSetFeeAdmin(address[] calldata feeAdmins, bool[] calldata flags) external; /// @notice Sets vaultAdmin function setVaultAdmin(address vaultAdmin, bool flag) external; /// @notice Batch sets vaultAdmin function batchSetVaultAdmin(address[] calldata vaultAdmins, bool[] calldata flags) external; /// @notice Sets rebateAdmin function setRebateAdmin(address rebateAdmin, bool flag) external; /// @notice Batch sets rebateAdmin function batchSetRebateAdmin(address[] calldata rebateAdmins, bool[] calldata flags) external; /// @notice Sets zeroFeeVault function setZeroFeeVault(address vault, bool flag) external; /// @notice Batch sets zeroFeeVault function batchSetZeroFeeVault(address[] calldata vaults, bool[] calldata flags) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IStrategy { function vault() external view returns (address); function want() external view returns (address); function beforeDeposit() external; function deposit() external; function withdraw(uint256) external; function balanceOf() external view returns (uint256); function balanceOfWant() external view returns (uint256); function balanceOfPool() external view returns (uint256); function harvest() external; function retireStrat() external; function emergencyExit() external; function paused() external view returns (bool); function inCaseTokensGetStuck(address stuckToken) external; }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /// @title Wrapped ETH. /// BSC: https://bscscan.com/address/0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c#code interface IWETH { /// @dev Deposit ETH into WETH function deposit() external payable; /// @dev Transfer WETH to receiver /// @param to address of WETH receiver /// @param value amount of WETH to transfer /// @return get true when succeed, else false function transfer(address to, uint256 value) external returns (bool); /// @dev Withdraw WETH to ETH function withdraw(uint256) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/utils/Pausable.sol'; import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; import '../../interfaces/PancakeSwap/IMasterChef.sol'; import '../../interfaces/IWooAccessManager.sol'; import '../../interfaces/IStrategy.sol'; import '../../interfaces/IVault.sol'; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** * Base strategy abstract contract for: * - vault and access manager setup * - fees management * - pause / unpause */ abstract contract BaseStrategy is Ownable, Pausable, IStrategy, ReentrancyGuard { using SafeERC20 for IERC20; using SafeMath for uint256; /* ----- State Variables ----- */ address public override want; address public immutable override vault; bool public harvestOnDeposit = true; /* ----- Constant Variables ----- */ uint256 public constant FEE_MAX = 10000; uint256 public performanceFee = 300; // 1 in 10000th: 100: 1%, 300: 3% uint256 public withdrawalFee = 0; // 1 in 10000th: 1: 0.01%, 10: 0.1% address public performanceTreasury; address public withdrawalTreasury; IWooAccessManager public accessManager; event PerformanceFeeUpdated(uint256 newFee); event WithdrawalFeeUpdated(uint256 newFee); constructor(address initVault, address initAccessManager) public { require(initVault != address(0), 'BaseStrategy: initVault_ZERO_ADDR'); require(initAccessManager != address(0), 'BaseStrategy: initAccessManager_ZERO_ADDR'); vault = initVault; accessManager = IWooAccessManager(initAccessManager); want = IVault(initVault).want(); } modifier onlyAdmin() { require(owner() == _msgSender() || accessManager.isVaultAdmin(msg.sender), 'BaseStrategy: NOT_ADMIN'); _; } /* ----- Public Functions ----- */ function beforeDeposit() public virtual override { require(msg.sender == address(vault), 'BaseStrategy: NOT_VAULT'); if (harvestOnDeposit) { harvest(); } } function balanceOf() public view override returns (uint256) { return balanceOfWant().add(balanceOfPool()); } function balanceOfWant() public view override returns (uint256) { return IERC20(want).balanceOf(address(this)); } /* ----- Internal Functions ----- */ function chargePerformanceFee(uint256 amount) internal returns (uint256) { uint256 fee = amount.mul(performanceFee).div(FEE_MAX); if (fee > 0) { TransferHelper.safeTransfer(want, performanceTreasury, fee); } return fee; } function chargeWithdrawalFee(uint256 amount) internal returns (uint256) { uint256 fee = amount.mul(withdrawalFee).div(FEE_MAX); if (fee > 0) { TransferHelper.safeTransfer(want, withdrawalTreasury, fee); } return fee; } /* ----- Abstract Method ----- */ function balanceOfPool() public view virtual override returns (uint256); function deposit() public virtual override; function withdraw(uint256 amount) external virtual override; function harvest() public virtual override; function retireStrat() external virtual override; function emergencyExit() external virtual override; function _giveAllowances() internal virtual; function _removeAllowances() internal virtual; /* ----- Admin Functions ----- */ function setPerformanceFee(uint256 fee) external onlyAdmin { require(fee <= FEE_MAX, 'BaseStrategy: fee_EXCCEEDS_MAX'); performanceFee = fee; emit PerformanceFeeUpdated(fee); } function setWithdrawalFee(uint256 fee) external onlyAdmin { require(fee <= FEE_MAX, 'BaseStrategy: fee_EXCCEEDS_MAX'); require(fee <= 500, 'BaseStrategy: fee_EXCCEEDS_5%'); // less than 5% withdrawalFee = fee; emit WithdrawalFeeUpdated(fee); } function setPerformanceTreasury(address treasury) external onlyAdmin { require(treasury != address(0), 'BaseStrategy: treasury_ZERO_ADDR'); performanceTreasury = treasury; } function setWithdrawalTreasury(address treasury) external onlyAdmin { require(treasury != address(0), 'BaseStrategy: treasury_ZERO_ADDR'); withdrawalTreasury = treasury; } function setHarvestOnDeposit(bool newHarvestOnDeposit) external onlyAdmin { harvestOnDeposit = newHarvestOnDeposit; } function pause() public onlyAdmin { _pause(); _removeAllowances(); } function unpause() external onlyAdmin { _unpause(); _giveAllowances(); deposit(); } function paused() public view override(IStrategy, Pausable) returns (bool) { return Pausable.paused(); } function inCaseTokensGetStuck(address stuckToken) external override onlyAdmin { require(stuckToken != address(want), 'BaseStrategy: stuckToken_NOT_WANT'); require(stuckToken != address(0), 'BaseStrategy: stuckToken_ZERO_ADDR'); uint256 amount = IERC20(stuckToken).balanceOf(address(this)); if (amount > 0) { TransferHelper.safeTransfer(stuckToken, msg.sender, amount); } } function inCaseNativeTokensGetStuck() external onlyAdmin { // NOTE: vault never needs native tokens to do the yield farming; // This native token balance indicates a user's incorrect transfer. if (address(this).balance > 0) { TransferHelper.safeTransferETH(msg.sender, address(this).balance); } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(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); } } } }
// 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 Context { 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; } }
// 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 ReentrancyGuard { // 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; constructor () internal { _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; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IMasterChef { function deposit(uint256 pid, uint256 amount) external; function withdraw(uint256 pid, uint256 amount) external; function enterStaking(uint256 amount) external; function leaveStaking(uint256 amount) external; function emergencyWithdraw(uint256 pid) external; function pendingCake(uint256 pid, address user) external view returns (uint256); function poolInfo(uint256 pid) external view returns ( address, uint256, uint256, uint256 ); function userInfo(uint256 pid, address user) external view returns (uint256, uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IVault { function want() external view returns (address); function deposit(uint256 amount) external payable; function withdraw(uint256 shares) external; function earn() external; function available() external view returns (uint256); function balance() external view returns (uint256); function getPricePerFullShare() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/utils/Pausable.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; import './BaseStrategy.sol'; contract VoidStrategy is BaseStrategy { using SafeERC20 for IERC20; using SafeMath for uint256; constructor(address initVault, address initAccessManager) public BaseStrategy(initVault, initAccessManager) { _giveAllowances(); } /* ----- External Functions ----- */ function withdraw(uint256 amount) external override nonReentrant { require(msg.sender == vault, 'VoidStrategy: NOT_VAULT'); uint256 wantBalance = IERC20(want).balanceOf(address(this)); uint256 withdrawAmount = amount < wantBalance ? amount : wantBalance; uint256 fee = chargeWithdrawalFee(withdrawAmount); if (withdrawAmount > fee) { TransferHelper.safeTransfer(want, vault, withdrawAmount.sub(fee)); } } function harvest() public override whenNotPaused { require(msg.sender == tx.origin || msg.sender == vault, 'VoidStrategy: EOA_OR_VAULT'); deposit(); } function deposit() public override whenNotPaused nonReentrant {} function balanceOfPool() public view override returns (uint256) { return 0; } /* ----- Private Functions ----- */ function _giveAllowances() internal override {} function _removeAllowances() internal override {} function retireStrat() external override { require(msg.sender == vault, '!vault'); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { TransferHelper.safeTransfer(want, vault, wantBalance); } } function emergencyExit() external override onlyAdmin { uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { TransferHelper.safeTransfer(want, vault, wantBalance); } } }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import './libraries/InitializableOwnable.sol'; import './libraries/DecimalMath.sol'; import './interfaces/IWooracle.sol'; import './interfaces/IWooVaultManager.sol'; import './interfaces/IWooGuardian.sol'; import './interfaces/AggregatorV3Interface.sol'; import './interfaces/IWooAccessManager.sol'; import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; import '@openzeppelin/contracts/utils/EnumerableSet.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; contract WooVaultManager is InitializableOwnable, ReentrancyGuard, IWooVaultManager { using SafeMath for uint256; using DecimalMath for uint256; using SafeERC20 for IERC20; using EnumerableSet for EnumerableSet.AddressSet; mapping(address => uint256) public override vaultWeight; uint256 public totalWeight; IWooPP private wooPP; address public immutable override quoteToken; // USDT address public immutable rewardToken; // WOO EnumerableSet.AddressSet private vaultSet; IWooAccessManager public accessManager; /* ----- Modifiers ----- */ modifier onlyAdmin() { require(msg.sender == _OWNER_ || accessManager.isVaultAdmin(msg.sender), 'WooVaultManager: NOT_ADMIN'); _; } constructor( address newQuoteToken, address newRewardToken, address newAccessManager ) public { require(newQuoteToken != address(0), 'WooVaultManager: INVALID_QUOTE'); require(newRewardToken != address(0), 'WooVaultManager: INVALID_RAWARD_TOKEN'); initOwner(msg.sender); quoteToken = newQuoteToken; rewardToken = newRewardToken; accessManager = IWooAccessManager(newAccessManager); } function allVaults() external view override returns (address[] memory) { address[] memory vaults = new address[](vaultSet.length()); for (uint256 i = 0; i < vaultSet.length(); ++i) { vaults[i] = vaultSet.at(i); } return vaults; } function addReward(uint256 amount) external override nonReentrant { if (amount == 0) { return; } uint256 balanceBefore = IERC20(quoteToken).balanceOf(address(this)); TransferHelper.safeTransferFrom(quoteToken, msg.sender, address(this), amount); uint256 balanceAfter = IERC20(quoteToken).balanceOf(address(this)); require(balanceAfter.sub(balanceBefore) >= amount, 'WooVaultManager: amount INSUFF'); } function pendingReward(address vaultAddr) external view override returns (uint256) { require(vaultAddr != address(0), 'WooVaultManager: vaultAddr_ZERO_ADDR'); uint256 totalReward = IERC20(quoteToken).balanceOf(address(this)); return totalReward.mul(vaultWeight[vaultAddr]).div(totalWeight); } function pendingAllReward() external view override returns (uint256) { return IERC20(quoteToken).balanceOf(address(this)); } // ----------- Admin Functions ------------- // function setVaultWeight(address vaultAddr, uint256 weight) external override onlyAdmin { require(vaultAddr != address(0), 'WooVaultManager: vaultAddr_ZERO_ADDR'); // NOTE: First clear all the pending reward if > 100u to keep the things fair if (IERC20(quoteToken).balanceOf(address(this)) >= 1e20) { distributeAllReward(); } uint256 prevWeight = vaultWeight[vaultAddr]; vaultWeight[vaultAddr] = weight; totalWeight = totalWeight.add(weight).sub(prevWeight); if (weight == 0) { vaultSet.remove(vaultAddr); } else { vaultSet.add(vaultAddr); } emit VaultWeightUpdated(vaultAddr, weight); } function distributeAllReward() public override onlyAdmin { uint256 totalRewardInQuote = IERC20(quoteToken).balanceOf(address(this)); if (totalRewardInQuote == 0 || totalWeight == 0) { return; } uint256 balanceBefore = IERC20(rewardToken).balanceOf(address(this)); TransferHelper.safeApprove(quoteToken, address(wooPP), totalRewardInQuote); uint256 wooAmount = IWooPP(wooPP).sellQuote(rewardToken, totalRewardInQuote, 0, address(this), address(0)); uint256 balanceAfter = IERC20(rewardToken).balanceOf(address(this)); require(balanceAfter.sub(balanceBefore) >= wooAmount, 'WooVaultManager: woo amount INSUFF'); for (uint256 i = 0; i < vaultSet.length(); ++i) { address vaultAddr = vaultSet.at(i); uint256 vaultAmount = wooAmount.mul(vaultWeight[vaultAddr]).div(totalWeight); if (vaultAmount > 0) { TransferHelper.safeTransfer(rewardToken, vaultAddr, vaultAmount); } emit RewardDistributed(vaultAddr, vaultAmount); } } function setWooPP(address newWooPP) external onlyAdmin { require(newWooPP != address(0), 'WooVaultManager: newWooPP_ZERO_ADDR'); wooPP = IWooPP(newWooPP); require(wooPP.quoteToken() == quoteToken, 'WooVaultManager: wooPP_quote_token_INVALID'); } function setAccessManager(address newAccessManager) external onlyOwner { require(newAccessManager != address(0), 'WooVaultManager: newAccessManager_ZERO_ADDR'); accessManager = IWooAccessManager(newAccessManager); } function emergencyWithdraw(address token, address to) public onlyOwner { require(token != address(0), 'WooVaultManager: token_ZERO_ADDR'); require(to != address(0), 'WooVaultManager: to_ZERO_ADDR'); TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this))); } }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /** * @title Ownable initializable contract. * * @notice Ownership related functions */ contract InitializableOwnable { address public _OWNER_; address public _NEW_OWNER_; bool internal _INITIALIZED_; // ============ Events ============ event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // ============ Modifiers ============ modifier notInitialized() { require(!_INITIALIZED_, 'InitializableOwnable: SHOULD_NOT_BE_INITIALIZED'); _; } modifier onlyOwner() { require(msg.sender == _OWNER_, 'InitializableOwnable: NOT_OWNER'); _; } // ============ Functions ============ /// @dev Init _OWNER_ from newOwner and set _INITIALIZED_ as true /// @param newOwner new owner address function initOwner(address newOwner) public notInitialized { _INITIALIZED_ = true; _OWNER_ = newOwner; } /// @dev Set _NEW_OWNER_ from newOwner /// @param newOwner new owner address function transferOwnership(address newOwner) public onlyOwner { emit OwnershipTransferPrepared(_OWNER_, newOwner); _NEW_OWNER_ = newOwner; } /// @dev Set _OWNER_ from _NEW_OWNER_ and set _NEW_OWNER_ equal zero address function claimOwnership() public { require(msg.sender == _NEW_OWNER_, 'InitializableOwnable: INVALID_CLAIM'); emit OwnershipTransferred(_OWNER_, _NEW_OWNER_); _OWNER_ = _NEW_OWNER_; _NEW_OWNER_ = address(0); } }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; import '@openzeppelin/contracts/math/SafeMath.sol'; /** * @title DecimalMath * * @notice Functions for fixed point number with 18 decimals */ library DecimalMath { using SafeMath for uint256; uint256 internal constant ONE = 10**18; uint256 internal constant TWO = 2 * 10**18; uint256 internal constant ONE2 = 10**36; function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) { return target.mul(d) / (10**18); } function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) { return _divCeil(target.mul(d), 10**18); } function divFloor(uint256 target, uint256 d) internal pure returns (uint256) { return target.mul(10**18).div(d); } function divCeil(uint256 target, uint256 d) internal pure returns (uint256) { return _divCeil(target.mul(10**18), d); } function reciprocalFloor(uint256 target) internal pure returns (uint256) { return uint256(10**36).div(target); } function reciprocalCeil(uint256 target) internal pure returns (uint256) { return _divCeil(uint256(10**36), target); } function _divCeil(uint256 a, uint256 b) private pure returns (uint256) { uint256 quotient = a.div(b); uint256 remainder = a - quotient * b; if (remainder > 0) { return quotient + 1; } else { return quotient; } } }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /// @title The oracle interface by Woo.Network. /// @notice update and posted the latest price info by Woo. interface IWooracle { /// @dev the quote token for Wooracle's pricing. /// @return the quote token function quoteToken() external view returns (address); /// @dev the price for the given base token /// @param base baseToken address /// @return priceNow the current price of base token /// @return feasible whether the current price is feasible and valid function price(address base) external view returns (uint256 priceNow, bool feasible); function getPrice(address base) external view returns (uint256); function getSpread(address base) external view returns (uint256); function getCoeff(address base) external view returns (uint256); /// @dev returns the state for the given base token. /// @param base baseToken address /// @return priceNow the current price of base token /// @return spreadNow the current spread of base token /// @return coeffNow the slippage coefficient of base token /// @return feasible whether the current state is feasible and valid function state(address base) external view returns ( uint256 priceNow, uint256 spreadNow, uint256 coeffNow, bool feasible ); /// @dev returns the last updated timestamp /// @return the last updated timestamp function timestamp() external view returns (uint256); /// @dev returns whether the base token price is valid. /// @param base baseToken address /// @return whether the base token price is valid. function isFeasible(address base) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /// @title Vault reward manager interface for WooFi Swap. interface IWooVaultManager { event VaultWeightUpdated(address indexed vaultAddr, uint256 weight); event RewardDistributed(address indexed vaultAddr, uint256 amount); /// @dev Gets the reward weight for the given vault. /// @param vaultAddr the vault address /// @return The weight of the given vault. function vaultWeight(address vaultAddr) external view returns (uint256); /// @dev Sets the reward weight for the given vault. /// @param vaultAddr the vault address /// @param weight the vault weight function setVaultWeight(address vaultAddr, uint256 weight) external; /// @dev Adds the reward quote amount. /// Note: The reward will be stored in this manager contract for /// further weight adjusted distribution. /// @param quoteAmount the reward amount in quote token. function addReward(uint256 quoteAmount) external; /// @dev Pending amount in quote token for the given vault. /// @param vaultAddr the vault address function pendingReward(address vaultAddr) external view returns (uint256); /// @dev All pending amount in quote token. /// @return the total pending reward function pendingAllReward() external view returns (uint256); /// @dev Distributes the reward to all the vaults based on the weights. function distributeAllReward() external; /// @dev All the vaults /// @return the vault address array function allVaults() external view returns (address[] memory); /// @dev get the quote token address /// @return address of quote token function quoteToken() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import '../interfaces/IWooPP.sol'; /// @title Guardian interface to ensure the trading price and volume correct interface IWooGuardian { event ChainlinkRefOracleUpdated(address indexed token, address indexed chainlinkRefOracle); /* ----- Main check APIs ----- */ function checkSwapPrice( uint256 price, address fromToken, address toToken ) external view; function checkInputAmount(address token, uint256 inputAmount) external view; function checkSwapAmount( address fromToken, address toToken, uint256 fromAmount, uint256 toAmount ) external view; }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); /// getRoundData and latestRoundData should both raise "No data present" /// if they do not have data to report, instead of returning unset values /// which could be misinterpreted as actual reported values. function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../../utils/Context.sol"; import "./IERC20.sol"; import "../../math/SafeMath.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name_, string memory symbol_) public { _name = name_; _symbol = symbol_; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view virtual returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal virtual { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping (bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement. bytes32 lastvalue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastvalue; // Update the index for the moved value set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { require(set._values.length > index, "EnumerableSet: index out of bounds"); return set._values[index]; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values on the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /// @title Woo private pool for swap. /// @notice Use this contract to directly interfact with woo's synthetic proactive /// marketing making pool. /// @author woo.network interface IWooPP { /* ----- Type declarations ----- */ /// @dev struct info to store the token info struct TokenInfo { uint112 reserve; // Token balance uint112 threshold; // Threshold for reserve update uint32 lastResetTimestamp; // Timestamp for last param update uint64 R; // Rebalance coefficient [0, 1] uint112 target; // Targeted balance for pricing bool isValid; // is this token info valid } /* ----- Events ----- */ event StrategistUpdated(address indexed strategist, bool flag); event FeeManagerUpdated(address indexed newFeeManager); event RewardManagerUpdated(address indexed newRewardManager); event WooracleUpdated(address indexed newWooracle); event WooGuardianUpdated(address indexed newWooGuardian); event ParametersUpdated(address indexed baseToken, uint256 newThreshold, uint256 newR); event Withdraw(address indexed token, address indexed to, uint256 amount); event WooSwap( address indexed fromToken, address indexed toToken, uint256 fromAmount, uint256 toAmount, address from, address indexed to, address rebateTo ); /* ----- External Functions ----- */ /// @dev Swap baseToken into quoteToken /// @param baseToken the base token /// @param baseAmount amount of baseToken that user want to swap /// @param minQuoteAmount minimum amount of quoteToken that user accept to receive /// @param to quoteToken receiver address /// @param rebateTo the wallet address for rebate /// @return quoteAmount the swapped amount of quote token function sellBase( address baseToken, uint256 baseAmount, uint256 minQuoteAmount, address to, address rebateTo ) external returns (uint256 quoteAmount); /// @dev Swap quoteToken into baseToken /// @param baseToken the base token /// @param quoteAmount amount of quoteToken that user want to swap /// @param minBaseAmount minimum amount of baseToken that user accept to receive /// @param to baseToken receiver address /// @param rebateTo the wallet address for rebate /// @return baseAmount the swapped amount of base token function sellQuote( address baseToken, uint256 quoteAmount, uint256 minBaseAmount, address to, address rebateTo ) external returns (uint256 baseAmount); /// @dev Query the amount for selling the base token amount. /// @param baseToken the base token to sell /// @param baseAmount the amount to sell /// @return quoteAmount the swapped quote amount function querySellBase(address baseToken, uint256 baseAmount) external view returns (uint256 quoteAmount); /// @dev Query the amount for selling the quote token. /// @param baseToken the base token to receive (buy) /// @param quoteAmount the amount to sell /// @return baseAmount the swapped base token amount function querySellQuote(address baseToken, uint256 quoteAmount) external view returns (uint256 baseAmount); /// @dev get the quote token address /// @return address of quote token function quoteToken() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/utils/Address.sol'; import '@openzeppelin/contracts/utils/Pausable.sol'; import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; import './libraries/DecimalMath.sol'; import './interfaces/IWooAccessManager.sol'; contract WooStakingVault is ERC20, Ownable, ReentrancyGuard, Pausable { using SafeERC20 for IERC20; using SafeMath for uint256; using DecimalMath for uint256; struct UserInfo { uint256 reserveAmount; // amount of stakedToken user reverseWithdraw uint256 lastReserveWithdrawTime; // keeps track of reverseWithdraw time for potential penalty } /* ----- Events ----- */ event Deposit(address indexed user, uint256 depositAmount, uint256 mintShares); event ReserveWithdraw(address indexed user, uint256 reserveAmount, uint256 burnShares); event Withdraw(address indexed user, uint256 withdrawAmount, uint256 withdrawFee); event InstantWithdraw(address indexed user, uint256 withdrawAmount, uint256 withdrawFee); event RewardAdded( address indexed sender, uint256 balanceBefore, uint256 sharePriceBefore, uint256 balanceAfter, uint256 sharePriceAfter ); /* ----- State variables ----- */ IERC20 public immutable stakedToken; mapping(address => uint256) public costSharePrice; mapping(address => UserInfo) public userInfo; uint256 public totalReserveAmount = 0; // affected by reserveWithdraw and withdraw uint256 public withdrawFeePeriod = 7 days; uint256 public withdrawFee = 500; // 5% (10000 as denominator) address public treasury; IWooAccessManager public wooAccessManager; /* ----- Constant variables ----- */ uint256 public constant MAX_WITHDRAW_FEE_PERIOD = 7 days; uint256 public constant MAX_WITHDRAW_FEE = 500; // 5% (10000 as denominator) constructor( address initialStakedToken, address initialTreasury, address initialWooAccessManager ) public ERC20( string(abi.encodePacked('Interest Bearing ', ERC20(initialStakedToken).name())), string(abi.encodePacked('x', ERC20(initialStakedToken).symbol())) ) { require(initialStakedToken != address(0), 'WooStakingVault: initialStakedToken_ZERO_ADDR'); require(initialTreasury != address(0), 'WooStakingVault: initialTreasury_ZERO_ADDR'); require(initialWooAccessManager != address(0), 'WooStakingVault: initialWooAccessManager_ZERO_ADDR'); stakedToken = IERC20(initialStakedToken); treasury = initialTreasury; wooAccessManager = IWooAccessManager(initialWooAccessManager); } /* ----- External Functions ----- */ function deposit(uint256 amount) external nonReentrant whenNotPaused { require(amount > 0, 'WooStakingVault: amount_CAN_NOT_BE_ZERO'); uint256 balanceBefore = balance(); TransferHelper.safeTransferFrom(address(stakedToken), msg.sender, address(this), amount); uint256 balanceAfter = balance(); amount = balanceAfter.sub(balanceBefore); uint256 xTotalSupply = totalSupply(); uint256 shares = xTotalSupply == 0 ? amount : amount.mul(xTotalSupply).div(balanceBefore); // must be executed before _mint _updateCostSharePrice(amount, shares); _mint(msg.sender, shares); emit Deposit(msg.sender, amount, shares); } function reserveWithdraw(uint256 shares) external nonReentrant { require(shares > 0, 'WooStakingVault: shares_CAN_NOT_BE_ZERO'); require(shares <= balanceOf(msg.sender), 'WooStakingVault: shares exceed balance'); uint256 currentReserveAmount = shares.mulFloor(getPricePerFullShare()); // calculate reserveAmount before _burn uint256 poolBalance = balance(); if (poolBalance < currentReserveAmount) { // in case reserve amount exceeds pool balance currentReserveAmount = poolBalance; } _burn(msg.sender, shares); totalReserveAmount = totalReserveAmount.add(currentReserveAmount); UserInfo storage user = userInfo[msg.sender]; user.reserveAmount = user.reserveAmount.add(currentReserveAmount); user.lastReserveWithdrawTime = block.timestamp; emit ReserveWithdraw(msg.sender, currentReserveAmount, shares); } function withdraw() external nonReentrant { UserInfo storage user = userInfo[msg.sender]; uint256 withdrawAmount = user.reserveAmount; require(withdrawAmount > 0, 'WooStakingVault: withdrawAmount_CAN_NOT_BE_ZERO'); uint256 fee = 0; if (block.timestamp < user.lastReserveWithdrawTime.add(withdrawFeePeriod)) { fee = withdrawAmount.mul(withdrawFee).div(10000); if (fee > 0) { TransferHelper.safeTransfer(address(stakedToken), treasury, fee); } } uint256 withdrawAmountAfterFee = withdrawAmount.sub(fee); user.reserveAmount = 0; totalReserveAmount = totalReserveAmount.sub(withdrawAmount); TransferHelper.safeTransfer(address(stakedToken), msg.sender, withdrawAmountAfterFee); emit Withdraw(msg.sender, withdrawAmount, fee); } function instantWithdraw(uint256 shares) external nonReentrant { require(shares > 0, 'WooStakingVault: shares_CAN_NOT_BE_ZERO'); require(shares <= balanceOf(msg.sender), 'WooStakingVault: shares exceed balance'); uint256 withdrawAmount = shares.mulFloor(getPricePerFullShare()); uint256 poolBalance = balance(); if (poolBalance < withdrawAmount) { withdrawAmount = poolBalance; } _burn(msg.sender, shares); uint256 fee = wooAccessManager.isZeroFeeVault(msg.sender) ? 0 : withdrawAmount.mul(withdrawFee).div(10000); if (fee > 0) { TransferHelper.safeTransfer(address(stakedToken), treasury, fee); } uint256 withdrawAmountAfterFee = withdrawAmount.sub(fee); TransferHelper.safeTransfer(address(stakedToken), msg.sender, withdrawAmountAfterFee); emit InstantWithdraw(msg.sender, withdrawAmount, fee); } function addReward(uint256 amount) external whenNotPaused { // Note: this method is only for adding Woo reward. Users may not call this method to deposit woo token. require(amount > 0, 'WooStakingVault: amount_CAN_NOT_BE_ZERO'); uint256 balanceBefore = balance(); uint256 sharePriceBefore = getPricePerFullShare(); TransferHelper.safeTransferFrom(address(stakedToken), msg.sender, address(this), amount); uint256 balanceAfter = balance(); uint256 sharePriceAfter = getPricePerFullShare(); emit RewardAdded(msg.sender, balanceBefore, sharePriceBefore, balanceAfter, sharePriceAfter); } /* ----- Public Functions ----- */ function getPricePerFullShare() public view returns (uint256) { if (totalSupply() == 0) { return 1e18; } return balance().divFloor(totalSupply()); } function balance() public view returns (uint256) { return stakedToken.balanceOf(address(this)).sub(totalReserveAmount); } /* ----- Private Functions ----- */ function _updateCostSharePrice(uint256 amount, uint256 shares) private { uint256 sharesBefore = balanceOf(msg.sender); uint256 costBefore = costSharePrice[msg.sender]; uint256 costAfter = (sharesBefore.mul(costBefore).add(amount.mul(1e18))).div(sharesBefore.add(shares)); costSharePrice[msg.sender] = costAfter; } /* ----- Admin Functions ----- */ /// @notice Sets withdraw fee period /// @dev Only callable by the contract owner. function setWithdrawFeePeriod(uint256 newWithdrawFeePeriod) external onlyOwner { require( newWithdrawFeePeriod <= MAX_WITHDRAW_FEE_PERIOD, 'WooStakingVault: newWithdrawFeePeriod>MAX_WITHDRAW_FEE_PERIOD' ); withdrawFeePeriod = newWithdrawFeePeriod; } /// @notice Sets withdraw fee /// @dev Only callable by the contract owner. function setWithdrawFee(uint256 newWithdrawFee) external onlyOwner { require(newWithdrawFee <= MAX_WITHDRAW_FEE, 'WooStakingVault: newWithdrawFee>MAX_WITHDRAW_FEE'); withdrawFee = newWithdrawFee; } /// @notice Sets treasury address /// @dev Only callable by the contract owner. function setTreasury(address newTreasury) external onlyOwner { require(newTreasury != address(0), 'WooStakingVault: newTreasury_ZERO_ADDR'); treasury = newTreasury; } /// @notice Sets WooAccessManager /// @dev Only callable by the contract owner. function setWooAccessManager(address newWooAccessManager) external onlyOwner { require(newWooAccessManager != address(0), 'WooStakingVault: newWooAccessManager_ZERO_ADDR'); wooAccessManager = IWooAccessManager(newWooAccessManager); } /** @notice Rescues random funds stuck. This method only saves the irrelevant tokens just in case users deposited in mistake. It cannot transfer any of user staked tokens. */ function inCaseTokensGetStuck(address stuckToken) external onlyOwner { require(stuckToken != address(0), 'WooStakingVault: stuckToken_ZERO_ADDR'); require(stuckToken != address(stakedToken), 'WooStakingVault: stuckToken_CAN_NOT_BE_stakedToken'); uint256 amount = IERC20(stuckToken).balanceOf(address(this)); TransferHelper.safeTransfer(stuckToken, msg.sender, amount); } /// @notice Pause the contract. function pause() external onlyOwner { super._pause(); } /// @notice Restart the contract. function unpause() external onlyOwner { super._unpause(); } }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import './libraries/InitializableOwnable.sol'; import './libraries/DecimalMath.sol'; import './interfaces/IWooracle.sol'; import './interfaces/IWooRebateManager.sol'; import './interfaces/IWooGuardian.sol'; import './interfaces/AggregatorV3Interface.sol'; import './interfaces/IWooAccessManager.sol'; import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; contract WooRebateManager is InitializableOwnable, ReentrancyGuard, IWooRebateManager { using SafeMath for uint256; using DecimalMath for uint256; using SafeERC20 for IERC20; // Note: this is the percent rate of the total swap fee (not the swap volume) // decimal: 18; 1e16 = 1%, 1e15 = 0.1%, 1e14 = 0.01% // // e.g. suppose: // rebateRate = 1e17 (10%), so the rebate amount is total_swap_fee * 10%. mapping(address => uint256) public override rebateRate; // pending rebate amount in quote token mapping(address => uint256) public pendingRebate; IWooPP private wooPP; address public immutable override quoteToken; // USDT address public immutable rewardToken; // WOO IWooAccessManager public accessManager; /* ----- Modifiers ----- */ modifier onlyAdmin() { require(msg.sender == _OWNER_ || accessManager.isRebateAdmin(msg.sender), 'WooRebateManager: NOT_ADMIN'); _; } constructor( address newQuoteToken, address newRewardToken, address newAccessManager ) public { require(newQuoteToken != address(0), 'WooRebateManager: INVALID_QUOTE'); require(newRewardToken != address(0), 'WooRebateManager: INVALID_REWARD_TOKEN'); initOwner(msg.sender); quoteToken = newQuoteToken; rewardToken = newRewardToken; accessManager = IWooAccessManager(newAccessManager); } function pendingRebateInUSDT(address brokerAddr) external view override returns (uint256) { require(brokerAddr != address(0), 'WooRebateManager: zero_brokerAddr'); return pendingRebate[brokerAddr]; } function pendingRebateInWOO(address brokerAddr) external view override returns (uint256) { require(brokerAddr != address(0), 'WooRebateManager: zero_brokerAddr'); return wooPP.querySellQuote(rewardToken, pendingRebate[brokerAddr]); } function claimRebate() external override nonReentrant { require(pendingRebate[msg.sender] > 0, 'WooRebateManager: NO_pending_rebate'); uint256 quoteAmount = pendingRebate[msg.sender]; // Note: set the pending rebate early to make external interactions safe. pendingRebate[msg.sender] = 0; uint256 balanceBefore = IERC20(rewardToken).balanceOf(address(this)); TransferHelper.safeApprove(quoteToken, address(wooPP), quoteAmount); uint256 wooAmount = wooPP.sellQuote(rewardToken, quoteAmount, 0, address(this), address(0)); uint256 balanceAfter = IERC20(rewardToken).balanceOf(address(this)); require(balanceAfter.sub(balanceBefore) >= wooAmount, 'WooRebateManager: woo amount INSUFF'); if (wooAmount > 0) { TransferHelper.safeTransfer(rewardToken, msg.sender, wooAmount); } emit ClaimReward(msg.sender, wooAmount); } /* ----- Admin Functions ----- */ function addRebate(address brokerAddr, uint256 amountInUSDT) external override nonReentrant onlyAdmin { if (brokerAddr == address(0)) { return; } pendingRebate[brokerAddr] = amountInUSDT.add(pendingRebate[brokerAddr]); } function setRebateRate(address brokerAddr, uint256 rate) external override onlyAdmin { require(brokerAddr != address(0), 'WooRebateManager: brokerAddr_ZERO_ADDR'); require(rate <= 1e18, 'WooRebateManager: INVALID_USER_REWARD_RATE'); // rate <= 100% rebateRate[brokerAddr] = rate; emit RebateRateUpdated(brokerAddr, rate); } function setWooPP(address newWooPP) external onlyAdmin { require(newWooPP != address(0), 'WooRebateManager: wooPP_ZERO_ADDR'); wooPP = IWooPP(newWooPP); require(wooPP.quoteToken() == quoteToken, 'WooRebateManager: wooPP_quote_token_INVALID'); } function setAccessManager(address newAccessManager) external onlyOwner { require(newAccessManager != address(0), 'WooRebateManager: newAccessManager_ZERO_ADDR'); accessManager = IWooAccessManager(newAccessManager); } function emergencyWithdraw(address token, address to) public onlyOwner { require(token != address(0), 'WooRebateManager: token_ZERO_ADDR'); require(to != address(0), 'WooRebateManager: to_ZERO_ADDR'); uint256 amount = IERC20(token).balanceOf(address(this)); TransferHelper.safeTransfer(token, to, amount); emit Withdraw(token, to, amount); } }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /// @title Rebate manager interface for WooFi Swap. /// @notice this is for swap rebate or potential incentive program interface IWooRebateManager { event Withdraw(address indexed token, address indexed to, uint256 amount); event RebateRateUpdated(address indexed brokerAddr, uint256 rate); event ClaimReward(address indexed brokerAddr, uint256 amount); /// @dev Gets the rebate rate for the given broker. /// Note: decimal: 18; 1e16 = 1%, 1e15 = 0.1%, 1e14 = 0.01% /// @param brokerAddr the address for rebate /// @return The rebate rate (decimal: 18; 1e16 = 1%, 1e15 = 0.1%, 1e14 = 0.01%) function rebateRate(address brokerAddr) external view returns (uint256); /// @dev set the rebate rate /// @param brokerAddr the rebate address /// @param rate the rebate rate function setRebateRate(address brokerAddr, uint256 rate) external; /// @dev adds the pending reward for the given user. /// @param brokerAddr the address for rebate /// @param amountInUSD the pending reward amount function addRebate(address brokerAddr, uint256 amountInUSD) external; /// @dev Pending amount in $woo. /// @param brokerAddr the address for rebate function pendingRebateInWOO(address brokerAddr) external view returns (uint256); /// @dev Pending amount in $woo. /// @param brokerAddr the address for rebate function pendingRebateInUSDT(address brokerAddr) external view returns (uint256); /// @dev Claims the reward ($woo token will be distributed) function claimRebate() external; /// @dev get the quote token address /// @return address of quote token function quoteToken() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import '../interfaces/IWooPP.sol'; /// @title Woo router interface /// @notice functions to interface with WooFi swap interface IWooRouter { /* ----- Type declarations ----- */ enum SwapType { WooSwap, DodoSwap } /* ----- Events ----- */ event WooRouterSwap( SwapType swapType, address indexed fromToken, address indexed toToken, uint256 fromAmount, uint256 toAmount, address from, address indexed to, address rebateTo ); event WooPoolChanged(address newPool); /* ----- Router properties ----- */ function WETH() external pure returns (address); function wooPool() external pure returns (IWooPP); /* ----- Main query & swap APIs ----- */ /// @dev query the amount to swap fromToken -> toToken /// @param fromToken the from token /// @param toToken the to token /// @param fromAmount the amount of fromToken to swap /// @return toAmount the predicted amount to receive function querySwap( address fromToken, address toToken, uint256 fromAmount ) external view returns (uint256 toAmount); /// @dev swap fromToken -> toToken /// @param fromToken the from token /// @param toToken the to token /// @param fromAmount the amount of fromToken to swap /// @param minToAmount the amount of fromToken to swap /// @param to the destination address /// @param rebateTo the rebate address (optional, can be 0) /// @return realToAmount the amount of toToken to receive function swap( address fromToken, address toToken, uint256 fromAmount, uint256 minToAmount, address payable to, address rebateTo ) external payable returns (uint256 realToAmount); /* ----- 3rd party DEX swap ----- */ /// @dev swap fromToken -> toToken via an external 3rd swap /// @param approveTarget the contract address for token transfer approval /// @param swapTarget the contract address for swap /// @param fromToken the from token /// @param toToken the to token /// @param fromAmount the amount of fromToken to swap /// @param to the destination address /// @param data call data for external call function externalSwap( address approveTarget, address swapTarget, address fromToken, address toToken, uint256 fromAmount, address payable to, bytes calldata data ) external payable; /// @dev swap fromToken -> toToken via an external 3rd swap /// @param approveTarget the contract address for token transfer approval /// @param swapTarget the contract address for swap /// @param fromToken the from token /// @param toToken the to token /// @param fromAmount the amount of fromToken to swap /// @param minToAmount the min amount of swapped toToken /// @param to the destination address /// @param data call data for external call function externalSwap( address approveTarget, address swapTarget, address fromToken, address toToken, uint256 fromAmount, uint256 minToAmount, address payable to, bytes calldata data ) external payable returns (uint256 realToAmount); }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import './libraries/InitializableOwnable.sol'; import './libraries/DecimalMath.sol'; import './interfaces/IWooracle.sol'; import './interfaces/IWooPP.sol'; import './interfaces/IWooFeeManager.sol'; import './interfaces/IWooGuardian.sol'; import './interfaces/AggregatorV3Interface.sol'; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import '@openzeppelin/contracts/utils/Pausable.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; /// @title Woo private pool for swaping. /// @notice the implementation class for interface IWooPP, mainly for query and swap tokens. contract WooPP is InitializableOwnable, ReentrancyGuard, Pausable, IWooPP { /* ----- Type declarations ----- */ using SafeMath for uint256; using DecimalMath for uint256; using SafeERC20 for IERC20; /* ----- State variables ----- */ mapping(address => TokenInfo) public tokenInfo; mapping(address => bool) public isStrategist; /// @inheritdoc IWooPP address public immutable override quoteToken; address public wooracle; IWooGuardian public wooGuardian; IWooFeeManager public feeManager; string public pairsInfo; // e.g. BNB/ETH/BTCB/WOO-USDT /* ----- Modifiers ----- */ modifier onlyStrategist() { require(msg.sender == _OWNER_ || isStrategist[msg.sender], 'WooPP: NOT_STRATEGIST'); _; } constructor( address newQuoteToken, address newWooracle, address newFeeManager, address newWooGuardian ) public { require(newQuoteToken != address(0), 'WooPP: INVALID_QUOTE'); require(newWooracle != address(0), 'WooPP: newWooracle_ZERO_ADDR'); require(newFeeManager != address(0), 'WooPP: newFeeManager_ZERO_ADDR'); require(newWooGuardian != address(0), 'WooPP: newWooGuardian_ZERO_ADDR'); initOwner(msg.sender); quoteToken = newQuoteToken; wooracle = newWooracle; feeManager = IWooFeeManager(newFeeManager); require(feeManager.quoteToken() == newQuoteToken, 'WooPP: feeManager_quoteToken_INVALID'); wooGuardian = IWooGuardian(newWooGuardian); TokenInfo storage quoteInfo = tokenInfo[newQuoteToken]; quoteInfo.isValid = true; } /* ----- External Functions ----- */ /// @inheritdoc IWooPP function querySellBase(address baseToken, uint256 baseAmount) external view override whenNotPaused returns (uint256 quoteAmount) { require(baseToken != address(0), 'WooPP: baseToken_ZERO_ADDR'); require(baseToken != quoteToken, 'WooPP: baseToken==quoteToken'); wooGuardian.checkInputAmount(baseToken, baseAmount); TokenInfo memory baseInfo = tokenInfo[baseToken]; require(baseInfo.isValid, 'WooPP: TOKEN_DOES_NOT_EXIST'); TokenInfo memory quoteInfo = tokenInfo[quoteToken]; _autoUpdate(baseToken, baseInfo, quoteInfo); quoteAmount = getQuoteAmountSellBase(baseToken, baseAmount, baseInfo, quoteInfo); wooGuardian.checkSwapAmount(baseToken, quoteToken, baseAmount, quoteAmount); uint256 lpFee = quoteAmount.mulCeil(feeManager.feeRate(baseToken)); quoteAmount = quoteAmount.sub(lpFee); require(quoteAmount <= IERC20(quoteToken).balanceOf(address(this)), 'WooPP: INSUFF_QUOTE'); } /// @inheritdoc IWooPP function querySellQuote(address baseToken, uint256 quoteAmount) external view override whenNotPaused returns (uint256 baseAmount) { require(baseToken != address(0), 'WooPP: baseToken_ZERO_ADDR'); require(baseToken != quoteToken, 'WooPP: baseToken==quoteToken'); wooGuardian.checkInputAmount(quoteToken, quoteAmount); TokenInfo memory baseInfo = tokenInfo[baseToken]; require(baseInfo.isValid, 'WooPP: TOKEN_DOES_NOT_EXIST'); TokenInfo memory quoteInfo = tokenInfo[quoteToken]; _autoUpdate(baseToken, baseInfo, quoteInfo); uint256 lpFee = quoteAmount.mulCeil(feeManager.feeRate(baseToken)); quoteAmount = quoteAmount.sub(lpFee); baseAmount = getBaseAmountSellQuote(baseToken, quoteAmount, baseInfo, quoteInfo); wooGuardian.checkSwapAmount(quoteToken, baseToken, quoteAmount, baseAmount); require(baseAmount <= IERC20(baseToken).balanceOf(address(this)), 'WooPP: INSUFF_BASE'); } /// @inheritdoc IWooPP function sellBase( address baseToken, uint256 baseAmount, uint256 minQuoteAmount, address to, address rebateTo ) external override nonReentrant whenNotPaused returns (uint256 quoteAmount) { require(baseToken != address(0), 'WooPP: baseToken_ZERO_ADDR'); require(to != address(0), 'WooPP: to_ZERO_ADDR'); require(baseToken != quoteToken, 'WooPP: baseToken==quoteToken'); wooGuardian.checkInputAmount(baseToken, baseAmount); address from = msg.sender; TokenInfo memory baseInfo = tokenInfo[baseToken]; require(baseInfo.isValid, 'WooPP: TOKEN_DOES_NOT_EXIST'); TokenInfo memory quoteInfo = tokenInfo[quoteToken]; _autoUpdate(baseToken, baseInfo, quoteInfo); TransferHelper.safeTransferFrom(baseToken, from, address(this), baseAmount); quoteAmount = getQuoteAmountSellBase(baseToken, baseAmount, baseInfo, quoteInfo); wooGuardian.checkSwapAmount(baseToken, quoteToken, baseAmount, quoteAmount); uint256 lpFee = quoteAmount.mulCeil(feeManager.feeRate(baseToken)); quoteAmount = quoteAmount.sub(lpFee); require(quoteAmount >= minQuoteAmount, 'WooPP: quoteAmount<minQuoteAmount'); TransferHelper.safeApprove(quoteToken, address(feeManager), lpFee); feeManager.collectFee(lpFee, rebateTo); uint256 balanceBefore = IERC20(quoteToken).balanceOf(to); TransferHelper.safeTransfer(quoteToken, to, quoteAmount); require(IERC20(quoteToken).balanceOf(to).sub(balanceBefore) >= minQuoteAmount, 'WooPP: INSUFF_OUTPUT_AMOUNT'); _updateReserve(baseToken, baseInfo, quoteInfo); tokenInfo[baseToken] = baseInfo; tokenInfo[quoteToken] = quoteInfo; emit WooSwap(baseToken, quoteToken, baseAmount, quoteAmount, from, to, rebateTo); } /// @inheritdoc IWooPP function sellQuote( address baseToken, uint256 quoteAmount, uint256 minBaseAmount, address to, address rebateTo ) external override nonReentrant whenNotPaused returns (uint256 baseAmount) { require(baseToken != address(0), 'WooPP: baseToken_ZERO_ADDR'); require(to != address(0), 'WooPP: to_ZERO_ADDR'); require(baseToken != quoteToken, 'WooPP: baseToken==quoteToken'); wooGuardian.checkInputAmount(quoteToken, quoteAmount); address from = msg.sender; TokenInfo memory baseInfo = tokenInfo[baseToken]; require(baseInfo.isValid, 'WooPP: TOKEN_DOES_NOT_EXIST'); TokenInfo memory quoteInfo = tokenInfo[quoteToken]; _autoUpdate(baseToken, baseInfo, quoteInfo); TransferHelper.safeTransferFrom(quoteToken, from, address(this), quoteAmount); uint256 lpFee = quoteAmount.mulCeil(feeManager.feeRate(baseToken)); quoteAmount = quoteAmount.sub(lpFee); baseAmount = getBaseAmountSellQuote(baseToken, quoteAmount, baseInfo, quoteInfo); require(baseAmount >= minBaseAmount, 'WooPP: baseAmount<minBaseAmount'); TransferHelper.safeApprove(quoteToken, address(feeManager), lpFee); feeManager.collectFee(lpFee, rebateTo); wooGuardian.checkSwapAmount(quoteToken, baseToken, quoteAmount, baseAmount); uint256 balanceBefore = IERC20(baseToken).balanceOf(to); TransferHelper.safeTransfer(baseToken, to, baseAmount); require(IERC20(baseToken).balanceOf(to).sub(balanceBefore) >= minBaseAmount, 'WooPP: INSUFF_OUTPUT_AMOUNT'); _updateReserve(baseToken, baseInfo, quoteInfo); tokenInfo[baseToken] = baseInfo; tokenInfo[quoteToken] = quoteInfo; emit WooSwap(quoteToken, baseToken, quoteAmount.add(lpFee), baseAmount, from, to, rebateTo); } /// @dev Set the pairsInfo /// @param newPairsInfo the pairs info to set function setPairsInfo(string calldata newPairsInfo) external nonReentrant onlyStrategist { pairsInfo = newPairsInfo; } /// @dev Get the pool's balance of token /// @param token the token pool to query function poolSize(address token) external view returns (uint256) { return IERC20(token).balanceOf(address(this)); } /// @dev Set wooracle from newWooracle /// @param newWooracle Wooracle address function setWooracle(address newWooracle) external nonReentrant onlyStrategist { require(newWooracle != address(0), 'WooPP: newWooracle_ZERO_ADDR'); wooracle = newWooracle; emit WooracleUpdated(newWooracle); } /// @dev Set wooGuardian from newWooGuardian /// @param newWooGuardian WooGuardian address function setWooGuardian(address newWooGuardian) external nonReentrant onlyStrategist { require(newWooGuardian != address(0), 'WooPP: newWooGuardian_ZERO_ADDR'); wooGuardian = IWooGuardian(newWooGuardian); emit WooGuardianUpdated(newWooGuardian); } /// @dev Set the feeManager. /// @param newFeeManager the fee manager function setFeeManager(address newFeeManager) external nonReentrant onlyStrategist { require(newFeeManager != address(0), 'WooPP: newFeeManager_ZERO_ADDR'); feeManager = IWooFeeManager(newFeeManager); require(feeManager.quoteToken() == quoteToken, 'WooPP: feeManager_quoteToken_INVALID'); emit FeeManagerUpdated(newFeeManager); } /// @dev Add the base token for swap /// @param baseToken the base token /// @param threshold the balance threshold info /// @param R the rebalance refactor function addBaseToken( address baseToken, uint256 threshold, uint256 R ) external nonReentrant onlyStrategist { require(baseToken != address(0), 'WooPP: BASE_TOKEN_ZERO_ADDR'); require(baseToken != quoteToken, 'WooPP: baseToken==quoteToken'); require(threshold <= type(uint112).max, 'WooPP: THRESHOLD_OUT_OF_RANGE'); require(R <= 1e18, 'WooPP: R_OUT_OF_RANGE'); TokenInfo memory info = tokenInfo[baseToken]; require(!info.isValid, 'WooPP: TOKEN_ALREADY_EXISTS'); info.threshold = uint112(threshold); info.R = uint64(R); info.target = max(info.threshold, info.target); info.isValid = true; tokenInfo[baseToken] = info; emit ParametersUpdated(baseToken, threshold, R); } /// @dev Remove the base token /// @param baseToken the base token function removeBaseToken(address baseToken) external nonReentrant onlyStrategist { require(baseToken != address(0), 'WooPP: BASE_TOKEN_ZERO_ADDR'); require(tokenInfo[baseToken].isValid, 'WooPP: TOKEN_DOES_NOT_EXIST'); delete tokenInfo[baseToken]; emit ParametersUpdated(baseToken, 0, 0); } /// @dev Tune the token params /// @param token the token to tune /// @param newThreshold the new balance threshold info /// @param newR the new rebalance refactor function tuneParameters( address token, uint256 newThreshold, uint256 newR ) external nonReentrant onlyStrategist { require(token != address(0), 'WooPP: token_ZERO_ADDR'); require(newThreshold <= type(uint112).max, 'WooPP: THRESHOLD_OUT_OF_RANGE'); require(newR <= 1e18, 'WooPP: R>1'); TokenInfo memory info = tokenInfo[token]; require(info.isValid, 'WooPP: TOKEN_DOES_NOT_EXIST'); info.threshold = uint112(newThreshold); info.R = uint64(newR); info.target = max(info.threshold, info.target); tokenInfo[token] = info; emit ParametersUpdated(token, newThreshold, newR); } /* ----- Admin Functions ----- */ /// @dev Pause the contract. function pause() external onlyStrategist { super._pause(); } /// @dev Restart the contract. function unpause() external onlyStrategist { super._unpause(); } /// @dev Update the strategist info. /// @param strategist the strategist to set /// @param flag true or false function setStrategist(address strategist, bool flag) external nonReentrant onlyStrategist { require(strategist != address(0), 'WooPP: strategist_ZERO_ADDR'); isStrategist[strategist] = flag; emit StrategistUpdated(strategist, flag); } /// @dev Withdraw the token. /// @param token the token to withdraw /// @param to the destination address /// @param amount the amount to withdraw function withdraw( address token, address to, uint256 amount ) public nonReentrant onlyOwner { require(token != address(0), 'WooPP: token_ZERO_ADDR'); require(to != address(0), 'WooPP: to_ZERO_ADDR'); TransferHelper.safeTransfer(token, to, amount); emit Withdraw(token, to, amount); } function withdrawAll(address token, address to) external onlyOwner { withdraw(token, to, IERC20(token).balanceOf(address(this))); } /// @dev Withdraw the token to the OWNER address /// @param token the token function withdrawAllToOwner(address token) external nonReentrant onlyStrategist { require(token != address(0), 'WooPP: token_ZERO_ADDR'); uint256 amount = IERC20(token).balanceOf(address(this)); TransferHelper.safeTransfer(token, _OWNER_, amount); emit Withdraw(token, _OWNER_, amount); } /* ----- Private Functions ----- */ function _autoUpdate( address baseToken, TokenInfo memory baseInfo, TokenInfo memory quoteInfo ) private view { require(baseToken != address(0), 'WooPP: BASETOKEN_ZERO_ADDR'); _updateReserve(baseToken, baseInfo, quoteInfo); // NOTE: only consider the least 32 bigs integer number is good engouh uint32 priceTimestamp = uint32(IWooracle(wooracle).timestamp()); if (priceTimestamp != baseInfo.lastResetTimestamp) { baseInfo.target = max(baseInfo.threshold, baseInfo.reserve); baseInfo.lastResetTimestamp = priceTimestamp; } if (priceTimestamp != quoteInfo.lastResetTimestamp) { quoteInfo.target = max(quoteInfo.threshold, quoteInfo.reserve); quoteInfo.lastResetTimestamp = priceTimestamp; } } function _updateReserve( address baseToken, TokenInfo memory baseInfo, TokenInfo memory quoteInfo ) private view { uint256 baseReserve = IERC20(baseToken).balanceOf(address(this)); uint256 quoteReserve = IERC20(quoteToken).balanceOf(address(this)); require(baseReserve <= type(uint112).max); require(quoteReserve <= type(uint112).max); baseInfo.reserve = uint112(baseReserve); quoteInfo.reserve = uint112(quoteReserve); } // When baseSold >= 0 , users sold the base token function getQuoteAmountLowQuoteSide( uint256 p, uint256 k, uint256 r, uint256 baseAmount ) private pure returns (uint256) { // priceFactor = 1 + k * baseAmount * p * r; uint256 priceFactor = DecimalMath.ONE.add(k.mulCeil(baseAmount).mulCeil(p).mulCeil(r)); // return baseAmount * p / priceFactor; return baseAmount.mulFloor(p).divFloor(priceFactor); // round down } // When baseSold >= 0 function getBaseAmountLowQuoteSide( uint256 p, uint256 k, uint256 r, uint256 quoteAmount ) private pure returns (uint256) { // priceFactor = (1 - k * quoteAmount * r); uint256 priceFactor = DecimalMath.ONE.sub(k.mulFloor(quoteAmount).mulFloor(r)); // return quoteAmount / p / priceFactor; return quoteAmount.divFloor(p).divFloor(priceFactor); } // When quoteSold >= 0 function getBaseAmountLowBaseSide( uint256 p, uint256 k, uint256 r, uint256 quoteAmount ) private pure returns (uint256) { // priceFactor = 1 + k * quoteAmount * r; uint256 priceFactor = DecimalMath.ONE.add(k.mulCeil(quoteAmount).mulCeil(r)); // return quoteAmount / p / priceFactor; return quoteAmount.divFloor(p).divFloor(priceFactor); // round down } // When quoteSold >= 0 function getQuoteAmountLowBaseSide( uint256 p, uint256 k, uint256 r, uint256 baseAmount ) private pure returns (uint256) { // priceFactor = 1 - k * baseAmount * p * r; uint256 priceFactor = DecimalMath.ONE.sub(k.mulFloor(baseAmount).mulFloor(p).mulFloor(r)); // return baseAmount * p / priceFactor; return baseAmount.mulFloor(p).divFloor(priceFactor); // round down } function getBoughtAmount( TokenInfo memory baseInfo, TokenInfo memory quoteInfo, uint256 p, uint256 k, bool isSellBase ) private pure returns (uint256 baseBought, uint256 quoteBought) { uint256 baseSold = 0; if (baseInfo.reserve < baseInfo.target) { baseBought = uint256(baseInfo.target).sub(uint256(baseInfo.reserve)); } else { baseSold = uint256(baseInfo.reserve).sub(uint256(baseInfo.target)); } uint256 quoteSold = 0; if (quoteInfo.reserve < quoteInfo.target) { quoteBought = uint256(quoteInfo.target).sub(uint256(quoteInfo.reserve)); } else { quoteSold = uint256(quoteInfo.reserve).sub(uint256(quoteInfo.target)); } if (baseSold.mulCeil(p) > quoteSold) { baseSold = baseSold.sub(quoteSold.divFloor(p)); quoteSold = 0; } else { quoteSold = quoteSold.sub(baseSold.mulCeil(p)); baseSold = 0; } uint256 virtualBaseBought = getBaseAmountLowBaseSide(p, k, DecimalMath.ONE, quoteSold); if (isSellBase == (virtualBaseBought < baseBought)) { baseBought = virtualBaseBought; } uint256 virtualQuoteBought = getQuoteAmountLowQuoteSide(p, k, DecimalMath.ONE, baseSold); if (isSellBase == (virtualQuoteBought > quoteBought)) { quoteBought = virtualQuoteBought; } } function getQuoteAmountSellBase( address baseToken, uint256 baseAmount, TokenInfo memory baseInfo, TokenInfo memory quoteInfo ) private view returns (uint256 quoteAmount) { uint256 p; uint256 s; uint256 k; bool isFeasible; (p, s, k, isFeasible) = IWooracle(wooracle).state(baseToken); require(isFeasible, 'WooPP: ORACLE_PRICE_NOT_FEASIBLE'); wooGuardian.checkSwapPrice(p, baseToken, quoteToken); // price: p * (1 - s / 2) p = p.mulFloor(DecimalMath.ONE.sub(s.divCeil(DecimalMath.TWO))); uint256 baseBought; uint256 quoteBought; (baseBought, quoteBought) = getBoughtAmount(baseInfo, quoteInfo, p, k, true); if (baseBought > 0) { uint256 quoteSold = getQuoteAmountLowBaseSide(p, k, baseInfo.R, baseBought); if (baseAmount > baseBought) { uint256 newBaseSold = baseAmount.sub(baseBought); quoteAmount = quoteSold.add(getQuoteAmountLowQuoteSide(p, k, DecimalMath.ONE, newBaseSold)); } else { uint256 newBaseBought = baseBought.sub(baseAmount); quoteAmount = quoteSold.sub(getQuoteAmountLowBaseSide(p, k, baseInfo.R, newBaseBought)); } } else { uint256 baseSold = getBaseAmountLowQuoteSide(p, k, DecimalMath.ONE, quoteBought); uint256 newBaseSold = baseAmount.add(baseSold); uint256 newQuoteBought = getQuoteAmountLowQuoteSide(p, k, DecimalMath.ONE, newBaseSold); quoteAmount = newQuoteBought > quoteBought ? newQuoteBought.sub(quoteBought) : 0; } } function getBaseAmountSellQuote( address baseToken, uint256 quoteAmount, TokenInfo memory baseInfo, TokenInfo memory quoteInfo ) private view returns (uint256 baseAmount) { uint256 p; uint256 s; uint256 k; bool isFeasible; (p, s, k, isFeasible) = IWooracle(wooracle).state(baseToken); require(isFeasible, 'WooPP: ORACLE_PRICE_NOT_FEASIBLE'); wooGuardian.checkSwapPrice(p, baseToken, quoteToken); // price: p * (1 + s / 2) p = p.mulCeil(DecimalMath.ONE.add(s.divCeil(DecimalMath.TWO))); uint256 baseBought; uint256 quoteBought; (baseBought, quoteBought) = getBoughtAmount(baseInfo, quoteInfo, p, k, false); if (quoteBought > 0) { uint256 baseSold = getBaseAmountLowQuoteSide(p, k, baseInfo.R, quoteBought); if (quoteAmount > quoteBought) { uint256 newQuoteSold = quoteAmount.sub(quoteBought); baseAmount = baseSold.add(getBaseAmountLowBaseSide(p, k, DecimalMath.ONE, newQuoteSold)); } else { uint256 newQuoteBought = quoteBought.sub(quoteAmount); baseAmount = baseSold.sub(getBaseAmountLowQuoteSide(p, k, baseInfo.R, newQuoteBought)); } } else { uint256 quoteSold = getQuoteAmountLowBaseSide(p, k, DecimalMath.ONE, baseBought); uint256 newQuoteSold = quoteAmount.add(quoteSold); uint256 newBaseBought = getBaseAmountLowBaseSide(p, k, DecimalMath.ONE, newQuoteSold); baseAmount = newBaseBought > baseBought ? newBaseBought.sub(baseBought) : 0; } } function max(uint112 a, uint112 b) private pure returns (uint112) { return a >= b ? a : b; } }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /// @title Contract to collect transaction fee of Woo private pool. interface IWooFeeManager { /* ----- Events ----- */ event FeeRateUpdated(address indexed token, uint256 newFeeRate); event Withdraw(address indexed token, address indexed to, uint256 amount); /* ----- External Functions ----- */ /// @dev fee rate for the given base token: /// NOTE: fee rate decimal 18: 1e16 = 1%, 1e15 = 0.1%, 1e14 = 0.01% /// @param token the base token /// @return the fee rate function feeRate(address token) external view returns (uint256); /// @dev Sets the fee rate for the given token /// @param token the base token /// @param newFeeRate the new fee rate function setFeeRate(address token, uint256 newFeeRate) external; /// @dev Collects the swap fee to the given brokder address. /// @param amount the swap fee amount /// @param brokerAddr the broker address to rebate to function collectFee(uint256 amount, address brokerAddr) external; /// @dev get the quote token address /// @return address of quote token function quoteToken() external view returns (address); /// @dev Collects the fee and distribute to rebate and vault managers. function distributeFees() external; }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import './libraries/InitializableOwnable.sol'; import './interfaces/IWooracle.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; /// @title Wooracle implementation in BSC /// @notice Will be maintained and updated periodically by Woo.network in multichains. contract Wooracle is InitializableOwnable, IWooracle { /* ----- State variables ----- */ // 128 + 64 + 64 = 256 bits (slot size) struct TokenInfo { uint128 price; // 18 - base_decimal + quote_decimal uint64 coeff; // 18. coeff <= 1e18 (2^64 = 1.84e19) uint64 spread; // 18. spread <= 2e18 (2^64 = 1.84e19) } mapping(address => TokenInfo) public infos; address public override quoteToken; uint256 public override timestamp; uint256 public staleDuration; constructor() public { initOwner(msg.sender); staleDuration = uint256(300); } /* ----- External Functions ----- */ /// @dev Set the quote token address. /// @param newQuoteToken token address function setQuoteToken(address newQuoteToken) external onlyOwner { quoteToken = newQuoteToken; } /// @dev Set the staleDuration. /// @param newStaleDuration the new stale duration function setStaleDuration(uint256 newStaleDuration) external onlyOwner { staleDuration = newStaleDuration; } /// @dev Update the base token prices. /// @param base the baseToken address /// @param newPrice the new prices for the base token function postPrice(address base, uint128 newPrice) external onlyOwner { infos[base].price = newPrice; timestamp = block.timestamp; } /// @dev batch update baseTokens prices /// @param bases list of baseToken address /// @param newPrices the updated prices list function postPriceList(address[] calldata bases, uint128[] calldata newPrices) external onlyOwner { uint256 length = bases.length; require(length == newPrices.length, 'Wooracle: length_INVALID'); for (uint256 i = 0; i < length; i++) { infos[bases[i]].price = newPrices[i]; } timestamp = block.timestamp; } /// @dev update the spreads info. /// @param base baseToken address /// @param newSpread the new spreads function postSpread(address base, uint64 newSpread) external onlyOwner { infos[base].spread = newSpread; timestamp = block.timestamp; } /// @dev batch update the spreads info. /// @param bases list of baseToken address /// @param newSpreads list of spreads info function postSpreadList(address[] calldata bases, uint64[] calldata newSpreads) external onlyOwner { uint256 length = bases.length; require(length == newSpreads.length, 'Wooracle: length_INVALID'); for (uint256 i = 0; i < length; i++) { infos[bases[i]].spread = newSpreads[i]; } timestamp = block.timestamp; } /// @dev update the state of the given base token. /// @param base baseToken address /// @param newPrice the new prices /// @param newSpread the new spreads /// @param newCoeff the new slippage coefficent function postState( address base, uint128 newPrice, uint64 newSpread, uint64 newCoeff ) external onlyOwner { _setState(base, newPrice, newSpread, newCoeff); timestamp = block.timestamp; } /// @dev batch update the prices, spreads and slipagge coeffs info. /// @param bases list of baseToken address /// @param newPrices the prices list /// @param newSpreads the spreads list /// @param newCoeffs the slippage coefficent list function postStateList( address[] calldata bases, uint128[] calldata newPrices, uint64[] calldata newSpreads, uint64[] calldata newCoeffs ) external onlyOwner { uint256 length = bases.length; require( length == newPrices.length && length == newSpreads.length && length == newCoeffs.length, 'Wooracle: length_INVALID' ); for (uint256 i = 0; i < length; i++) { _setState(bases[i], newPrices[i], newSpreads[i], newCoeffs[i]); } timestamp = block.timestamp; } /// @inheritdoc IWooracle function price(address base) external view override returns (uint256 priceNow, bool feasible) { priceNow = uint256(infos[base].price); feasible = priceNow != 0 && block.timestamp <= (timestamp + staleDuration * 1 seconds); } function getPrice(address base) external view override returns (uint256) { return uint256(infos[base].price); } function getSpread(address base) external view override returns (uint256) { return uint256(infos[base].spread); } function getCoeff(address base) external view override returns (uint256) { return uint256(infos[base].coeff); } /// @inheritdoc IWooracle function state(address base) external view override returns ( uint256 priceNow, uint256 spreadNow, uint256 coeffNow, bool feasible ) { TokenInfo storage info = infos[base]; priceNow = uint256(info.price); spreadNow = uint256(info.spread); coeffNow = uint256(info.coeff); feasible = priceNow != 0 && block.timestamp <= (timestamp + staleDuration * 1 seconds); } function isFeasible(address base) public view override returns (bool) { return infos[base].price != 0 && block.timestamp <= (timestamp + staleDuration * 1 seconds); } /* ----- Private Functions ----- */ function _setState( address base, uint128 newPrice, uint64 newSpread, uint64 newCoeff ) private { TokenInfo storage info = infos[base]; info.price = newPrice; info.spread = newSpread; info.coeff = newCoeff; } }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import './libraries/InitializableOwnable.sol'; import './interfaces/IWooracle.sol'; /// @title Wooracle implementation in Avalanche chain. /// @notice Will be maintained and updated periodically by Woo.network in multichains. contract Wooracle is InitializableOwnable, IWooracle { /* ----- State variables ----- */ struct TokenInfo { uint256 price; // 18 - base_decimal + quote_decimal uint256 coeff; // 36 - quote uint256 spread; // 18 } mapping(address => TokenInfo) public infos; address public override quoteToken; uint256 public override timestamp; uint256 public staleDuration; constructor() public { initOwner(msg.sender); staleDuration = uint256(300); } /* ----- External Functions ----- */ /// @dev Set the quote token address. /// @param newQuoteToken token address function setQuoteToken(address newQuoteToken) external onlyOwner { quoteToken = newQuoteToken; } /// @dev Set the staleDuration. /// @param newStaleDuration the new stale duration function setStaleDuration(uint256 newStaleDuration) external onlyOwner { staleDuration = newStaleDuration; } /// @dev Update the base token prices. /// @param base the baseToken address /// @param newPrice the new prices for the base token function postPrice(address base, uint256 newPrice) external onlyOwner { infos[base].price = newPrice; timestamp = block.timestamp; } /// @dev batch update baseTokens prices /// @param bases list of baseToken address /// @param newPrices the updated prices list function postPriceList(address[] calldata bases, uint256[] calldata newPrices) external onlyOwner { uint256 length = bases.length; require(length == newPrices.length, 'Wooracle: length_INVALID'); for (uint256 i = 0; i < length; i++) { infos[bases[i]].price = newPrices[i]; } timestamp = block.timestamp; } /// @dev update the spreads info. /// @param base baseToken address /// @param newSpread the new spreads function postSpread(address base, uint256 newSpread) external onlyOwner { infos[base].spread = newSpread; timestamp = block.timestamp; } /// @dev batch update the spreads info. /// @param bases list of baseToken address /// @param newSpreads list of spreads info function postSpreadList(address[] calldata bases, uint256[] calldata newSpreads) external onlyOwner { uint256 length = bases.length; require(length == newSpreads.length, 'Wooracle: length_INVALID'); for (uint256 i = 0; i < length; i++) { infos[bases[i]].spread = newSpreads[i]; } timestamp = block.timestamp; } /// @dev update the state of the given base token. /// @param base baseToken address /// @param newPrice the new prices /// @param newSpread the new spreads /// @param newCoeff the new slippage coefficent function postState( address base, uint256 newPrice, uint256 newSpread, uint256 newCoeff ) external onlyOwner { _setState(base, newPrice, newSpread, newCoeff); timestamp = block.timestamp; } /// @dev batch update the prices, spreads and slipagge coeffs info. /// @param bases list of baseToken address /// @param newPrices the prices list /// @param newSpreads the spreads list /// @param newCoeffs the slippage coefficent list function postStateList( address[] calldata bases, uint256[] calldata newPrices, uint256[] calldata newSpreads, uint256[] calldata newCoeffs ) external onlyOwner { uint256 length = bases.length; require( length == newPrices.length && length == newSpreads.length && length == newCoeffs.length, 'Wooracle: length_INVALID' ); for (uint256 i = 0; i < length; i++) { _setState(bases[i], newPrices[i], newSpreads[i], newCoeffs[i]); } timestamp = block.timestamp; } /// @inheritdoc IWooracle function price(address base) external view override returns (uint256 priceNow, bool feasible) { priceNow = infos[base].price; feasible = priceNow != 0 && block.timestamp <= (timestamp + staleDuration * 1 seconds); } function getPrice(address base) external view override returns (uint256) { return infos[base].price; } function getSpread(address base) external view override returns (uint256) { return infos[base].spread; } function getCoeff(address base) external view override returns (uint256) { return infos[base].coeff; } /// @inheritdoc IWooracle function state(address base) external view override returns ( uint256 priceNow, uint256 spreadNow, uint256 coeffNow, bool feasible ) { TokenInfo storage info = infos[base]; priceNow = info.price; spreadNow = info.spread; coeffNow = info.coeff; feasible = priceNow != 0 && block.timestamp <= (timestamp + staleDuration * 1 seconds); } function isFeasible(address base) public view override returns (bool) { return infos[base].price != 0 && block.timestamp <= (timestamp + staleDuration * 1 seconds); } /* ----- Private Functions ----- */ function _setState( address base, uint256 newPrice, uint256 newSpread, uint256 newCoeff ) private { TokenInfo storage info = infos[base]; info.price = newPrice; info.spread = newSpread; info.coeff = newCoeff; } }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import './libraries/InitializableOwnable.sol'; import './libraries/DecimalMath.sol'; import './interfaces/IWooGuardian.sol'; import './interfaces/AggregatorV3Interface.sol'; import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; /// @title Woo guardian implementation. contract WooGuardian is IWooGuardian, InitializableOwnable { using SafeMath for uint256; using DecimalMath for uint256; /* ----- Type declarations ----- */ struct RefInfo { address chainlinkRefOracle; // chainlink oracle for price checking uint96 refPriceFixCoeff; // chainlink price fix coeff } /* ----- State variables ----- */ mapping(address => RefInfo) public refInfo; uint256 public priceBound; constructor(uint256 newPriceBound) public { initOwner(msg.sender); require(newPriceBound <= 1e18, 'WooGuardian: priceBound out of range'); priceBound = newPriceBound; } function checkSwapPrice( uint256 price, address fromToken, address toToken ) external view override { require(fromToken != address(0), 'WooGuardian: fromToken_ZERO_ADDR'); require(toToken != address(0), 'WooGuardian: toToken_ZERO_ADDR'); uint256 refPrice = _refPrice(fromToken, toToken); require( refPrice.mulFloor(1e18 - priceBound) <= price && price <= refPrice.mulCeil(1e18 + priceBound), 'WooGuardian: PRICE_UNRELIABLE' ); } function checkInputAmount(address token, uint256 inputAmount) external view override { // TODO: qinchao } function checkSwapAmount( address fromToken, address toToken, uint256 fromAmount, uint256 toAmount ) external view override { require(fromToken != address(0), 'WooGuardian: fromToken_ZERO_ADDR'); require(toToken != address(0), 'WooGuardian: toToken_ZERO_ADDR'); uint256 refPrice = _refPrice(fromToken, toToken); uint256 refToAmount = fromAmount.mulFloor(refPrice); require( refToAmount.mulFloor(1e18 - priceBound) <= toAmount && toAmount <= refToAmount.mulCeil(1e18 + priceBound), 'WooGuardian: TO_AMOUNT_UNRELIABLE' ); } function setToken(address token, address chainlinkRefOracle) external onlyOwner { require(token != address(0), 'WooGuardian: token_ZERO_ADDR'); RefInfo storage info = refInfo[token]; info.chainlinkRefOracle = chainlinkRefOracle; info.refPriceFixCoeff = _refPriceFixCoeff(token, chainlinkRefOracle); emit ChainlinkRefOracleUpdated(token, chainlinkRefOracle); } function _refPriceFixCoeff(address token, address chainlink) private view returns (uint96) { if (chainlink == address(0)) { return 0; } // About decimals: // For a sell base trade, we have quoteSize = baseSize * price // For calculation convenience, the decimals of price is 18-base.decimals+quote.decimals // If we have price = basePrice / quotePrice, then decimals of tokenPrice should be 36-token.decimals() // We use chainlink oracle price as token reference price, which decimals is chainlinkPrice.decimals() // We should multiply it by 10e(36-(token.decimals+chainlinkPrice.decimals)), which is refPriceFixCoeff uint256 decimalsToFix = uint256(ERC20(token).decimals()).add( uint256(AggregatorV3Interface(chainlink).decimals()) ); uint256 refPriceFixCoeff = 10**(uint256(36).sub(decimalsToFix)); require(refPriceFixCoeff <= type(uint96).max); return uint96(refPriceFixCoeff); } function _refPrice(address fromToken, address toToken) private view returns (uint256) { RefInfo storage baseInfo = refInfo[fromToken]; RefInfo storage quoteInfo = refInfo[toToken]; require(baseInfo.chainlinkRefOracle != address(0), 'WooGuardian: fromToken_RefOracle_INVALID'); require(quoteInfo.chainlinkRefOracle != address(0), 'WooGuardian: toToken_RefOracle_INVALID'); (, int256 rawBaseRefPrice, , , ) = AggregatorV3Interface(baseInfo.chainlinkRefOracle).latestRoundData(); require(rawBaseRefPrice >= 0, 'WooGuardian: INVALID_CHAINLINK_PRICE'); (, int256 rawQuoteRefPrice, , , ) = AggregatorV3Interface(quoteInfo.chainlinkRefOracle).latestRoundData(); require(rawQuoteRefPrice >= 0, 'WooGuardian: INVALID_CHAINLINK_QUOTE_PRICE'); uint256 baseRefPrice = uint256(rawBaseRefPrice).mul(uint256(baseInfo.refPriceFixCoeff)); uint256 quoteRefPrice = uint256(rawQuoteRefPrice).mul(uint256(quoteInfo.refPriceFixCoeff)); return baseRefPrice.divFloor(quoteRefPrice); } }
// SPDX-License-Identifier: MIT pragma solidity =0.6.12; pragma experimental ABIEncoderV2; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import './libraries/InitializableOwnable.sol'; import './libraries/DecimalMath.sol'; import './interfaces/IWooPP.sol'; import './interfaces/IWooRebateManager.sol'; import './interfaces/IWooFeeManager.sol'; import './interfaces/IWooVaultManager.sol'; import './interfaces/IWooAccessManager.sol'; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import '@openzeppelin/contracts/utils/Pausable.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; /// @title Contract to collect transaction fee of Woo private pool. contract WooFeeManager is InitializableOwnable, ReentrancyGuard, IWooFeeManager { /* ----- Type declarations ----- */ using SafeMath for uint256; using DecimalMath for uint256; using SafeERC20 for IERC20; /* ----- State variables ----- */ mapping(address => uint256) public override feeRate; // decimal: 18; 1e16 = 1%, 1e15 = 0.1%, 1e14 = 0.01% uint256 public vaultRewardRate; // decimal: 18; 1e16 = 1%, 1e15 = 0.1%, 1e14 = 0.01% uint256 public rebateAmount; address public immutable override quoteToken; IWooRebateManager public rebateManager; IWooVaultManager public vaultManager; IWooAccessManager public accessManager; address public treasury; /* ----- Modifiers ----- */ modifier onlyAdmin() { require(msg.sender == _OWNER_ || accessManager.isFeeAdmin(msg.sender), 'WooFeeManager: NOT_ADMIN'); _; } constructor( address newQuoteToken, address newRebateManager, address newVaultManager, address newAccessManager, address newTreasury ) public { require(newQuoteToken != address(0), 'WooFeeManager: quoteToken_ZERO_ADDR'); initOwner(msg.sender); quoteToken = newQuoteToken; rebateManager = IWooRebateManager(newRebateManager); vaultManager = IWooVaultManager(newVaultManager); vaultRewardRate = 1e18; accessManager = IWooAccessManager(newAccessManager); treasury = newTreasury; } /* ----- Public Functions ----- */ function collectFee(uint256 amount, address brokerAddr) external override nonReentrant { TransferHelper.safeTransferFrom(quoteToken, msg.sender, address(this), amount); uint256 rebateRate = rebateManager.rebateRate(brokerAddr); if (rebateRate > 0) { uint256 curRebateAmount = amount.mulFloor(rebateRate); rebateManager.addRebate(brokerAddr, curRebateAmount); rebateAmount = rebateAmount.add(curRebateAmount); } } /* ----- Admin Functions ----- */ function distributeFees() external override nonReentrant onlyAdmin { uint256 balance = IERC20(quoteToken).balanceOf(address(this)); require(balance > 0, 'WooFeeManager: balance_ZERO'); // Step 1: distribute the rebate balance if (rebateAmount > 0) { TransferHelper.safeApprove(quoteToken, address(rebateManager), rebateAmount); TransferHelper.safeTransfer(quoteToken, address(rebateManager), rebateAmount); balance = balance.sub(rebateAmount); rebateAmount = 0; } // Step 2: distribute the vault balance uint256 vaultAmount = balance.mulFloor(vaultRewardRate); if (vaultAmount > 0) { TransferHelper.safeApprove(quoteToken, address(vaultManager), vaultAmount); TransferHelper.safeTransfer(quoteToken, address(vaultManager), vaultAmount); balance = balance.sub(vaultAmount); } // Step 3: balance left for treasury if (balance > 0) { TransferHelper.safeApprove(quoteToken, treasury, balance); TransferHelper.safeTransfer(quoteToken, treasury, balance); } } function setFeeRate(address token, uint256 newFeeRate) external override onlyAdmin { require(newFeeRate <= 1e16, 'WooFeeManager: FEE_RATE>1%'); feeRate[token] = newFeeRate; emit FeeRateUpdated(token, newFeeRate); } function setRebateManager(address newRebateManager) external onlyAdmin { require(newRebateManager != address(0), 'WooFeeManager: rebateManager_ZERO_ADDR'); rebateManager = IWooRebateManager(newRebateManager); } function setVaultManager(address newVaultManager) external onlyAdmin { require(newVaultManager != address(0), 'WooFeeManager: newVaultManager_ZERO_ADDR'); vaultManager = IWooVaultManager(newVaultManager); } function setVaultRewardRate(uint256 newVaultRewardRate) external onlyAdmin { require(newVaultRewardRate <= 1e18, 'WooFeeManager: vaultRewardRate_INVALID'); vaultRewardRate = newVaultRewardRate; } function setAccessManager(address newAccessManager) external onlyOwner { require(newAccessManager != address(0), 'WooFeeManager: newAccessManager_ZERO_ADDR'); accessManager = IWooAccessManager(newAccessManager); } function setTreasury(address newTreasury) external onlyOwner { require(newTreasury != address(0), 'WooFeeManager: newTreasury_ZERO_ADDR'); treasury = newTreasury; } function emergencyWithdraw(address token, address to) external onlyOwner { require(token != address(0), 'WooFeeManager: token_ZERO_ADDR'); require(to != address(0), 'WooFeeManager: to_ZERO_ADDR'); uint256 amount = IERC20(token).balanceOf(address(this)); TransferHelper.safeTransfer(token, to, amount); emit Withdraw(token, to, amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/utils/Pausable.sol'; import './interfaces/IWooAccessManager.sol'; contract WooAccessManager is IWooAccessManager, Ownable, Pausable { /* ----- State variables ----- */ mapping(address => bool) public override isFeeAdmin; mapping(address => bool) public override isVaultAdmin; mapping(address => bool) public override isRebateAdmin; mapping(address => bool) public override isZeroFeeVault; /* ----- Admin Functions ----- */ /// @inheritdoc IWooAccessManager function setFeeAdmin(address feeAdmin, bool flag) external override onlyOwner whenNotPaused { require(feeAdmin != address(0), 'WooAccessManager: feeAdmin_ZERO_ADDR'); isFeeAdmin[feeAdmin] = flag; emit FeeAdminUpdated(feeAdmin, flag); } /// @inheritdoc IWooAccessManager function batchSetFeeAdmin(address[] calldata feeAdmins, bool[] calldata flags) external override onlyOwner whenNotPaused { require(feeAdmins.length == flags.length, 'WooAccessManager: length_INVALID'); for (uint256 i = 0; i < feeAdmins.length; i++) { require(feeAdmins[i] != address(0), 'WooAccessManager: feeAdmin_ZERO_ADDR'); isFeeAdmin[feeAdmins[i]] = flags[i]; emit FeeAdminUpdated(feeAdmins[i], flags[i]); } } /// @inheritdoc IWooAccessManager function setVaultAdmin(address vaultAdmin, bool flag) external override onlyOwner whenNotPaused { require(vaultAdmin != address(0), 'WooAccessManager: vaultAdmin_ZERO_ADDR'); isVaultAdmin[vaultAdmin] = flag; emit VaultAdminUpdated(vaultAdmin, flag); } /// @inheritdoc IWooAccessManager function batchSetVaultAdmin(address[] calldata vaultAdmins, bool[] calldata flags) external override onlyOwner whenNotPaused { require(vaultAdmins.length == flags.length, 'WooAccessManager: length_INVALID'); for (uint256 i = 0; i < vaultAdmins.length; i++) { require(vaultAdmins[i] != address(0), 'WooAccessManager: vaultAdmin_ZERO_ADDR'); isVaultAdmin[vaultAdmins[i]] = flags[i]; emit VaultAdminUpdated(vaultAdmins[i], flags[i]); } } /// @inheritdoc IWooAccessManager function setRebateAdmin(address rebateAdmin, bool flag) external override onlyOwner whenNotPaused { require(rebateAdmin != address(0), 'WooAccessManager: rebateAdmin_ZERO_ADDR'); isRebateAdmin[rebateAdmin] = flag; emit RebateAdminUpdated(rebateAdmin, flag); } /// @inheritdoc IWooAccessManager function batchSetRebateAdmin(address[] calldata rebateAdmins, bool[] calldata flags) external override onlyOwner whenNotPaused { require(rebateAdmins.length == flags.length, 'WooAccessManager: length_INVALID'); for (uint256 i = 0; i < rebateAdmins.length; i++) { require(rebateAdmins[i] != address(0), 'WooAccessManager: rebateAdmin_ZERO_ADDR'); isRebateAdmin[rebateAdmins[i]] = flags[i]; emit RebateAdminUpdated(rebateAdmins[i], flags[i]); } } /// @inheritdoc IWooAccessManager function setZeroFeeVault(address vault, bool flag) external override onlyOwner whenNotPaused { require(vault != address(0), 'WooAccessManager: vault_ZERO_ADDR'); isZeroFeeVault[vault] = flag; emit ZeroFeeVaultUpdated(vault, flag); } /// @inheritdoc IWooAccessManager function batchSetZeroFeeVault(address[] calldata vaults, bool[] calldata flags) external override onlyOwner whenNotPaused { require(vaults.length == flags.length, 'WooAccessManager: length_INVALID'); for (uint256 i = 0; i < vaults.length; i++) { require(vaults[i] != address(0), 'WooAccessManager: vault_ZERO_ADDR'); isZeroFeeVault[vaults[i]] = flags[i]; emit ZeroFeeVaultUpdated(vaults[i], flags[i]); } } /// @notice Pause the contract. function pause() external onlyOwner { super._pause(); } /// @notice Restart the contract. function unpause() external onlyOwner { super._unpause(); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.6.12; import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; contract TestToken is ERC20('TestToken', 'TT'), Ownable { using SafeMath for uint256; function mint(address _to, uint256 _amount) public onlyOwner { _mint(_to, _amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; import '../interfaces/IStrategy.sol'; import '../interfaces/IWETH.sol'; import '../interfaces/IWooAccessManager.sol'; import '../interfaces/IVault.sol'; /* ░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗ ░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║ ░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║ ░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║ ░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║ ░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝ * * MIT License * =========== * * Copyright (c) 2020 WooTrade * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ contract VaultErc20 is IVault, ERC20, Ownable, ReentrancyGuard { using SafeERC20 for IERC20; using SafeMath for uint256; struct StratCandidate { address implementation; uint256 proposedTime; } /* ----- State Variables ----- */ address public immutable override want; IWooAccessManager public immutable accessManager; IStrategy public strategy; StratCandidate public stratCandidate; mapping(address => uint256) public costSharePrice; event NewStratCandidate(address indexed implementation); event UpgradeStrat(address indexed implementation); constructor(address initWant, address initAccessManager) public ERC20( string(abi.encodePacked('WOOFi Earn ', ERC20(initWant).name())), string(abi.encodePacked('we', ERC20(initWant).symbol())) ) { require(initWant != address(0), 'Vault: initWant_ZERO_ADDR'); require(initAccessManager != address(0), 'Vault: initAccessManager_ZERO_ADDR'); want = initWant; accessManager = IWooAccessManager(initAccessManager); } modifier onlyAdmin() { require(owner() == _msgSender() || accessManager.isVaultAdmin(msg.sender), 'Vault: NOT_ADMIN'); _; } /* ----- External Functions ----- */ function deposit(uint256 amount) public payable override nonReentrant { require(amount > 0, 'Vault: amount_CAN_NOT_BE_ZERO'); // STEP 0: strategy's routing work before deposit. if (address(strategy) != address(0)) { require(!strategy.paused(), 'Vault: strat_paused'); strategy.beforeDeposit(); } // STEP 1: check the deposit amount uint256 balanceBefore = balance(); TransferHelper.safeTransferFrom(want, msg.sender, address(this), amount); uint256 balanceAfter = balance(); require(amount <= balanceAfter.sub(balanceBefore), 'Vault: amount_NOT_ENOUGH'); // STEP 2: issues the shares and update the cost basis uint256 shares = totalSupply() == 0 ? amount : amount.mul(totalSupply()).div(balanceBefore); uint256 sharesBefore = balanceOf(msg.sender); uint256 costBefore = costSharePrice[msg.sender]; uint256 costAfter = (sharesBefore.mul(costBefore).add(amount.mul(1e18))).div(sharesBefore.add(shares)); costSharePrice[msg.sender] = costAfter; _mint(msg.sender, shares); // STEP 3 earn(); } function withdraw(uint256 shares) public override nonReentrant { require(shares > 0, 'Vault: shares_ZERO'); require(shares <= balanceOf(msg.sender), 'Vault: shares_NOT_ENOUGH'); // STEP 0: burn the user's shares to start the withdrawal process. uint256 withdrawAmount = shares.mul(balance()).div(totalSupply()); _burn(msg.sender, shares); // STEP 1: withdraw the token from strategy if needed uint256 balanceBefore = IERC20(want).balanceOf(address(this)); if (balanceBefore < withdrawAmount) { uint256 balanceToWithdraw = withdrawAmount.sub(balanceBefore); require(_isStratActive(), 'Vault: STRAT_INACTIVE'); strategy.withdraw(balanceToWithdraw); uint256 balanceAfter = IERC20(want).balanceOf(address(this)); require(balanceAfter.sub(balanceBefore) > 0, 'Vault: Strat_WITHDRAW_ERROR'); if (withdrawAmount > balanceAfter) { // NOTE: Tiny diff is accepted due to the decimal precision. withdrawAmount = balanceAfter; } } // STEP 3 TransferHelper.safeTransfer(want, msg.sender, withdrawAmount); } function earn() public override { if (_isStratActive()) { uint256 balanceAvail = available(); if (balanceAvail > 0) { TransferHelper.safeTransfer(want, address(strategy), balanceAvail); strategy.deposit(); } } } function available() public view override returns (uint256) { return IERC20(want).balanceOf(address(this)); } function balance() public view override returns (uint256) { return address(strategy) != address(0) ? available().add(strategy.balanceOf()) : available(); } function getPricePerFullShare() public view override returns (uint256) { return totalSupply() == 0 ? 1e18 : balance().mul(1e18).div(totalSupply()); } function _isStratActive() internal view returns (bool) { return address(strategy) != address(0) && !strategy.paused(); } /* ----- Admin Functions ----- */ function setupStrat(address _strat) public onlyAdmin { require(_strat != address(0), 'Vault: STRAT_ZERO_ADDR'); require(address(strategy) == address(0), 'Vault: STRAT_ALREADY_SET'); require(address(this) == IStrategy(_strat).vault(), 'Vault: STRAT_VAULT_INVALID'); require(want == IStrategy(_strat).want(), 'Vault: STRAT_WANT_INVALID'); strategy = IStrategy(_strat); emit UpgradeStrat(_strat); } function proposeStrat(address _implementation) public onlyAdmin { require(address(this) == IStrategy(_implementation).vault(), 'Vault: STRAT_VAULT_INVALID'); require(want == IStrategy(_implementation).want(), 'Vault: STRAT_WANT_INVALID'); stratCandidate = StratCandidate({implementation: _implementation, proposedTime: block.timestamp}); emit NewStratCandidate(_implementation); } function upgradeStrat() public onlyAdmin { require(stratCandidate.implementation != address(0), 'Vault: NO_CANDIDATE'); require(stratCandidate.proposedTime.add(48 hours) < block.timestamp, 'Vault: TIME_INVALID'); emit UpgradeStrat(stratCandidate.implementation); strategy.retireStrat(); strategy = IStrategy(stratCandidate.implementation); stratCandidate.implementation = address(0); stratCandidate.proposedTime = 5000000000; // 100+ years to ensure proposedTime check earn(); } function inCaseTokensGetStuck(address stuckToken) external onlyAdmin { // NOTE: vault never allowed to access users' `want` token require(stuckToken != want, 'Vault: stuckToken_NOT_WANT'); require(stuckToken != address(0), 'Vault: stuckToken_ZERO_ADDR'); uint256 amount = IERC20(stuckToken).balanceOf(address(this)); if (amount > 0) { TransferHelper.safeTransfer(stuckToken, msg.sender, amount); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; import '../interfaces/IStrategy.sol'; import '../interfaces/IWETH.sol'; import '../interfaces/IWooAccessManager.sol'; import '../interfaces/IVault.sol'; contract VaultAvax is IVault, ERC20, Ownable, ReentrancyGuard { using SafeERC20 for IERC20; using SafeMath for uint256; struct StratCandidate { address implementation; uint256 proposedTime; } /* ----- State Variables ----- */ address public immutable override want; IWooAccessManager public immutable accessManager; IStrategy public strategy; StratCandidate public stratCandidate; mapping(address => uint256) public costSharePrice; event NewStratCandidate(address indexed implementation); event UpgradeStrat(address indexed implementation); /* ----- Constant Variables ----- */ // WAVAX: https://snowtrace.io/address/0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7 address public constant wrappedEther = 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7; constructor(address initWant, address initAccessManager) public ERC20( string(abi.encodePacked('WOOFi Earn ', ERC20(initWant).name())), string(abi.encodePacked('we', ERC20(initWant).symbol())) ) { require(initWant != address(0), 'Vault: initWant_ZERO_ADDR'); require(initAccessManager != address(0), 'Vault: initAccessManager_ZERO_ADDR'); want = initWant; accessManager = IWooAccessManager(initAccessManager); } modifier onlyAdmin() { require(owner() == _msgSender() || accessManager.isVaultAdmin(msg.sender), 'Vault: NOT_ADMIN'); _; } /* ----- External Functions ----- */ function deposit(uint256 amount) public payable override nonReentrant { require(amount > 0, 'Vault: amount_CAN_NOT_BE_ZERO'); if (want == wrappedEther) { require(msg.value == amount, 'Vault: msg.value_INSUFFICIENT'); } else { require(msg.value == 0, 'Vault: msg.value_INVALID'); } if (address(strategy) != address(0)) { require(!strategy.paused(), 'Vault: strat_paused'); strategy.beforeDeposit(); } uint256 balanceBefore = balance(); if (want == wrappedEther) { IWETH(wrappedEther).deposit{value: msg.value}(); } else { TransferHelper.safeTransferFrom(want, msg.sender, address(this), amount); } uint256 balanceAfter = balance(); require(amount <= balanceAfter.sub(balanceBefore), 'Vault: amount_NOT_ENOUGH'); uint256 shares = totalSupply() == 0 ? amount : amount.mul(totalSupply()).div(balanceBefore); uint256 sharesBefore = balanceOf(msg.sender); uint256 costBefore = costSharePrice[msg.sender]; uint256 costAfter = (sharesBefore.mul(costBefore).add(amount.mul(1e18))).div(sharesBefore.add(shares)); costSharePrice[msg.sender] = costAfter; _mint(msg.sender, shares); earn(); } function withdraw(uint256 shares) public override nonReentrant { require(shares > 0, 'Vault: shares_ZERO'); require(shares <= balanceOf(msg.sender), 'Vault: shares_NOT_ENOUGH'); uint256 withdrawAmount = shares.mul(balance()).div(totalSupply()); _burn(msg.sender, shares); uint256 balanceBefore = IERC20(want).balanceOf(address(this)); if (balanceBefore < withdrawAmount) { uint256 balanceToWithdraw = withdrawAmount.sub(balanceBefore); require(_isStratActive(), 'Vault: STRAT_INACTIVE'); strategy.withdraw(balanceToWithdraw); uint256 balanceAfter = IERC20(want).balanceOf(address(this)); if (withdrawAmount > balanceAfter) { // NOTE: in case a small amount not counted in, due to the decimal precision. withdrawAmount = balanceAfter; } } if (want == wrappedEther) { IWETH(wrappedEther).withdraw(withdrawAmount); TransferHelper.safeTransferETH(msg.sender, withdrawAmount); } else { TransferHelper.safeTransfer(want, msg.sender, withdrawAmount); } } function earn() public override { if (_isStratActive()) { uint256 balanceAvail = available(); TransferHelper.safeTransfer(want, address(strategy), balanceAvail); strategy.deposit(); } } function available() public view override returns (uint256) { return IERC20(want).balanceOf(address(this)); } function balance() public view override returns (uint256) { return address(strategy) != address(0) ? available().add(strategy.balanceOf()) : available(); } function getPricePerFullShare() public view override returns (uint256) { return totalSupply() == 0 ? 1e18 : balance().mul(1e18).div(totalSupply()); } function _isStratActive() internal view returns (bool) { return address(strategy) != address(0) && !strategy.paused(); } /* ----- Admin Functions ----- */ function setupStrat(address _strat) public onlyAdmin { require(_strat != address(0), 'Vault: STRAT_ZERO_ADDR'); require(address(strategy) == address(0), 'Vault: STRAT_ALREADY_SET'); require(address(this) == IStrategy(_strat).vault(), 'Vault: STRAT_VAULT_INVALID'); require(want == IStrategy(_strat).want(), 'Vault: STRAT_WANT_INVALID'); strategy = IStrategy(_strat); emit UpgradeStrat(_strat); } function proposeStrat(address _implementation) public onlyAdmin { require(address(this) == IStrategy(_implementation).vault(), 'Vault: STRAT_VAULT_INVALID'); require(want == IStrategy(_implementation).want(), 'Vault: STRAT_WANT_INVALID'); stratCandidate = StratCandidate({implementation: _implementation, proposedTime: block.timestamp}); emit NewStratCandidate(_implementation); } function upgradeStrat() public onlyAdmin { require(stratCandidate.implementation != address(0), 'Vault: NO_CANDIDATE'); require(stratCandidate.proposedTime.add(48 hours) < block.timestamp, 'Vault: TIME_INVALID'); emit UpgradeStrat(stratCandidate.implementation); strategy.retireStrat(); strategy = IStrategy(stratCandidate.implementation); stratCandidate.implementation = address(0); stratCandidate.proposedTime = 5000000000; // 100+ years to ensure proposedTime check earn(); } function inCaseTokensGetStuck(address stuckToken) external onlyAdmin { require(stuckToken != address(0), 'Vault: stuckToken_ZERO_ADDR'); require(stuckToken != want, 'Vault: stuckToken_NOT_WANT'); uint256 amount = IERC20(stuckToken).balanceOf(address(this)); if (amount > 0) { TransferHelper.safeTransfer(stuckToken, msg.sender, amount); } } function inCaseNativeTokensGetStuck() external onlyAdmin { // NOTE: vault never needs native tokens to do the yield farming; // This native token balance indicates a user's incorrect transfer. if (address(this).balance > 0) { TransferHelper.safeTransferETH(msg.sender, address(this).balance); } } receive() external payable {} }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; import '../interfaces/IStrategy.sol'; import '../interfaces/IWETH.sol'; import '../interfaces/IWooAccessManager.sol'; import '../interfaces/IVault.sol'; contract Vault is IVault, ERC20, Ownable, ReentrancyGuard { using SafeERC20 for IERC20; using SafeMath for uint256; struct StratCandidate { address implementation; uint256 proposedTime; } /* ----- State Variables ----- */ address public immutable override want; IWooAccessManager public immutable accessManager; IStrategy public strategy; StratCandidate public stratCandidate; mapping(address => uint256) public costSharePrice; event NewStratCandidate(address indexed implementation); event UpgradeStrat(address indexed implementation); /* ----- Constant Variables ----- */ // WBNB: https://bscscan.com/token/0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c address public constant wrappedEther = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c; constructor(address initWant, address initAccessManager) public ERC20( string(abi.encodePacked('WOOFi Earn ', ERC20(initWant).name())), string(abi.encodePacked('we', ERC20(initWant).symbol())) ) { require(initWant != address(0), 'Vault: initWant_ZERO_ADDR'); require(initAccessManager != address(0), 'Vault: initAccessManager_ZERO_ADDR'); want = initWant; accessManager = IWooAccessManager(initAccessManager); } modifier onlyAdmin() { require(owner() == _msgSender() || accessManager.isVaultAdmin(msg.sender), 'Vault: NOT_ADMIN'); _; } /* ----- External Functions ----- */ function deposit(uint256 amount) public payable override nonReentrant { require(amount > 0, 'Vault: amount_CAN_NOT_BE_ZERO'); if (want == wrappedEther) { require(msg.value == amount, 'Vault: msg.value_INSUFFICIENT'); } else { require(msg.value == 0, 'Vault: msg.value_INVALID'); } if (address(strategy) != address(0)) { require(!strategy.paused(), 'Vault: strat_paused'); strategy.beforeDeposit(); } uint256 balanceBefore = balance(); if (want == wrappedEther) { IWETH(wrappedEther).deposit{value: msg.value}(); } else { TransferHelper.safeTransferFrom(want, msg.sender, address(this), amount); } uint256 balanceAfter = balance(); require(amount <= balanceAfter.sub(balanceBefore), 'Vault: amount_NOT_ENOUGH'); uint256 shares = totalSupply() == 0 ? amount : amount.mul(totalSupply()).div(balanceBefore); uint256 sharesBefore = balanceOf(msg.sender); uint256 costBefore = costSharePrice[msg.sender]; uint256 costAfter = (sharesBefore.mul(costBefore).add(amount.mul(1e18))).div(sharesBefore.add(shares)); costSharePrice[msg.sender] = costAfter; _mint(msg.sender, shares); earn(); } function withdraw(uint256 shares) public override nonReentrant { require(shares > 0, 'Vault: shares_ZERO'); require(shares <= balanceOf(msg.sender), 'Vault: shares_NOT_ENOUGH'); uint256 withdrawAmount = shares.mul(balance()).div(totalSupply()); _burn(msg.sender, shares); uint256 balanceBefore = IERC20(want).balanceOf(address(this)); if (balanceBefore < withdrawAmount) { uint256 balanceToWithdraw = withdrawAmount.sub(balanceBefore); require(_isStratActive(), 'Vault: STRAT_INACTIVE'); strategy.withdraw(balanceToWithdraw); uint256 balanceAfter = IERC20(want).balanceOf(address(this)); if (withdrawAmount > balanceAfter) { // NOTE: in case a small amount not counted in, due to the decimal precision. withdrawAmount = balanceAfter; } } if (want == wrappedEther) { IWETH(wrappedEther).withdraw(withdrawAmount); TransferHelper.safeTransferETH(msg.sender, withdrawAmount); } else { TransferHelper.safeTransfer(want, msg.sender, withdrawAmount); } } function earn() public override { if (_isStratActive()) { uint256 balanceAvail = available(); TransferHelper.safeTransfer(want, address(strategy), balanceAvail); strategy.deposit(); } } function available() public view override returns (uint256) { return IERC20(want).balanceOf(address(this)); } function balance() public view override returns (uint256) { return address(strategy) != address(0) ? available().add(strategy.balanceOf()) : available(); } function getPricePerFullShare() public view override returns (uint256) { return totalSupply() == 0 ? 1e18 : balance().mul(1e18).div(totalSupply()); } function _isStratActive() internal view returns (bool) { return address(strategy) != address(0) && !strategy.paused(); } /* ----- Admin Functions ----- */ function setupStrat(address _strat) public onlyAdmin { require(_strat != address(0), 'Vault: STRAT_ZERO_ADDR'); require(address(strategy) == address(0), 'Vault: STRAT_ALREADY_SET'); require(address(this) == IStrategy(_strat).vault(), 'Vault: STRAT_VAULT_INVALID'); require(want == IStrategy(_strat).want(), 'Vault: STRAT_WANT_INVALID'); strategy = IStrategy(_strat); emit UpgradeStrat(_strat); } function proposeStrat(address _implementation) public onlyAdmin { require(address(this) == IStrategy(_implementation).vault(), 'Vault: STRAT_VAULT_INVALID'); require(want == IStrategy(_implementation).want(), 'Vault: STRAT_WANT_INVALID'); stratCandidate = StratCandidate({implementation: _implementation, proposedTime: block.timestamp}); emit NewStratCandidate(_implementation); } function upgradeStrat() public onlyAdmin { require(stratCandidate.implementation != address(0), 'Vault: NO_CANDIDATE'); require(stratCandidate.proposedTime.add(48 hours) < block.timestamp, 'Vault: TIME_INVALID'); emit UpgradeStrat(stratCandidate.implementation); strategy.retireStrat(); strategy = IStrategy(stratCandidate.implementation); stratCandidate.implementation = address(0); stratCandidate.proposedTime = 5000000000; // 100+ years to ensure proposedTime check earn(); } function inCaseTokensGetStuck(address stuckToken) external onlyAdmin { require(stuckToken != want, 'Vault: stuckToken_NOT_WANT'); require(stuckToken != address(0), 'Vault: stuckToken_ZERO_ADDR'); uint256 amount = IERC20(stuckToken).balanceOf(address(this)); if (amount > 0) { TransferHelper.safeTransfer(stuckToken, msg.sender, amount); } } function inCaseNativeTokensGetStuck() external onlyAdmin { // NOTE: vault never needs native tokens to do the yield farming; // This native token balance indicates a user's incorrect transfer. if (address(this).balance > 0) { TransferHelper.safeTransferETH(msg.sender, address(this).balance); } } receive() external payable {} }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/utils/Pausable.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; import '../../../interfaces/PancakeSwap/IMasterChef.sol'; import '../../../interfaces/TraderJoe/IUniswapPair.sol'; import '../../../interfaces/TraderJoe/IUniswapRouter.sol'; import '../../../interfaces/IWooAccessManager.sol'; import '../../../interfaces/IStrategy.sol'; import '../BaseStrategy.sol'; contract StrategyTraderJoeLP is BaseStrategy { using SafeERC20 for IERC20; using SafeMath for uint256; /* ----- State Variables ----- */ uint256 public immutable pid; address[] public rewardToLP0Route; address[] public rewardToLP1Route; address public lpToken0; address public lpToken1; address public constant reward = address(0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd); address public constant uniRouter = address(0x60aE616a2155Ee3d9A68541Ba4544862310933d4); address public constant masterChef = address(0xd6a4F121CA35509aF06A0Be99093d08462f53052); constructor( address initVault, address initAccessManager, uint256 initPid, address[] memory initRewardToLP0Route, address[] memory initRewardToLP1Route ) public BaseStrategy(initVault, initAccessManager) { pid = initPid; rewardToLP0Route = initRewardToLP0Route; rewardToLP1Route = initRewardToLP1Route; if (initRewardToLP0Route.length == 0) { lpToken0 = reward; } else { require(initRewardToLP0Route[0] == reward); lpToken0 = initRewardToLP0Route[initRewardToLP0Route.length - 1]; } if (initRewardToLP1Route.length == 0) { lpToken1 = reward; } else { require(initRewardToLP1Route[0] == reward); lpToken1 = initRewardToLP1Route[initRewardToLP1Route.length - 1]; } require( IUniswapV2Pair(want).token0() == lpToken0 || IUniswapV2Pair(want).token0() == lpToken1, 'StrategyLP: LP_token0_INVALID' ); require( IUniswapV2Pair(want).token1() == lpToken0 || IUniswapV2Pair(want).token1() == lpToken1, 'StrategyLP: LP_token1_INVALID' ); (address lpToken, , , ) = IMasterChef(masterChef).poolInfo(initPid); require(lpToken == want, 'StrategyLP: wrong_initPid'); _giveAllowances(); } /* ----- External Functions ----- */ function withdraw(uint256 amount) external override nonReentrant { require(msg.sender == vault, 'StrategyLP: NOT_VAULT'); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance < amount) { IMasterChef(masterChef).withdraw(pid, amount.sub(wantBalance)); wantBalance = IERC20(want).balanceOf(address(this)); } // just in case the decimal precision for the very left staking amount uint256 withdrawAmount = amount < wantBalance ? amount : wantBalance; uint256 fee = chargeWithdrawalFee(withdrawAmount); if (withdrawAmount > fee) { TransferHelper.safeTransfer(want, vault, withdrawAmount.sub(fee)); } } function harvest() public override whenNotPaused { require(msg.sender == tx.origin || msg.sender == vault, 'StrategyLP: EOA_OR_VAULT'); IMasterChef(masterChef).deposit(pid, 0); uint256 rewardAmount = IERC20(reward).balanceOf(address(this)); if (rewardAmount > 0) { uint256 wantBefore = IERC20(want).balanceOf(address(this)); _addLiquidity(); uint256 wantAfter = IERC20(want).balanceOf(address(this)); uint256 perfAmount = wantAfter.sub(wantBefore); chargePerformanceFee(perfAmount); } deposit(); } function deposit() public override whenNotPaused nonReentrant { uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { IMasterChef(masterChef).deposit(pid, wantBalance); } } function balanceOfPool() public view override returns (uint256) { (uint256 amount, ) = IMasterChef(masterChef).userInfo(pid, address(this)); return amount; } /* ----- Private Functions ----- */ function _giveAllowances() internal override { TransferHelper.safeApprove(want, masterChef, 0); TransferHelper.safeApprove(want, masterChef, uint256(-1)); TransferHelper.safeApprove(reward, uniRouter, 0); TransferHelper.safeApprove(reward, uniRouter, uint256(-1)); TransferHelper.safeApprove(lpToken0, uniRouter, 0); TransferHelper.safeApprove(lpToken0, uniRouter, uint256(-1)); TransferHelper.safeApprove(lpToken1, uniRouter, 0); TransferHelper.safeApprove(lpToken1, uniRouter, uint256(-1)); } function _removeAllowances() internal override { TransferHelper.safeApprove(want, masterChef, 0); TransferHelper.safeApprove(reward, uniRouter, 0); TransferHelper.safeApprove(lpToken0, uniRouter, 0); TransferHelper.safeApprove(lpToken1, uniRouter, 0); } function _addLiquidity() private { uint256 rewardHalf = IERC20(reward).balanceOf(address(this)).div(2); if (lpToken0 != reward) { IUniswapRouter(uniRouter).swapExactTokensForTokens(rewardHalf, 0, rewardToLP0Route, address(this), now); } if (lpToken1 != reward) { IUniswapRouter(uniRouter).swapExactTokensForTokens(rewardHalf, 0, rewardToLP1Route, address(this), now); } uint256 lp0Balance = IERC20(lpToken0).balanceOf(address(this)); uint256 lp1Balance = IERC20(lpToken1).balanceOf(address(this)); IUniswapRouter(uniRouter).addLiquidity(lpToken0, lpToken1, lp0Balance, lp1Balance, 0, 0, address(this), now); } function retireStrat() external override { require(msg.sender == vault, '!vault'); IMasterChef(masterChef).emergencyWithdraw(pid); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { TransferHelper.safeTransfer(want, vault, wantBalance); } } function emergencyExit() external override onlyAdmin { IMasterChef(masterChef).emergencyWithdraw(pid); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { TransferHelper.safeTransfer(want, vault, wantBalance); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IUniswapV2Pair { function factory() external view returns (address); function token0() external view returns (address); function token1() external view returns (address); function burn(address to) external returns (uint256 amount0, uint256 amount1); function getReserves() external view returns ( uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast ); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IUniswapRouter { function factory() external pure returns (address); function WBNB() external pure returns (address); function addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns ( uint256 amountA, uint256 amountB, uint256 liquidity ); function addLiquidityBNB( address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountBNBMin, address to, uint256 deadline ) external payable returns ( uint256 amountToken, uint256 amountBNB, uint256 liquidity ); function removeLiquidity( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityBNB( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountBNBMin, address to, uint256 deadline ) external returns (uint256 amountToken, uint256 amountBNB); function removeLiquidityWithPermit( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityBNBWithPermit( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountBNBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountToken, uint256 amountBNB); function removeLiquidityBNBSupportingFeeOnTransferTokens( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountBNBMin, address to, uint256 deadline ) external returns (uint256 amountBNB); function removeLiquidityBNBWithPermitSupportingFeeOnTransferTokens( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountBNBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountBNB); function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapTokensForExactTokens( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external; function swapExactBNBForTokensSupportingFeeOnTransferTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable; function swapExactTokensForBNBSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external; function swapExactBNBForTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable returns (uint256[] memory amounts); function swapTokensForExactBNB( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapExactTokensForBNB( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapBNBForExactTokens( uint256 amountOut, address[] calldata path, address to, uint256 deadline ) external payable returns (uint256[] memory amounts); function quote( uint256 amountA, uint256 reserveA, uint256 reserveB ) external pure returns (uint256 amountB); function getAmountOut( uint256 amountIn, uint256 reserveIn, uint256 reserveOut ) external pure returns (uint256 amountOut); function getAmountIn( uint256 amountOut, uint256 reserveIn, uint256 reserveOut ) external pure returns (uint256 amountIn); function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/utils/Pausable.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; import '../../../interfaces/PancakeSwap/IMasterChef.sol'; import '../../../interfaces/TraderJoe/IUniswapPair.sol'; import '../../../interfaces/TraderJoe/IUniswapRouter.sol'; import '../../../interfaces/IWooAccessManager.sol'; import '../../../interfaces/IStrategy.sol'; import '../BaseStrategy.sol'; contract StrategyTraderJoeDualLP is BaseStrategy { using SafeERC20 for IERC20; using SafeMath for uint256; /* ----- State Variables ----- */ uint256 public immutable pid; address[] public rewardToLP0Route; address[] public rewardToLP1Route; address[] public secondRewardToLP0Route; address[] public secondRewardToLP1Route; address public lpToken0; address public lpToken1; address public constant reward = address(0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd); // JOE address public constant secondReward = address(0x8729438EB15e2C8B576fCc6AeCdA6A148776C0F5); // QI address public constant uniRouter = address(0x60aE616a2155Ee3d9A68541Ba4544862310933d4); address public constant masterChef = address(0x188bED1968b795d5c9022F6a0bb5931Ac4c18F00); constructor( address initVault, address initAccessManager, uint256 initPid, address[] memory initRewardToLP0Route, address[] memory initRewardToLP1Route, address[] memory initSecondRewardToLP0Route, address[] memory initSecondRewardToLP1Route ) public BaseStrategy(initVault, initAccessManager) { pid = initPid; rewardToLP0Route = initRewardToLP0Route; rewardToLP1Route = initRewardToLP1Route; secondRewardToLP0Route = initSecondRewardToLP0Route; secondRewardToLP1Route = initSecondRewardToLP1Route; if (initRewardToLP0Route.length == 0) { lpToken0 = reward; } else { require(initRewardToLP0Route[0] == reward); lpToken0 = initRewardToLP0Route[initRewardToLP0Route.length - 1]; } if (initRewardToLP1Route.length == 0) { lpToken1 = reward; } else { require(initRewardToLP1Route[0] == reward); lpToken1 = initRewardToLP1Route[initRewardToLP1Route.length - 1]; } require(initSecondRewardToLP0Route[0] == secondReward); require(initSecondRewardToLP1Route[0] == secondReward); require( IUniswapV2Pair(want).token0() == lpToken0 || IUniswapV2Pair(want).token0() == lpToken1, 'StrategyLP: LP_token0_INVALID' ); require( IUniswapV2Pair(want).token1() == lpToken0 || IUniswapV2Pair(want).token1() == lpToken1, 'StrategyLP: LP_token1_INVALID' ); (address lpToken, , , ) = IMasterChef(masterChef).poolInfo(initPid); require(lpToken == want, 'StrategyLP: wrong_initPid'); _giveAllowances(); } /* ----- External Functions ----- */ function withdraw(uint256 amount) external override nonReentrant { require(msg.sender == vault, 'StrategyLP: NOT_VAULT'); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance < amount) { IMasterChef(masterChef).withdraw(pid, amount.sub(wantBalance)); wantBalance = IERC20(want).balanceOf(address(this)); } // just in case the decimal precision for the very left staking amount uint256 withdrawAmount = amount < wantBalance ? amount : wantBalance; uint256 fee = chargeWithdrawalFee(withdrawAmount); if (withdrawAmount > fee) { TransferHelper.safeTransfer(want, vault, withdrawAmount.sub(fee)); } } function harvest() public override whenNotPaused { require(msg.sender == tx.origin || msg.sender == vault, 'StrategyLP: EOA_OR_VAULT'); IMasterChef(masterChef).deposit(pid, 0); uint256 rewardAmount = IERC20(reward).balanceOf(address(this)); uint256 secondRewardAmount = IERC20(secondReward).balanceOf(address(this)); if (rewardAmount > 0 || secondRewardAmount > 0) { uint256 wantBefore = IERC20(want).balanceOf(address(this)); _addLiquidity(); uint256 wantAfter = IERC20(want).balanceOf(address(this)); uint256 perfAmount = wantAfter.sub(wantBefore); chargePerformanceFee(perfAmount); } deposit(); } function deposit() public override whenNotPaused nonReentrant { uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { IMasterChef(masterChef).deposit(pid, wantBalance); } } function balanceOfPool() public view override returns (uint256) { (uint256 amount, ) = IMasterChef(masterChef).userInfo(pid, address(this)); return amount; } /* ----- Private Functions ----- */ function _giveAllowances() internal override { TransferHelper.safeApprove(want, masterChef, 0); TransferHelper.safeApprove(want, masterChef, uint256(-1)); TransferHelper.safeApprove(reward, uniRouter, 0); TransferHelper.safeApprove(reward, uniRouter, uint256(-1)); TransferHelper.safeApprove(secondReward, uniRouter, 0); TransferHelper.safeApprove(secondReward, uniRouter, uint256(-1)); TransferHelper.safeApprove(lpToken0, uniRouter, 0); TransferHelper.safeApprove(lpToken0, uniRouter, uint256(-1)); TransferHelper.safeApprove(lpToken1, uniRouter, 0); TransferHelper.safeApprove(lpToken1, uniRouter, uint256(-1)); } function _removeAllowances() internal override { TransferHelper.safeApprove(want, masterChef, 0); TransferHelper.safeApprove(reward, uniRouter, 0); TransferHelper.safeApprove(secondReward, uniRouter, 0); TransferHelper.safeApprove(lpToken0, uniRouter, 0); TransferHelper.safeApprove(lpToken1, uniRouter, 0); } function _addLiquidity() private { uint256 rewardHalf = IERC20(reward).balanceOf(address(this)).div(2); uint256 secondRewardHalf = IERC20(secondReward).balanceOf(address(this)).div(2); if (lpToken0 != reward) { IUniswapRouter(uniRouter).swapExactTokensForTokens(rewardHalf, 0, rewardToLP0Route, address(this), now); } if (lpToken1 != reward) { IUniswapRouter(uniRouter).swapExactTokensForTokens(rewardHalf, 0, rewardToLP1Route, address(this), now); } if (lpToken0 != secondReward) { IUniswapRouter(uniRouter).swapExactTokensForTokens( secondRewardHalf, 0, secondRewardToLP0Route, address(this), now ); } if (lpToken1 != secondReward) { IUniswapRouter(uniRouter).swapExactTokensForTokens( secondRewardHalf, 0, secondRewardToLP1Route, address(this), now ); } uint256 lp0Balance = IERC20(lpToken0).balanceOf(address(this)); uint256 lp1Balance = IERC20(lpToken1).balanceOf(address(this)); IUniswapRouter(uniRouter).addLiquidity(lpToken0, lpToken1, lp0Balance, lp1Balance, 0, 0, address(this), now); } function retireStrat() external override { require(msg.sender == vault, '!vault'); IMasterChef(masterChef).emergencyWithdraw(pid); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { TransferHelper.safeTransfer(want, vault, wantBalance); } } function emergencyExit() external override onlyAdmin { IMasterChef(masterChef).emergencyWithdraw(pid); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { TransferHelper.safeTransfer(want, vault, wantBalance); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/utils/Pausable.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; import '../../../interfaces/PancakeSwap/IMasterChef.sol'; import '../../../interfaces/PancakeSwap/IPancakePair.sol'; import '../../../interfaces/PancakeSwap/IPancakeRouter.sol'; import '../../../interfaces/IWooAccessManager.sol'; import '../../../interfaces/IStrategy.sol'; import '../BaseStrategy.sol'; contract StrategyLP is BaseStrategy { using SafeERC20 for IERC20; using SafeMath for uint256; /* ----- State Variables ----- */ uint256 public immutable pid; address[] public rewardToLP0Route; address[] public rewardToLP1Route; address public lpToken0; address public lpToken1; address public constant reward = address(0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82); address public constant uniRouter = address(0x10ED43C718714eb63d5aA57B78B54704E256024E); address public constant masterChef = address(0x73feaa1eE314F8c655E354234017bE2193C9E24E); constructor( address initVault, address initAccessManager, uint256 initPid, address[] memory initRewardToLP0Route, address[] memory initRewardToLP1Route ) public BaseStrategy(initVault, initAccessManager) { pid = initPid; rewardToLP0Route = initRewardToLP0Route; rewardToLP1Route = initRewardToLP1Route; if (initRewardToLP0Route.length == 0) { lpToken0 = reward; } else { require(initRewardToLP0Route[0] == reward); lpToken0 = initRewardToLP0Route[initRewardToLP0Route.length - 1]; } if (initRewardToLP1Route.length == 0) { lpToken1 = reward; } else { require(initRewardToLP1Route[0] == reward); lpToken1 = initRewardToLP1Route[initRewardToLP1Route.length - 1]; } require( IPancakePair(want).token0() == lpToken0 || IPancakePair(want).token0() == lpToken1, 'StrategyLP: LP_token0_INVALID' ); require( IPancakePair(want).token1() == lpToken0 || IPancakePair(want).token1() == lpToken1, 'StrategyLP: LP_token1_INVALID' ); (address lpToken, , , ) = IMasterChef(masterChef).poolInfo(initPid); require(lpToken == want, 'StrategyLP: wrong_initPid'); _giveAllowances(); } /* ----- External Functions ----- */ function withdraw(uint256 amount) external override nonReentrant { require(msg.sender == vault, 'StrategyLP: NOT_VAULT'); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance < amount) { IMasterChef(masterChef).withdraw(pid, amount.sub(wantBalance)); wantBalance = IERC20(want).balanceOf(address(this)); } // just in case the decimal precision for the very left staking amount uint256 withdrawAmount = amount < wantBalance ? amount : wantBalance; uint256 fee = chargeWithdrawalFee(withdrawAmount); if (withdrawAmount > fee) { TransferHelper.safeTransfer(want, vault, withdrawAmount.sub(fee)); } } function harvest() public override whenNotPaused { require(msg.sender == tx.origin || msg.sender == vault, 'StrategyLP: EOA_OR_VAULT'); IMasterChef(masterChef).deposit(pid, 0); uint256 rewardAmount = IERC20(reward).balanceOf(address(this)); if (rewardAmount > 0) { uint256 wantBefore = IERC20(want).balanceOf(address(this)); _addLiquidity(); uint256 wantAfter = IERC20(want).balanceOf(address(this)); uint256 perfAmount = wantAfter.sub(wantBefore); chargePerformanceFee(perfAmount); } deposit(); } function deposit() public override whenNotPaused nonReentrant { uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { IMasterChef(masterChef).deposit(pid, wantBalance); } } function balanceOfPool() public view override returns (uint256) { (uint256 amount, ) = IMasterChef(masterChef).userInfo(pid, address(this)); return amount; } /* ----- Private Functions ----- */ function _giveAllowances() internal override { TransferHelper.safeApprove(want, masterChef, 0); TransferHelper.safeApprove(want, masterChef, uint256(-1)); TransferHelper.safeApprove(reward, uniRouter, 0); TransferHelper.safeApprove(reward, uniRouter, uint256(-1)); TransferHelper.safeApprove(lpToken0, uniRouter, 0); TransferHelper.safeApprove(lpToken0, uniRouter, uint256(-1)); TransferHelper.safeApprove(lpToken1, uniRouter, 0); TransferHelper.safeApprove(lpToken1, uniRouter, uint256(-1)); } function _removeAllowances() internal override { TransferHelper.safeApprove(want, masterChef, 0); TransferHelper.safeApprove(reward, uniRouter, 0); TransferHelper.safeApprove(lpToken0, uniRouter, 0); TransferHelper.safeApprove(lpToken1, uniRouter, 0); } function _addLiquidity() private { uint256 rewardHalf = IERC20(reward).balanceOf(address(this)).div(2); if (lpToken0 != reward) { IPancakeRouter(uniRouter).swapExactTokensForTokens(rewardHalf, 0, rewardToLP0Route, address(this), now); } if (lpToken1 != reward) { IPancakeRouter(uniRouter).swapExactTokensForTokens(rewardHalf, 0, rewardToLP1Route, address(this), now); } uint256 lp0Balance = IERC20(lpToken0).balanceOf(address(this)); uint256 lp1Balance = IERC20(lpToken1).balanceOf(address(this)); IPancakeRouter(uniRouter).addLiquidity(lpToken0, lpToken1, lp0Balance, lp1Balance, 0, 0, address(this), now); } function retireStrat() external override { require(msg.sender == vault, '!vault'); IMasterChef(masterChef).emergencyWithdraw(pid); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { TransferHelper.safeTransfer(want, vault, wantBalance); } } function emergencyExit() external override onlyAdmin { IMasterChef(masterChef).emergencyWithdraw(pid); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { TransferHelper.safeTransfer(want, vault, wantBalance); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IPancakePair { event Approval(address indexed owner, address indexed spender, uint256 value); event Transfer(address indexed from, address indexed to, uint256 value); function name() external pure returns (string memory); function symbol() external pure returns (string memory); function decimals() external pure returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address owner) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 value) external returns (bool); function transfer(address to, uint256 value) external returns (bool); function transferFrom( address from, address to, uint256 value ) external returns (bool); function DOMAIN_SEPARATOR() external view returns (bytes32); function PERMIT_TYPEHASH() external pure returns (bytes32); function nonces(address owner) external view returns (uint256); function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; event Mint(address indexed sender, uint256 amount0, uint256 amount1); event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to); event Swap( address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to ); event Sync(uint112 reserve0, uint112 reserve1); function MINIMUM_LIQUIDITY() external pure returns (uint256); function factory() external view returns (address); function token0() external view returns (address); function token1() external view returns (address); function getReserves() external view returns ( uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast ); function price0CumulativeLast() external view returns (uint256); function price1CumulativeLast() external view returns (uint256); function kLast() external view returns (uint256); function mint(address to) external returns (uint256 liquidity); function burn(address to) external returns (uint256 amount0, uint256 amount1); function swap( uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data ) external; function skim(address to) external; function sync() external; function initialize(address, address) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/utils/Pausable.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; import '../../../interfaces/BankerJoe/IJoeRouter.sol'; import '../../../interfaces/BankerJoe/IVToken.sol'; import '../../../interfaces/BankerJoe/IComptroller.sol'; import '../../../interfaces/IWooAccessManager.sol'; import '../../../interfaces/IWETH.sol'; import '../BaseStrategy.sol'; contract StrategyBankerJoe is BaseStrategy { using SafeERC20 for IERC20; using SafeMath for uint256; /* ----- State Variables ----- */ address public iToken; address[] public rewardToWantRoute; uint256 public lastHarvest; uint256 public supplyBal; /* ----- Constant Variables ----- */ address public constant wrappedEther = address(0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7); // WAVAX address public constant reward = address(0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd); // JOE address public constant uniRouter = address(0x60aE616a2155Ee3d9A68541Ba4544862310933d4); // JoeRouter address public constant comptroller = address(0xdc13687554205E5b89Ac783db14bb5bba4A1eDaC); /* ----- Events ----- */ event StratHarvest(address indexed harvester, uint256 wantHarvested, uint256 tvl); event Deposit(uint256 tvl); event Withdraw(uint256 tvl); constructor( address initVault, address initAccessManager, address initIToken, address[] memory initRewardToWantRoute ) public BaseStrategy(initVault, initAccessManager) { iToken = initIToken; rewardToWantRoute = initRewardToWantRoute; _giveAllowances(); } /* ----- External Functions ----- */ function beforeDeposit() public override { super.beforeDeposit(); updateSupplyBal(); } function rewardToWant() external view returns (address[] memory) { return rewardToWantRoute; } /* ----- Public Functions ----- */ function harvest() public override whenNotPaused { require(msg.sender == tx.origin || msg.sender == address(vault), 'StrategyBankerJoeNative: EOA_or_vault'); // When pendingImplementation not zero address, means there is a new implement ready to replace. if (IComptroller(comptroller).pendingImplementation() == address(0)) { uint256 beforeBal = balanceOfWant(); IComptroller(comptroller).claimReward(0, address(this)); IComptroller(comptroller).claimReward(1, address(this)); uint256 toWrap = address(this).balance; if (toWrap > 0) { IWETH(wrappedEther).deposit{value: toWrap}(); } uint256 rewardBal = IERC20(reward).balanceOf(address(this)); if (rewardBal > 0) { IJoeRouter(uniRouter).swapExactTokensForTokens(rewardBal, 0, rewardToWantRoute, address(this), now); uint256 wantHarvested = balanceOfWant().sub(beforeBal); uint256 fee = chargePerformanceFee(wantHarvested); deposit(); lastHarvest = block.timestamp; emit StratHarvest(msg.sender, wantHarvested.sub(fee), balanceOf()); } } else { _withdrawAll(); pause(); } } function deposit() public override whenNotPaused nonReentrant { uint256 wantBal = balanceOfWant(); if (wantBal > 0) { IVToken(iToken).mint(wantBal); updateSupplyBal(); emit Deposit(balanceOf()); } } function withdraw(uint256 amount) public override nonReentrant { require(msg.sender == vault, 'StrategyBankerJoeNative: !vault'); require(amount > 0, 'StrategyBankerJoeNative: !amount'); uint256 wantBal = balanceOfWant(); if (wantBal < amount) { IVToken(iToken).redeemUnderlying(amount.sub(wantBal)); updateSupplyBal(); uint256 newWantBal = IERC20(want).balanceOf(address(this)); require(newWantBal > wantBal, 'StrategyBankerJoeNative: !newWantBal'); wantBal = newWantBal; } uint256 withdrawAmt = amount < wantBal ? amount : wantBal; uint256 fee = chargeWithdrawalFee(withdrawAmt); if (withdrawAmt > fee) { TransferHelper.safeTransfer(want, vault, withdrawAmt.sub(fee)); } emit Withdraw(balanceOf()); } function updateSupplyBal() public { supplyBal = IVToken(iToken).balanceOfUnderlying(address(this)); } function balanceOfPool() public view override returns (uint256) { return supplyBal; } /* ----- Internal Functions ----- */ function _giveAllowances() internal override { TransferHelper.safeApprove(want, iToken, 0); TransferHelper.safeApprove(want, iToken, uint256(-1)); TransferHelper.safeApprove(reward, uniRouter, 0); TransferHelper.safeApprove(reward, uniRouter, uint256(-1)); TransferHelper.safeApprove(wrappedEther, uniRouter, 0); TransferHelper.safeApprove(wrappedEther, uniRouter, uint256(-1)); } function _removeAllowances() internal override { TransferHelper.safeApprove(want, iToken, 0); TransferHelper.safeApprove(reward, uniRouter, 0); TransferHelper.safeApprove(wrappedEther, uniRouter, 0); } function _withdrawAll() internal { uint256 iTokenBal = IERC20(iToken).balanceOf(address(this)); if (iTokenBal > 0) { IVToken(iToken).redeem(iTokenBal); } updateSupplyBal(); } /* ----- Admin Functions ----- */ function retireStrat() external override { require(msg.sender == vault, 'StrategyBankerJoeNative: !vault'); _withdrawAll(); uint256 wantBal = IERC20(want).balanceOf(address(this)); if (wantBal > 0) { TransferHelper.safeTransfer(want, vault, wantBal); } } function emergencyExit() external override onlyAdmin { _withdrawAll(); uint256 wantBal = IERC20(want).balanceOf(address(this)); if (wantBal > 0) { TransferHelper.safeTransfer(want, vault, wantBal); } } receive() external payable {} }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IJoeRouter { function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns ( uint256 amountA, uint256 amountB, uint256 liquidity ); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; interface IVToken is IERC20 { function underlying() external returns (address); function mint(uint256 mintAmount) external returns (uint256); function redeem(uint256 redeemTokens) external returns (uint256); function redeemUnderlying(uint256 redeemAmount) external returns (uint256); function borrow(uint256 borrowAmount) external returns (uint256); function repayBorrow(uint256 repayAmount) external returns (uint256); function balanceOfUnderlying(address owner) external returns (uint256); function borrowBalanceCurrent(address account) external returns (uint256); function comptroller() external returns (address); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IComptroller { function claimComp(address holder, address[] calldata _iTokens) external; function claimComp(address holder) external; function compAccrued(address holder) external view returns (uint256 comp); function enterMarkets(address[] memory _iTokens) external; function pendingComptrollerImplementation() external view returns (address implementation); function pendingImplementation() external view returns (address implementation); function claimReward(uint8 rewardType, address holder) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import '@openzeppelin/contracts/math/SafeMath.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/utils/Pausable.sol'; import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; import '../BaseStrategy.sol'; contract StrategyCake is BaseStrategy { using SafeERC20 for IERC20; using SafeMath for uint256; /* ----- Constant Variables ----- */ address public constant masterChef = address(0x73feaa1eE314F8c655E354234017bE2193C9E24E); constructor(address initVault, address initAccessManager) public BaseStrategy(initVault, initAccessManager) { _giveAllowances(); } /* ----- External Functions ----- */ function withdraw(uint256 amount) external override nonReentrant { require(msg.sender == address(vault), 'StrategyCake: NOT_VAULT'); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance < amount) { IMasterChef(masterChef).leaveStaking(amount.sub(wantBalance)); wantBalance = IERC20(want).balanceOf(address(this)); } // just in case the decimal precision for the very left staking amount uint256 withdrawAmount = amount < wantBalance ? amount : wantBalance; uint256 fee = chargeWithdrawalFee(withdrawAmount); if (withdrawAmount > fee) { TransferHelper.safeTransfer(want, vault, withdrawAmount.sub(fee)); } } function harvest() public override whenNotPaused { require(msg.sender == tx.origin || msg.sender == address(vault), 'StrategyCake: EOA_or_vault'); uint256 balanceBefore = IERC20(want).balanceOf(address(this)); IMasterChef(masterChef).leaveStaking(0); uint256 balanceAfter = IERC20(want).balanceOf(address(this)); uint256 perfAmount = balanceAfter.sub(balanceBefore); chargePerformanceFee(perfAmount); deposit(); } function deposit() public override whenNotPaused nonReentrant { uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { IMasterChef(masterChef).enterStaking(wantBalance); } } function balanceOfPool() public view override returns (uint256) { (uint256 amount, ) = IMasterChef(masterChef).userInfo(0, address(this)); return amount; } function _giveAllowances() internal override { TransferHelper.safeApprove(want, masterChef, 0); TransferHelper.safeApprove(want, masterChef, uint256(-1)); } function _removeAllowances() internal override { TransferHelper.safeApprove(want, masterChef, 0); } /* ----- Admin Functions ----- */ function retireStrat() external override { require(msg.sender == vault, '!vault'); IMasterChef(masterChef).emergencyWithdraw(0); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { TransferHelper.safeTransfer(want, vault, wantBalance); } } function emergencyExit() external override onlyAdmin { IMasterChef(masterChef).emergencyWithdraw(0); uint256 wantBalance = IERC20(want).balanceOf(address(this)); if (wantBalance > 0) { TransferHelper.safeTransfer(want, vault, wantBalance); } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity =0.6.12; import '../libraries/DecimalMath.sol'; contract DecimalMathTest { function mulFloor(uint256 target, uint256 d) external pure returns (uint256) { return DecimalMath.mulFloor(target, d); } function mulCeil(uint256 target, uint256 d) external pure returns (uint256) { return DecimalMath.mulCeil(target, d); } function divFloor(uint256 target, uint256 d) external pure returns (uint256) { return DecimalMath.divFloor(target, d); } function divCeil(uint256 target, uint256 d) external pure returns (uint256) { return DecimalMath.divCeil(target, d); } function reciprocalFloor(uint256 target) external pure returns (uint256) { return DecimalMath.reciprocalFloor(target); } function reciprocalCeil(uint256 target) external pure returns (uint256) { return DecimalMath.reciprocalCeil(target); } }
{ "evmVersion": "istanbul", "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"weth","type":"address"},{"internalType":"address","name":"newPool","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newPool","type":"address"}],"name":"WooPoolChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum IWooRouter.SwapType","name":"swapType","type":"uint8"},{"indexed":true,"internalType":"address","name":"fromToken","type":"address"},{"indexed":true,"internalType":"address","name":"toToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"rebateTo","type":"address"}],"name":"WooRouterSwap","type":"event"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"approveTarget","type":"address"},{"internalType":"address","name":"swapTarget","type":"address"},{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"minToAmount","type":"uint256"},{"internalType":"address payable","name":"to","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"externalSwap","outputs":[{"internalType":"uint256","name":"realToAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"approveTarget","type":"address"},{"internalType":"address","name":"swapTarget","type":"address"},{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"address payable","name":"to","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"externalSwap","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"baseToken","type":"address"},{"internalType":"uint256","name":"baseAmount","type":"uint256"}],"name":"querySellBase","outputs":[{"internalType":"uint256","name":"quoteAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"baseToken","type":"address"},{"internalType":"uint256","name":"quoteAmount","type":"uint256"}],"name":"querySellQuote","outputs":[{"internalType":"uint256","name":"baseAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"}],"name":"querySwap","outputs":[{"internalType":"uint256","name":"toAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quoteToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"baseToken","type":"address"},{"internalType":"uint256","name":"baseAmount","type":"uint256"},{"internalType":"uint256","name":"minQuoteAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"rebateTo","type":"address"}],"name":"sellBase","outputs":[{"internalType":"uint256","name":"realQuoteAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"baseToken","type":"address"},{"internalType":"uint256","name":"quoteAmount","type":"uint256"},{"internalType":"uint256","name":"minBaseAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"rebateTo","type":"address"}],"name":"sellQuote","outputs":[{"internalType":"uint256","name":"realBaseAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPool","type":"address"}],"name":"setPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bool","name":"whitelisted","type":"bool"}],"name":"setWhitelisted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"minToAmount","type":"uint256"},{"internalType":"address payable","name":"to","type":"address"},{"internalType":"address","name":"rebateTo","type":"address"}],"name":"swap","outputs":[{"internalType":"uint256","name":"realToAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wooPool","outputs":[{"internalType":"contract IWooPP","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60a06040523480156200001157600080fd5b5060405162002d9238038062002d928339810160408190526200003491620002d8565b600062000040620000e5565b600080546001600160a01b0319166001600160a01b0383169081178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350600180556001600160a01b038216620000c05760405162461bcd60e51b8152600401620000b790620003cd565b60405180910390fd5b6001600160601b0319606083901b16608052620000dd81620000e9565b505062000454565b3390565b600260015414156200010f5760405162461bcd60e51b8152600401620000b79062000396565b60026001556200011e620000e5565b6001600160a01b03166200013162000299565b6001600160a01b0316146200015a5760405162461bcd60e51b8152600401620000b79062000361565b6001600160a01b038116620001835760405162461bcd60e51b8152600401620000b7906200032a565b600280546001600160a01b0319166001600160a01b03838116919091179182905560408051630217a4b760e41b81529051929091169163217a4b7091600480820192602092909190829003018186803b158015620001e057600080fd5b505afa158015620001f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200021b9190620002a8565b600480546001600160a01b0319166001600160a01b03928316179081905516620002595760405162461bcd60e51b8152600401620000b79062000404565b7f4577a21bd8e55848c574b7582f8e6cc6a7cf1c1958f36a9751eab6329d656b1e816040516200028a919062000316565b60405180910390a15060018055565b6000546001600160a01b031690565b600060208284031215620002ba578081fd5b81516001600160a01b0381168114620002d1578182fd5b9392505050565b60008060408385031215620002eb578081fd5b8251620002f8816200043b565b60208401519092506200030b816200043b565b809150509250929050565b6001600160a01b0391909116815260200190565b6020808252601c908201527f576f6f526f757465723a206e6577506f6f6c5f414444525f5a45524f00000000604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526019908201527f576f6f526f757465723a20776574685f5a45524f5f4144445200000000000000604082015260600190565b6020808252601f908201527f576f6f526f757465723a2071756f7465546f6b656e5f414444525f5a45524f00604082015260600190565b6001600160a01b03811681146200045157600080fd5b50565b60805160601c6128f36200049f6000398061011d52806107cb5280610b6c5280610c8f5280610cbe5280610d0852806110a3528061113f528061118d5280611c1752506128f36000f3fe60806040526004361061010d5760003560e01c80637dc2038211610095578063ad5c464811610064578063ad5c4648146102ef578063e94803f414610304578063ea71ee8414610324578063f2fde38b14610337578063f3287c2f1461035757610160565b80637dc20382146102925780638da5cb5b146102a55780639281aa0b146102ba578063a7394603146102da57610160565b806366410a21116100dc57806366410a21146101fd5780636846fb501461021d578063715018a61461023d57806378e3214f1461025257806379a048761461027257610160565b8063199b83fa14610165578063217a4b701461018e5780633af32abf146101b05780634437152a146101dd57610160565b3661016057336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148061015857503360009081526003602052604090205460ff165b61015e57fe5b005b600080fd5b610178610173366004611efe565b610377565b6040516101859190612864565b60405180910390f35b34801561019a57600080fd5b506101a36105b9565b60405161018591906121a0565b3480156101bc57600080fd5b506101d06101cb366004611e1e565b6105c8565b6040516101859190612223565b3480156101e957600080fd5b5061015e6101f8366004611e1e565b6105dd565b34801561020957600080fd5b5061017861021836600461209a565b610777565b34801561022957600080fd5b506101786102383660046120c5565b610875565b34801561024957600080fd5b5061015e6109ef565b34801561025e57600080fd5b5061015e61026d36600461209a565b610a78565b34801561027e57600080fd5b5061017861028d36600461209a565b610b18565b6101786102a0366004611ff3565b610bbf565b3480156102b157600080fd5b506101a3610fc7565b3480156102c657600080fd5b5061015e6102d5366004612062565b610fd6565b3480156102e657600080fd5b506101a3611092565b3480156102fb57600080fd5b506101a36110a1565b34801561031057600080fd5b5061017861031f366004611fb3565b6110c5565b61015e610332366004611e56565b6113a6565b34801561034357600080fd5b5061015e610352366004611e1e565b6113c3565b34801561036357600080fd5b506101786103723660046120c5565b611483565b6000600260015414156103a55760405162461bcd60e51b815260040161039c906127f6565b60405180910390fd5b60026001556001600160a01b038a166103d05760405162461bcd60e51b815260040161039c90612566565b6001600160a01b0389166103f65760405162461bcd60e51b815260040161039c9061229f565b6001600160a01b03881661041c5760405162461bcd60e51b815260040161039c906127bf565b6001600160a01b0387166104425760405162461bcd60e51b815260040161039c906124a8565b6001600160a01b0384166104685760405162461bcd60e51b815260040161039c906125a8565b6001600160a01b038a1660009081526003602052604090205460ff166104a05760405162461bcd60e51b815260040161039c906124df565b6001600160a01b03891660009081526003602052604090205460ff166104d85760405162461bcd60e51b815260040161039c9061242f565b60006104e488306115f9565b90506104f48b8b8b8a88886116b0565b600061050089306115f9565b9050808211156105225760405162461bcd60e51b815260040161039c906126cd565b61052c818361182f565b92508683101561054e5760405162461bcd60e51b815260040161039c90612524565b610559898785611857565b856001600160a01b0316896001600160a01b03168b6001600160a01b031660008051602061289e83398151915260018c8833600060405161059e95949392919061222e565b60405180910390a45050600180559998505050505050505050565b6004546001600160a01b031681565b60036020526000908152604090205460ff1681565b600260015414156106005760405162461bcd60e51b815260040161039c906127f6565b600260015561060d6118a1565b6001600160a01b031661061e610fc7565b6001600160a01b0316146106445760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b03811661066a5760405162461bcd60e51b815260040161039c9061231c565b600280546001600160a01b0319166001600160a01b03838116919091179182905560408051630217a4b760e41b81529051929091169163217a4b7091600480820192602092909190829003018186803b1580156106c657600080fd5b505afa1580156106da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106fe9190611e3a565b600480546001600160a01b0319166001600160a01b039283161790819055166107395760405162461bcd60e51b815260040161039c9061282d565b7f4577a21bd8e55848c574b7582f8e6cc6a7cf1c1958f36a9751eab6329d656b1e8160405161076891906121a0565b60405180910390a15060018055565b60006001600160a01b03831661079f5760405162461bcd60e51b815260040161039c906125df565b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146107c957826107eb565b7f00000000000000000000000000000000000000000000000000000000000000005b6002546040516366410a2160e01b81529194506001600160a01b0316906366410a219061081e90869086906004016121d8565b60206040518083038186803b15801561083657600080fd5b505afa15801561084a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086e919061213f565b9392505050565b60006002600154141561089a5760405162461bcd60e51b815260040161039c906127f6565b60026001556001600160a01b0386166108c55760405162461bcd60e51b815260040161039c906125df565b6001600160a01b0383166108eb5760405162461bcd60e51b815260040161039c906125a8565b6108f7863330886118a5565b60025461090f9087906001600160a01b031687611996565b6002546040516306846fb560e41b81526001600160a01b0390911690636846fb509061094790899089908990899089906004016121f1565b602060405180830381600087803b15801561096157600080fd5b505af1158015610975573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610999919061213f565b6004546040519192506001600160a01b0380861692918116919089169060008051602061289e833981519152906109da906000908b90889033908b9061222e565b60405180910390a46001805595945050505050565b6109f76118a1565b6001600160a01b0316610a08610fc7565b6001600160a01b031614610a2e5760405162461bcd60e51b815260040161039c9061264d565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60026001541415610a9b5760405162461bcd60e51b815260040161039c906127f6565b6002600155610aa86118a1565b6001600160a01b0316610ab9610fc7565b6001600160a01b031614610adf5760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b038216610b055760405162461bcd60e51b815260040161039c90612268565b610b10823383611a84565b505060018055565b60006001600160a01b038316610b405760405162461bcd60e51b815260040161039c906125df565b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610b6a5782610b8c565b7f00000000000000000000000000000000000000000000000000000000000000005b600254604051633cd0243b60e11b81529194506001600160a01b0316906379a048769061081e90869086906004016121d8565b600060026001541415610be45760405162461bcd60e51b815260040161039c906127f6565b60026001556001600160a01b038716610c0f5760405162461bcd60e51b815260040161039c906127bf565b6001600160a01b038616610c355760405162461bcd60e51b815260040161039c906124a8565b6001600160a01b038316610c5b5760405162461bcd60e51b815260040161039c906125a8565b6001600160a01b0387811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9081149188161481610c8d5788610caf565b7f00000000000000000000000000000000000000000000000000000000000000005b985080610cbc5787610cde565b7f00000000000000000000000000000000000000000000000000000000000000005b97508115610d7f5734871115610d065760405162461bcd60e51b815260040161039c90612616565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015610d6157600080fd5b505af1158015610d75573d6000803e3d6000fd5b5050505050610d8b565b610d8b8933308a6118a5565b600254610da3908a906001600160a01b031689611996565b6004546001600160a01b038a811691161415610dce57610dc7818989898989611b6b565b9250610f2b565b6004546001600160a01b0389811691161415610e6e576002546040516306846fb560e41b81526001600160a01b0390911690636846fb5090610e1c908c908b908b908b908b906004016121f1565b602060405180830381600087803b158015610e3657600080fd5b505af1158015610e4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc7919061213f565b6002546040516306846fb560e41b81526000916001600160a01b031690636846fb5090610ea7908d908c90869030908c906004016121f1565b602060405180830381600087803b158015610ec157600080fd5b505af1158015610ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef9919061213f565b600454600254919250610f19916001600160a01b03918216911683611996565b610f27828a838a8a8a611b6b565b9350505b846001600160a01b031681610f405788610f56565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b031683610f6a578a610f80565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b031660008051602061289e83398151915260008b88338b604051610faf95949392919061222e565b60405180910390a45050600180559695505050505050565b6000546001600160a01b031690565b60026001541415610ff95760405162461bcd60e51b815260040161039c906127f6565b60026001556110066118a1565b6001600160a01b0316611017610fc7565b6001600160a01b03161461103d5760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b0382166110635760405162461bcd60e51b815260040161039c90612788565b6001600160a01b03919091166000908152600360205260409020805460ff191691151591909117905560018055565b6002546001600160a01b031681565b7f000000000000000000000000000000000000000000000000000000000000000081565b60006001600160a01b0384166110ed5760405162461bcd60e51b815260040161039c906127bf565b6001600160a01b0383166111135760405162461bcd60e51b815260040161039c906124a8565b6001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461113d578361115f565b7f00000000000000000000000000000000000000000000000000000000000000005b93506001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461118b57826111ad565b7f00000000000000000000000000000000000000000000000000000000000000005b6004549093506001600160a01b038581169116141561124f576002546040516366410a2160e01b81526001600160a01b03909116906366410a21906111f890869086906004016121d8565b60206040518083038186803b15801561121057600080fd5b505afa158015611224573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611248919061213f565b905061086e565b6004546001600160a01b038481169116141561129757600254604051633cd0243b60e11b81526001600160a01b03909116906379a04876906111f890879086906004016121d8565b600254604051633cd0243b60e11b81526000916001600160a01b0316906379a04876906112ca90889087906004016121d8565b60206040518083038186803b1580156112e257600080fd5b505afa1580156112f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061131a919061213f565b6002546040516366410a2160e01b81529192506001600160a01b0316906366410a219061134d90879085906004016121d8565b60206040518083038186803b15801561136557600080fd5b505afa158015611379573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061139d919061213f565b95945050505050565b6113b888888888886000898989610377565b505050505050505050565b6113cb6118a1565b6001600160a01b03166113dc610fc7565b6001600160a01b0316146114025760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b0381166114285760405162461bcd60e51b815260040161039c906122d6565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000600260015414156114a85760405162461bcd60e51b815260040161039c906127f6565b60026001556001600160a01b0386166114d35760405162461bcd60e51b815260040161039c906125df565b6001600160a01b0383166114f95760405162461bcd60e51b815260040161039c906125a8565b600454611511906001600160a01b03163330886118a5565b60045460025461152e916001600160a01b03908116911687611996565b60025460405163f3287c2f60e01b81526001600160a01b039091169063f3287c2f9061156690899089908990899089906004016121f1565b602060405180830381600087803b15801561158057600080fd5b505af1158015611594573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b8919061213f565b6004546040519192506001600160a01b0380861692898216929091169060008051602061289e833981519152906109da906000908b90889033908b9061222e565b60006001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146116a0576040516370a0823160e01b81526001600160a01b038416906370a082319061164b9085906004016121a0565b60206040518083038186803b15801561166357600080fd5b505afa158015611677573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061169b919061213f565b61086e565b506001600160a01b031631919050565b6001600160a01b03861660009081526003602052604090205460ff166116e85760405162461bcd60e51b815260040161039c906124df565b6001600160a01b03851660009081526003602052604090205460ff166117205760405162461bcd60e51b815260040161039c9061242f565b6001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461176057611750843330866118a5565b61175b848785611996565b611780565b348311156117805760405162461bcd60e51b815260040161039c90612616565b60006001600160a01b0380871690861673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146117b15760006117b3565b845b84846040516117c3929190612157565b60006040518083038185875af1925050503d8060008114611800576040519150601f19603f3d011682016040523d82523d6000602084013e611805565b606091505b50509050806118265760405162461bcd60e51b815260040161039c90612353565b50505050505050565b6000828211156118515760405162461bcd60e51b815260040161039c90612471565b50900390565b801561189c576001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14156118915761188c8282611d4a565b61189c565b61189c838383611a84565b505050565b3390565b60006060856001600160a01b03166323b872dd8686866040516024016118cd939291906121b4565b6040516020818303038152906040529060e01b6020820180516001600160e01b0383818316178352505050506040516119069190612167565b6000604051808303816000865af19150503d8060008114611943576040519150601f19603f3d011682016040523d82523d6000602084013e611948565b606091505b50915091508180156119725750805115806119725750808060200190518101906119729190612123565b61198e5760405162461bcd60e51b815260040161039c9061238a565b505050505050565b60006060846001600160a01b031663095ea7b385856040516024016119bc9291906121d8565b6040516020818303038152906040529060e01b6020820180516001600160e01b0383818316178352505050506040516119f59190612167565b6000604051808303816000865af19150503d8060008114611a32576040519150601f19603f3d011682016040523d82523d6000602084013e611a37565b606091505b5091509150818015611a61575080511580611a61575080806020019051810190611a619190612123565b611a7d5760405162461bcd60e51b815260040161039c90612682565b5050505050565b60006060846001600160a01b031663a9059cbb8585604051602401611aaa9291906121d8565b6040516020818303038152906040529060e01b6020820180516001600160e01b038381831617835250505050604051611ae39190612167565b6000604051808303816000865af19150503d8060008114611b20576040519150601f19603f3d011682016040523d82523d6000602084013e611b25565b606091505b5091509150818015611b4f575080511580611b4f575080806020019051810190611b4f9190612123565b611a7d5760405162461bcd60e51b815260040161039c9061273b565b60008615611cb35760025460405163f3287c2f60e01b81526001600160a01b039091169063f3287c2f90611bab90899089908990309089906004016121f1565b602060405180830381600087803b158015611bc557600080fd5b505af1158015611bd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bfd919061213f565b604051632e1a7d4d60e01b81529091506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632e1a7d4d90611c4c908490600401612864565b600060405180830381600087803b158015611c6657600080fd5b505af1158015611c7a573d6000803e3d6000fd5b505050506001600160a01b038316611ca45760405162461bcd60e51b815260040161039c90612704565b611cae8382611d4a565b611d40565b60025460405163f3287c2f60e01b81526001600160a01b039091169063f3287c2f90611ceb90899089908990899089906004016121f1565b602060405180830381600087803b158015611d0557600080fd5b505af1158015611d19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d3d919061213f565b90505b9695505050505050565b604080516000808252602082019092526001600160a01b038416908390604051611d749190612167565b60006040518083038185875af1925050503d8060008114611db1576040519150601f19603f3d011682016040523d82523d6000602084013e611db6565b606091505b505090508061189c5760405162461bcd60e51b815260040161039c906123db565b60008083601f840112611de8578182fd5b50813567ffffffffffffffff811115611dff578182fd5b602083019150836020828501011115611e1757600080fd5b9250929050565b600060208284031215611e2f578081fd5b813561086e8161287a565b600060208284031215611e4b578081fd5b815161086e8161287a565b60008060008060008060008060e0898b031215611e71578384fd5b8835611e7c8161287a565b97506020890135611e8c8161287a565b96506040890135611e9c8161287a565b95506060890135611eac8161287a565b94506080890135935060a0890135611ec38161287a565b925060c089013567ffffffffffffffff811115611ede578283fd5b611eea8b828c01611dd7565b999c989b5096995094979396929594505050565b60008060008060008060008060006101008a8c031215611f1c578081fd5b8935611f278161287a565b985060208a0135611f378161287a565b975060408a0135611f478161287a565b965060608a0135611f578161287a565b955060808a0135945060a08a0135935060c08a0135611f758161287a565b925060e08a013567ffffffffffffffff811115611f90578182fd5b611f9c8c828d01611dd7565b915080935050809150509295985092959850929598565b600080600060608486031215611fc7578283fd5b8335611fd28161287a565b92506020840135611fe28161287a565b929592945050506040919091013590565b60008060008060008060c0878903121561200b578182fd5b86356120168161287a565b955060208701356120268161287a565b9450604087013593506060870135925060808701356120448161287a565b915060a08701356120548161287a565b809150509295509295509295565b60008060408385031215612074578182fd5b823561207f8161287a565b9150602083013561208f8161288f565b809150509250929050565b600080604083850312156120ac578182fd5b82356120b78161287a565b946020939093013593505050565b600080600080600060a086880312156120dc578081fd5b85356120e78161287a565b9450602086013593506040860135925060608601356121058161287a565b915060808601356121158161287a565b809150509295509295909350565b600060208284031215612134578081fd5b815161086e8161288f565b600060208284031215612150578081fd5b5051919050565b6000828483379101908152919050565b60008251815b81811015612187576020818601810151858301520161216d565b818111156121955782828501525b509190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0395861681526020810194909452604084019290925283166060830152909116608082015260a00190565b901515815260200190565b60a0810161223b8761286d565b958152602081019490945260408401929092526001600160a01b0390811660608401521660809091015290565b6020808252601a908201527f576f6f526f757465723a20746f6b656e5f414444525f5a45524f000000000000604082015260600190565b6020808252601f908201527f576f6f526f757465723a20737761705461726765745f414444525f5a45524f00604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601c908201527f576f6f526f757465723a206e6577506f6f6c5f414444525f5a45524f00000000604082015260600190565b6020808252601f908201527f576f6f526f757465723a2046414c4c4241434b5f535741505f4641494c454400604082015260600190565b60208082526031908201527f5472616e7366657248656c7065723a3a7472616e7366657246726f6d3a207472604082015270185b9cd9995c919c9bdb4819985a5b1959607a1b606082015260800190565b60208082526034908201527f5472616e7366657248656c7065723a3a736166655472616e736665724554483a60408201527308115512081d1c985b9cd9995c8819985a5b195960621b606082015260800190565b60208082526022908201527f576f6f526f757465723a20535741505f5441524745545f4e4f545f414c4c4f57604082015261115160f21b606082015260800190565b6020808252601e908201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604082015260600190565b6020808252601c908201527f576f6f526f757465723a20746f546f6b656e5f414444525f5a45524f00000000604082015260600190565b60208082526025908201527f576f6f526f757465723a20415050524f56455f5441524745545f4e4f545f414c6040820152641313d5d15160da1b606082015260800190565b60208082526022908201527f576f6f526f757465723a207265616c546f416d6f756e745f4e4f545f454e4f5560408201526108e960f31b606082015260800190565b60208082526022908201527f576f6f526f757465723a20617070726f76655461726765745f414444525f5a45604082015261524f60f01b606082015260800190565b60208082526017908201527f576f6f526f757465723a20746f5f414444525f5a45524f000000000000000000604082015260600190565b6020808252601e908201527f576f6f526f757465723a2062617365546f6b656e5f414444525f5a45524f0000604082015260600190565b6020808252601d908201527f576f6f526f757465723a2066726f6d416d6f756e745f494e56414c4944000000604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252602b908201527f5472616e7366657248656c7065723a3a73616665417070726f76653a2061707060408201526a1c9bdd994819985a5b195960aa1b606082015260800190565b60208082526018908201527f576f6f526f757465723a2062616c616e63655f4552524f520000000000000000604082015260600190565b60208082526017908201527f576f6f526f757465723a20746f5f5a45524f5f41444452000000000000000000604082015260600190565b6020808252602d908201527f5472616e7366657248656c7065723a3a736166655472616e736665723a20747260408201526c185b9cd9995c8819985a5b1959609a1b606082015260800190565b6020808252601b908201527f576f6f526f757465723a207461726765745f414444525f5a45524f0000000000604082015260600190565b6020808252601e908201527f576f6f526f757465723a2066726f6d546f6b656e5f414444525f5a45524f0000604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6020808252601f908201527f576f6f526f757465723a2071756f7465546f6b656e5f414444525f5a45524f00604082015260600190565b90815260200190565b6002811061287757fe5b50565b6001600160a01b038116811461287757600080fd5b801515811461287757600080fdfe27c98e911efdd224f4002f6cd831c3ad0d2759ee176f9ee8466d95826af22a1ca2646970667358221220cbe5c78b9ca4c75261104c135b8f3a9e2c582b1bc27767cb32dc41fd9a4e37c864736f6c634300060c0033000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c7000000000000000000000000f8ce0d043891b62c55380fb1efbfb4f186153d96
Deployed Bytecode
0x60806040526004361061010d5760003560e01c80637dc2038211610095578063ad5c464811610064578063ad5c4648146102ef578063e94803f414610304578063ea71ee8414610324578063f2fde38b14610337578063f3287c2f1461035757610160565b80637dc20382146102925780638da5cb5b146102a55780639281aa0b146102ba578063a7394603146102da57610160565b806366410a21116100dc57806366410a21146101fd5780636846fb501461021d578063715018a61461023d57806378e3214f1461025257806379a048761461027257610160565b8063199b83fa14610165578063217a4b701461018e5780633af32abf146101b05780634437152a146101dd57610160565b3661016057336001600160a01b037f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c716148061015857503360009081526003602052604090205460ff165b61015e57fe5b005b600080fd5b610178610173366004611efe565b610377565b6040516101859190612864565b60405180910390f35b34801561019a57600080fd5b506101a36105b9565b60405161018591906121a0565b3480156101bc57600080fd5b506101d06101cb366004611e1e565b6105c8565b6040516101859190612223565b3480156101e957600080fd5b5061015e6101f8366004611e1e565b6105dd565b34801561020957600080fd5b5061017861021836600461209a565b610777565b34801561022957600080fd5b506101786102383660046120c5565b610875565b34801561024957600080fd5b5061015e6109ef565b34801561025e57600080fd5b5061015e61026d36600461209a565b610a78565b34801561027e57600080fd5b5061017861028d36600461209a565b610b18565b6101786102a0366004611ff3565b610bbf565b3480156102b157600080fd5b506101a3610fc7565b3480156102c657600080fd5b5061015e6102d5366004612062565b610fd6565b3480156102e657600080fd5b506101a3611092565b3480156102fb57600080fd5b506101a36110a1565b34801561031057600080fd5b5061017861031f366004611fb3565b6110c5565b61015e610332366004611e56565b6113a6565b34801561034357600080fd5b5061015e610352366004611e1e565b6113c3565b34801561036357600080fd5b506101786103723660046120c5565b611483565b6000600260015414156103a55760405162461bcd60e51b815260040161039c906127f6565b60405180910390fd5b60026001556001600160a01b038a166103d05760405162461bcd60e51b815260040161039c90612566565b6001600160a01b0389166103f65760405162461bcd60e51b815260040161039c9061229f565b6001600160a01b03881661041c5760405162461bcd60e51b815260040161039c906127bf565b6001600160a01b0387166104425760405162461bcd60e51b815260040161039c906124a8565b6001600160a01b0384166104685760405162461bcd60e51b815260040161039c906125a8565b6001600160a01b038a1660009081526003602052604090205460ff166104a05760405162461bcd60e51b815260040161039c906124df565b6001600160a01b03891660009081526003602052604090205460ff166104d85760405162461bcd60e51b815260040161039c9061242f565b60006104e488306115f9565b90506104f48b8b8b8a88886116b0565b600061050089306115f9565b9050808211156105225760405162461bcd60e51b815260040161039c906126cd565b61052c818361182f565b92508683101561054e5760405162461bcd60e51b815260040161039c90612524565b610559898785611857565b856001600160a01b0316896001600160a01b03168b6001600160a01b031660008051602061289e83398151915260018c8833600060405161059e95949392919061222e565b60405180910390a45050600180559998505050505050505050565b6004546001600160a01b031681565b60036020526000908152604090205460ff1681565b600260015414156106005760405162461bcd60e51b815260040161039c906127f6565b600260015561060d6118a1565b6001600160a01b031661061e610fc7565b6001600160a01b0316146106445760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b03811661066a5760405162461bcd60e51b815260040161039c9061231c565b600280546001600160a01b0319166001600160a01b03838116919091179182905560408051630217a4b760e41b81529051929091169163217a4b7091600480820192602092909190829003018186803b1580156106c657600080fd5b505afa1580156106da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106fe9190611e3a565b600480546001600160a01b0319166001600160a01b039283161790819055166107395760405162461bcd60e51b815260040161039c9061282d565b7f4577a21bd8e55848c574b7582f8e6cc6a7cf1c1958f36a9751eab6329d656b1e8160405161076891906121a0565b60405180910390a15060018055565b60006001600160a01b03831661079f5760405162461bcd60e51b815260040161039c906125df565b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146107c957826107eb565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c75b6002546040516366410a2160e01b81529194506001600160a01b0316906366410a219061081e90869086906004016121d8565b60206040518083038186803b15801561083657600080fd5b505afa15801561084a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086e919061213f565b9392505050565b60006002600154141561089a5760405162461bcd60e51b815260040161039c906127f6565b60026001556001600160a01b0386166108c55760405162461bcd60e51b815260040161039c906125df565b6001600160a01b0383166108eb5760405162461bcd60e51b815260040161039c906125a8565b6108f7863330886118a5565b60025461090f9087906001600160a01b031687611996565b6002546040516306846fb560e41b81526001600160a01b0390911690636846fb509061094790899089908990899089906004016121f1565b602060405180830381600087803b15801561096157600080fd5b505af1158015610975573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610999919061213f565b6004546040519192506001600160a01b0380861692918116919089169060008051602061289e833981519152906109da906000908b90889033908b9061222e565b60405180910390a46001805595945050505050565b6109f76118a1565b6001600160a01b0316610a08610fc7565b6001600160a01b031614610a2e5760405162461bcd60e51b815260040161039c9061264d565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60026001541415610a9b5760405162461bcd60e51b815260040161039c906127f6565b6002600155610aa86118a1565b6001600160a01b0316610ab9610fc7565b6001600160a01b031614610adf5760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b038216610b055760405162461bcd60e51b815260040161039c90612268565b610b10823383611a84565b505060018055565b60006001600160a01b038316610b405760405162461bcd60e51b815260040161039c906125df565b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610b6a5782610b8c565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c75b600254604051633cd0243b60e11b81529194506001600160a01b0316906379a048769061081e90869086906004016121d8565b600060026001541415610be45760405162461bcd60e51b815260040161039c906127f6565b60026001556001600160a01b038716610c0f5760405162461bcd60e51b815260040161039c906127bf565b6001600160a01b038616610c355760405162461bcd60e51b815260040161039c906124a8565b6001600160a01b038316610c5b5760405162461bcd60e51b815260040161039c906125a8565b6001600160a01b0387811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9081149188161481610c8d5788610caf565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c75b985080610cbc5787610cde565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c75b97508115610d7f5734871115610d065760405162461bcd60e51b815260040161039c90612616565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c76001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015610d6157600080fd5b505af1158015610d75573d6000803e3d6000fd5b5050505050610d8b565b610d8b8933308a6118a5565b600254610da3908a906001600160a01b031689611996565b6004546001600160a01b038a811691161415610dce57610dc7818989898989611b6b565b9250610f2b565b6004546001600160a01b0389811691161415610e6e576002546040516306846fb560e41b81526001600160a01b0390911690636846fb5090610e1c908c908b908b908b908b906004016121f1565b602060405180830381600087803b158015610e3657600080fd5b505af1158015610e4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc7919061213f565b6002546040516306846fb560e41b81526000916001600160a01b031690636846fb5090610ea7908d908c90869030908c906004016121f1565b602060405180830381600087803b158015610ec157600080fd5b505af1158015610ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef9919061213f565b600454600254919250610f19916001600160a01b03918216911683611996565b610f27828a838a8a8a611b6b565b9350505b846001600160a01b031681610f405788610f56565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b031683610f6a578a610f80565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b031660008051602061289e83398151915260008b88338b604051610faf95949392919061222e565b60405180910390a45050600180559695505050505050565b6000546001600160a01b031690565b60026001541415610ff95760405162461bcd60e51b815260040161039c906127f6565b60026001556110066118a1565b6001600160a01b0316611017610fc7565b6001600160a01b03161461103d5760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b0382166110635760405162461bcd60e51b815260040161039c90612788565b6001600160a01b03919091166000908152600360205260409020805460ff191691151591909117905560018055565b6002546001600160a01b031681565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c781565b60006001600160a01b0384166110ed5760405162461bcd60e51b815260040161039c906127bf565b6001600160a01b0383166111135760405162461bcd60e51b815260040161039c906124a8565b6001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461113d578361115f565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c75b93506001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461118b57826111ad565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c75b6004549093506001600160a01b038581169116141561124f576002546040516366410a2160e01b81526001600160a01b03909116906366410a21906111f890869086906004016121d8565b60206040518083038186803b15801561121057600080fd5b505afa158015611224573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611248919061213f565b905061086e565b6004546001600160a01b038481169116141561129757600254604051633cd0243b60e11b81526001600160a01b03909116906379a04876906111f890879086906004016121d8565b600254604051633cd0243b60e11b81526000916001600160a01b0316906379a04876906112ca90889087906004016121d8565b60206040518083038186803b1580156112e257600080fd5b505afa1580156112f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061131a919061213f565b6002546040516366410a2160e01b81529192506001600160a01b0316906366410a219061134d90879085906004016121d8565b60206040518083038186803b15801561136557600080fd5b505afa158015611379573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061139d919061213f565b95945050505050565b6113b888888888886000898989610377565b505050505050505050565b6113cb6118a1565b6001600160a01b03166113dc610fc7565b6001600160a01b0316146114025760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b0381166114285760405162461bcd60e51b815260040161039c906122d6565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000600260015414156114a85760405162461bcd60e51b815260040161039c906127f6565b60026001556001600160a01b0386166114d35760405162461bcd60e51b815260040161039c906125df565b6001600160a01b0383166114f95760405162461bcd60e51b815260040161039c906125a8565b600454611511906001600160a01b03163330886118a5565b60045460025461152e916001600160a01b03908116911687611996565b60025460405163f3287c2f60e01b81526001600160a01b039091169063f3287c2f9061156690899089908990899089906004016121f1565b602060405180830381600087803b15801561158057600080fd5b505af1158015611594573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b8919061213f565b6004546040519192506001600160a01b0380861692898216929091169060008051602061289e833981519152906109da906000908b90889033908b9061222e565b60006001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146116a0576040516370a0823160e01b81526001600160a01b038416906370a082319061164b9085906004016121a0565b60206040518083038186803b15801561166357600080fd5b505afa158015611677573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061169b919061213f565b61086e565b506001600160a01b031631919050565b6001600160a01b03861660009081526003602052604090205460ff166116e85760405162461bcd60e51b815260040161039c906124df565b6001600160a01b03851660009081526003602052604090205460ff166117205760405162461bcd60e51b815260040161039c9061242f565b6001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461176057611750843330866118a5565b61175b848785611996565b611780565b348311156117805760405162461bcd60e51b815260040161039c90612616565b60006001600160a01b0380871690861673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146117b15760006117b3565b845b84846040516117c3929190612157565b60006040518083038185875af1925050503d8060008114611800576040519150601f19603f3d011682016040523d82523d6000602084013e611805565b606091505b50509050806118265760405162461bcd60e51b815260040161039c90612353565b50505050505050565b6000828211156118515760405162461bcd60e51b815260040161039c90612471565b50900390565b801561189c576001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14156118915761188c8282611d4a565b61189c565b61189c838383611a84565b505050565b3390565b60006060856001600160a01b03166323b872dd8686866040516024016118cd939291906121b4565b6040516020818303038152906040529060e01b6020820180516001600160e01b0383818316178352505050506040516119069190612167565b6000604051808303816000865af19150503d8060008114611943576040519150601f19603f3d011682016040523d82523d6000602084013e611948565b606091505b50915091508180156119725750805115806119725750808060200190518101906119729190612123565b61198e5760405162461bcd60e51b815260040161039c9061238a565b505050505050565b60006060846001600160a01b031663095ea7b385856040516024016119bc9291906121d8565b6040516020818303038152906040529060e01b6020820180516001600160e01b0383818316178352505050506040516119f59190612167565b6000604051808303816000865af19150503d8060008114611a32576040519150601f19603f3d011682016040523d82523d6000602084013e611a37565b606091505b5091509150818015611a61575080511580611a61575080806020019051810190611a619190612123565b611a7d5760405162461bcd60e51b815260040161039c90612682565b5050505050565b60006060846001600160a01b031663a9059cbb8585604051602401611aaa9291906121d8565b6040516020818303038152906040529060e01b6020820180516001600160e01b038381831617835250505050604051611ae39190612167565b6000604051808303816000865af19150503d8060008114611b20576040519150601f19603f3d011682016040523d82523d6000602084013e611b25565b606091505b5091509150818015611b4f575080511580611b4f575080806020019051810190611b4f9190612123565b611a7d5760405162461bcd60e51b815260040161039c9061273b565b60008615611cb35760025460405163f3287c2f60e01b81526001600160a01b039091169063f3287c2f90611bab90899089908990309089906004016121f1565b602060405180830381600087803b158015611bc557600080fd5b505af1158015611bd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bfd919061213f565b604051632e1a7d4d60e01b81529091506001600160a01b037f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c71690632e1a7d4d90611c4c908490600401612864565b600060405180830381600087803b158015611c6657600080fd5b505af1158015611c7a573d6000803e3d6000fd5b505050506001600160a01b038316611ca45760405162461bcd60e51b815260040161039c90612704565b611cae8382611d4a565b611d40565b60025460405163f3287c2f60e01b81526001600160a01b039091169063f3287c2f90611ceb90899089908990899089906004016121f1565b602060405180830381600087803b158015611d0557600080fd5b505af1158015611d19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d3d919061213f565b90505b9695505050505050565b604080516000808252602082019092526001600160a01b038416908390604051611d749190612167565b60006040518083038185875af1925050503d8060008114611db1576040519150601f19603f3d011682016040523d82523d6000602084013e611db6565b606091505b505090508061189c5760405162461bcd60e51b815260040161039c906123db565b60008083601f840112611de8578182fd5b50813567ffffffffffffffff811115611dff578182fd5b602083019150836020828501011115611e1757600080fd5b9250929050565b600060208284031215611e2f578081fd5b813561086e8161287a565b600060208284031215611e4b578081fd5b815161086e8161287a565b60008060008060008060008060e0898b031215611e71578384fd5b8835611e7c8161287a565b97506020890135611e8c8161287a565b96506040890135611e9c8161287a565b95506060890135611eac8161287a565b94506080890135935060a0890135611ec38161287a565b925060c089013567ffffffffffffffff811115611ede578283fd5b611eea8b828c01611dd7565b999c989b5096995094979396929594505050565b60008060008060008060008060006101008a8c031215611f1c578081fd5b8935611f278161287a565b985060208a0135611f378161287a565b975060408a0135611f478161287a565b965060608a0135611f578161287a565b955060808a0135945060a08a0135935060c08a0135611f758161287a565b925060e08a013567ffffffffffffffff811115611f90578182fd5b611f9c8c828d01611dd7565b915080935050809150509295985092959850929598565b600080600060608486031215611fc7578283fd5b8335611fd28161287a565b92506020840135611fe28161287a565b929592945050506040919091013590565b60008060008060008060c0878903121561200b578182fd5b86356120168161287a565b955060208701356120268161287a565b9450604087013593506060870135925060808701356120448161287a565b915060a08701356120548161287a565b809150509295509295509295565b60008060408385031215612074578182fd5b823561207f8161287a565b9150602083013561208f8161288f565b809150509250929050565b600080604083850312156120ac578182fd5b82356120b78161287a565b946020939093013593505050565b600080600080600060a086880312156120dc578081fd5b85356120e78161287a565b9450602086013593506040860135925060608601356121058161287a565b915060808601356121158161287a565b809150509295509295909350565b600060208284031215612134578081fd5b815161086e8161288f565b600060208284031215612150578081fd5b5051919050565b6000828483379101908152919050565b60008251815b81811015612187576020818601810151858301520161216d565b818111156121955782828501525b509190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0395861681526020810194909452604084019290925283166060830152909116608082015260a00190565b901515815260200190565b60a0810161223b8761286d565b958152602081019490945260408401929092526001600160a01b0390811660608401521660809091015290565b6020808252601a908201527f576f6f526f757465723a20746f6b656e5f414444525f5a45524f000000000000604082015260600190565b6020808252601f908201527f576f6f526f757465723a20737761705461726765745f414444525f5a45524f00604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601c908201527f576f6f526f757465723a206e6577506f6f6c5f414444525f5a45524f00000000604082015260600190565b6020808252601f908201527f576f6f526f757465723a2046414c4c4241434b5f535741505f4641494c454400604082015260600190565b60208082526031908201527f5472616e7366657248656c7065723a3a7472616e7366657246726f6d3a207472604082015270185b9cd9995c919c9bdb4819985a5b1959607a1b606082015260800190565b60208082526034908201527f5472616e7366657248656c7065723a3a736166655472616e736665724554483a60408201527308115512081d1c985b9cd9995c8819985a5b195960621b606082015260800190565b60208082526022908201527f576f6f526f757465723a20535741505f5441524745545f4e4f545f414c4c4f57604082015261115160f21b606082015260800190565b6020808252601e908201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604082015260600190565b6020808252601c908201527f576f6f526f757465723a20746f546f6b656e5f414444525f5a45524f00000000604082015260600190565b60208082526025908201527f576f6f526f757465723a20415050524f56455f5441524745545f4e4f545f414c6040820152641313d5d15160da1b606082015260800190565b60208082526022908201527f576f6f526f757465723a207265616c546f416d6f756e745f4e4f545f454e4f5560408201526108e960f31b606082015260800190565b60208082526022908201527f576f6f526f757465723a20617070726f76655461726765745f414444525f5a45604082015261524f60f01b606082015260800190565b60208082526017908201527f576f6f526f757465723a20746f5f414444525f5a45524f000000000000000000604082015260600190565b6020808252601e908201527f576f6f526f757465723a2062617365546f6b656e5f414444525f5a45524f0000604082015260600190565b6020808252601d908201527f576f6f526f757465723a2066726f6d416d6f756e745f494e56414c4944000000604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252602b908201527f5472616e7366657248656c7065723a3a73616665417070726f76653a2061707060408201526a1c9bdd994819985a5b195960aa1b606082015260800190565b60208082526018908201527f576f6f526f757465723a2062616c616e63655f4552524f520000000000000000604082015260600190565b60208082526017908201527f576f6f526f757465723a20746f5f5a45524f5f41444452000000000000000000604082015260600190565b6020808252602d908201527f5472616e7366657248656c7065723a3a736166655472616e736665723a20747260408201526c185b9cd9995c8819985a5b1959609a1b606082015260800190565b6020808252601b908201527f576f6f526f757465723a207461726765745f414444525f5a45524f0000000000604082015260600190565b6020808252601e908201527f576f6f526f757465723a2066726f6d546f6b656e5f414444525f5a45524f0000604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6020808252601f908201527f576f6f526f757465723a2071756f7465546f6b656e5f414444525f5a45524f00604082015260600190565b90815260200190565b6002811061287757fe5b50565b6001600160a01b038116811461287757600080fd5b801515811461287757600080fdfe27c98e911efdd224f4002f6cd831c3ad0d2759ee176f9ee8466d95826af22a1ca2646970667358221220cbe5c78b9ca4c75261104c135b8f3a9e2c582b1bc27767cb32dc41fd9a4e37c864736f6c634300060c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c7000000000000000000000000f8ce0d043891b62c55380fb1efbfb4f186153d96
-----Decoded View---------------
Arg [0] : weth (address): 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7
Arg [1] : newPool (address): 0xF8cE0D043891b62c55380fB1EFBfB4F186153D96
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c7
Arg [1] : 000000000000000000000000f8ce0d043891b62c55380fb1efbfb4f186153d96
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
AVAX | 100.00% | $22.76 | 34.2495 | $779.64 |
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.