├── Boardroom.sol ├── Distributor.sol ├── DummyToken.sol ├── GBond.sol ├── Grape.sol ├── LICENSE ├── Migrations.sol ├── Oracle.sol ├── README.md ├── SimpleERCFund.sol ├── TaxOffice.sol ├── TaxOfficeV2.sol ├── TaxOracle.sol ├── Timelock.sol ├── Treasury.sol ├── Wine.sol ├── distribution ├── GrapeGenesisRewardPool.sol ├── GrapeRewardPool.sol └── WineRewardPool.sol ├── interfaces ├── IBasisAsset.sol ├── IBoardroom.sol ├── IDecimals.sol ├── IDistributor.sol ├── IERC20.sol ├── IGShareRewardPool.sol ├── IOracle.sol ├── IShare.sol ├── ISimpleERCFund.sol ├── ITaxable.sol ├── ITreasury.sol ├── IUniswapV2Callee.sol ├── IUniswapV2ERC20.sol ├── IUniswapV2Factory.sol ├── IUniswapV2Pair.sol ├── IUniswapV2Router.sol └── IWrappedEth.sol ├── lib ├── Babylonian.sol ├── FixedPoint.sol ├── Safe112.sol ├── SafeMath8.sol ├── UQ112x112.sol ├── UniswapV2Library.sol └── UniswapV2OracleLibrary.sol ├── owner └── Operator.sol └── utils ├── ContractGuard.sol └── Epoch.sol /Boardroom.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 7 | 8 | import "./utils/ContractGuard.sol"; 9 | import "./interfaces/IBasisAsset.sol"; 10 | import "./interfaces/ITreasury.sol"; 11 | 12 | contract ShareWrapper { 13 | using SafeMath for uint256; 14 | using SafeERC20 for IERC20; 15 | 16 | IERC20 public share; 17 | 18 | uint256 private _totalSupply; 19 | mapping(address => uint256) private _balances; 20 | 21 | function totalSupply() public view returns (uint256) { 22 | return _totalSupply; 23 | } 24 | 25 | function balanceOf(address account) public view returns (uint256) { 26 | return _balances[account]; 27 | } 28 | 29 | function stake(uint256 amount) public virtual { 30 | _totalSupply = _totalSupply.add(amount); 31 | _balances[msg.sender] = _balances[msg.sender].add(amount); 32 | share.safeTransferFrom(msg.sender, address(this), amount); 33 | } 34 | 35 | function withdraw(uint256 amount) public virtual { 36 | uint256 memberShare = _balances[msg.sender]; 37 | require(memberShare >= amount, "Boardroom: withdraw request greater than staked amount"); 38 | _totalSupply = _totalSupply.sub(amount); 39 | _balances[msg.sender] = memberShare.sub(amount); 40 | share.safeTransfer(msg.sender, amount); 41 | } 42 | } 43 | 44 | 45 | contract Boardroom is ShareWrapper, ContractGuard { 46 | using SafeERC20 for IERC20; 47 | using Address for address; 48 | using SafeMath for uint256; 49 | 50 | /* ========== DATA STRUCTURES ========== */ 51 | 52 | struct Memberseat { 53 | uint256 lastSnapshotIndex; 54 | uint256 rewardEarned; 55 | uint256 epochTimerStart; 56 | } 57 | 58 | struct BoardroomSnapshot { 59 | uint256 time; 60 | uint256 rewardReceived; 61 | uint256 rewardPerShare; 62 | } 63 | 64 | /* ========== STATE VARIABLES ========== */ 65 | 66 | // governance 67 | address public operator; 68 | 69 | // flags 70 | bool public initialized = false; 71 | 72 | IERC20 public grape; 73 | ITreasury public treasury; 74 | 75 | mapping(address => Memberseat) public members; 76 | BoardroomSnapshot[] public boardroomHistory; 77 | 78 | uint256 public withdrawLockupEpochs; 79 | uint256 public rewardLockupEpochs; 80 | 81 | /* ========== EVENTS ========== */ 82 | 83 | event Initialized(address indexed executor, uint256 at); 84 | event Staked(address indexed user, uint256 amount); 85 | event Withdrawn(address indexed user, uint256 amount); 86 | event RewardPaid(address indexed user, uint256 reward); 87 | event RewardAdded(address indexed user, uint256 reward); 88 | 89 | /* ========== Modifiers =============== */ 90 | 91 | modifier onlyOperator() { 92 | require(operator == msg.sender, "Boardroom: caller is not the operator"); 93 | _; 94 | } 95 | 96 | modifier memberExists() { 97 | require(balanceOf(msg.sender) > 0, "Boardroom: The member does not exist"); 98 | _; 99 | } 100 | 101 | modifier updateReward(address member) { 102 | if (member != address(0)) { 103 | Memberseat memory seat = members[member]; 104 | seat.rewardEarned = earned(member); 105 | seat.lastSnapshotIndex = latestSnapshotIndex(); 106 | members[member] = seat; 107 | } 108 | _; 109 | } 110 | 111 | modifier notInitialized() { 112 | require(!initialized, "Boardroom: already initialized"); 113 | _; 114 | } 115 | 116 | /* ========== GOVERNANCE ========== */ 117 | 118 | function initialize( 119 | IERC20 _grape, 120 | IERC20 _share, 121 | ITreasury _treasury 122 | ) public notInitialized { 123 | grape = _grape; 124 | share = _share; 125 | treasury = _treasury; 126 | 127 | BoardroomSnapshot memory genesisSnapshot = BoardroomSnapshot({time: block.number, rewardReceived: 0, rewardPerShare: 0}); 128 | boardroomHistory.push(genesisSnapshot); 129 | 130 | withdrawLockupEpochs = 6; // Lock for 6 epochs (36h) before release withdraw 131 | rewardLockupEpochs = 3; // Lock for 3 epochs (18h) before release claimReward 132 | 133 | initialized = true; 134 | operator = msg.sender; 135 | emit Initialized(msg.sender, block.number); 136 | } 137 | 138 | function setOperator(address _operator) external onlyOperator { 139 | operator = _operator; 140 | } 141 | 142 | function setLockUp(uint256 _withdrawLockupEpochs, uint256 _rewardLockupEpochs) external onlyOperator { 143 | require(_withdrawLockupEpochs >= _rewardLockupEpochs && _withdrawLockupEpochs <= 56, "_withdrawLockupEpochs: out of range"); // <= 2 week 144 | withdrawLockupEpochs = _withdrawLockupEpochs; 145 | rewardLockupEpochs = _rewardLockupEpochs; 146 | } 147 | 148 | /* ========== VIEW FUNCTIONS ========== */ 149 | 150 | // =========== Snapshot getters 151 | 152 | function latestSnapshotIndex() public view returns (uint256) { 153 | return boardroomHistory.length.sub(1); 154 | } 155 | 156 | function getLatestSnapshot() internal view returns (BoardroomSnapshot memory) { 157 | return boardroomHistory[latestSnapshotIndex()]; 158 | } 159 | 160 | function getLastSnapshotIndexOf(address member) public view returns (uint256) { 161 | return members[member].lastSnapshotIndex; 162 | } 163 | 164 | function getLastSnapshotOf(address member) internal view returns (BoardroomSnapshot memory) { 165 | return boardroomHistory[getLastSnapshotIndexOf(member)]; 166 | } 167 | 168 | function canWithdraw(address member) external view returns (bool) { 169 | return members[member].epochTimerStart.add(withdrawLockupEpochs) <= treasury.epoch(); 170 | } 171 | 172 | function canClaimReward(address member) external view returns (bool) { 173 | return members[member].epochTimerStart.add(rewardLockupEpochs) <= treasury.epoch(); 174 | } 175 | 176 | function epoch() external view returns (uint256) { 177 | return treasury.epoch(); 178 | } 179 | 180 | function nextEpochPoint() external view returns (uint256) { 181 | return treasury.nextEpochPoint(); 182 | } 183 | 184 | function getGrapePrice() external view returns (uint256) { 185 | return treasury.getGrapePrice(); 186 | } 187 | 188 | // =========== Member getters 189 | 190 | function rewardPerShare() public view returns (uint256) { 191 | return getLatestSnapshot().rewardPerShare; 192 | } 193 | 194 | function earned(address member) public view returns (uint256) { 195 | uint256 latestRPS = getLatestSnapshot().rewardPerShare; 196 | uint256 storedRPS = getLastSnapshotOf(member).rewardPerShare; 197 | 198 | return balanceOf(member).mul(latestRPS.sub(storedRPS)).div(1e18).add(members[member].rewardEarned); 199 | } 200 | 201 | /* ========== MUTATIVE FUNCTIONS ========== */ 202 | 203 | function stake(uint256 amount) public override onlyOneBlock updateReward(msg.sender) { 204 | require(amount > 0, "Boardroom: Cannot stake 0"); 205 | super.stake(amount); 206 | members[msg.sender].epochTimerStart = treasury.epoch(); // reset timer 207 | emit Staked(msg.sender, amount); 208 | } 209 | 210 | function withdraw(uint256 amount) public override onlyOneBlock memberExists updateReward(msg.sender) { 211 | require(amount > 0, "Boardroom: Cannot withdraw 0"); 212 | require(members[msg.sender].epochTimerStart.add(withdrawLockupEpochs) <= treasury.epoch(), "Boardroom: still in withdraw lockup"); 213 | claimReward(); 214 | super.withdraw(amount); 215 | emit Withdrawn(msg.sender, amount); 216 | } 217 | 218 | function exit() external { 219 | withdraw(balanceOf(msg.sender)); 220 | } 221 | 222 | function claimReward() public updateReward(msg.sender) { 223 | uint256 reward = members[msg.sender].rewardEarned; 224 | if (reward > 0) { 225 | require(members[msg.sender].epochTimerStart.add(rewardLockupEpochs) <= treasury.epoch(), "Boardroom: still in reward lockup"); 226 | members[msg.sender].epochTimerStart = treasury.epoch(); // reset timer 227 | members[msg.sender].rewardEarned = 0; 228 | grape.safeTransfer(msg.sender, reward); 229 | emit RewardPaid(msg.sender, reward); 230 | } 231 | } 232 | 233 | function allocateSeigniorage(uint256 amount) external onlyOneBlock onlyOperator { 234 | require(amount > 0, "Boardroom: Cannot allocate 0"); 235 | require(totalSupply() > 0, "Boardroom: Cannot allocate when totalSupply is 0"); 236 | 237 | // Create & add new snapshot 238 | uint256 prevRPS = getLatestSnapshot().rewardPerShare; 239 | uint256 nextRPS = prevRPS.add(amount.mul(1e18).div(totalSupply())); 240 | 241 | BoardroomSnapshot memory newSnapshot = BoardroomSnapshot({time: block.number, rewardReceived: amount, rewardPerShare: nextRPS}); 242 | boardroomHistory.push(newSnapshot); 243 | 244 | grape.safeTransferFrom(msg.sender, address(this), amount); 245 | emit RewardAdded(msg.sender, amount); 246 | } 247 | 248 | function governanceRecoverUnsupported( 249 | IERC20 _token, 250 | uint256 _amount, 251 | address _to 252 | ) external onlyOperator { 253 | // do not allow to drain core tokens 254 | require(address(_token) != address(grape), "grape"); 255 | require(address(_token) != address(share), "share"); 256 | _token.safeTransfer(_to, _amount); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /Distributor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "./interfaces/IDistributor.sol"; 6 | 7 | contract Distributor { 8 | IDistributor[] public distributors; 9 | 10 | constructor(IDistributor[] memory _distributors) public { 11 | distributors = _distributors; 12 | } 13 | 14 | function distribute() public { 15 | for (uint256 i = 0; i < distributors.length; i++) { 16 | distributors[i].distribute(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /DummyToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol"; 6 | 7 | import "./owner/Operator.sol"; 8 | 9 | contract DummyToken is ERC20Burnable, Operator { 10 | 11 | constructor() public ERC20("DummyToken", "DUMMY") {} 12 | 13 | function mint(address recipient_, uint256 amount_) public onlyOperator returns (bool) { 14 | uint256 balanceBefore = balanceOf(recipient_); 15 | _mint(recipient_, amount_); 16 | super.burnFrom(recipient_, amount_); 17 | uint256 balanceAfter = balanceOf(recipient_); 18 | 19 | return balanceAfter > balanceBefore; 20 | } 21 | 22 | function burn(uint256 amount) public override { 23 | super.burn(amount); 24 | } 25 | 26 | function burnFrom(address account, uint256 amount) public override onlyOperator { 27 | super.burnFrom(account, amount); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /GBond.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol"; 6 | 7 | import "./owner/Operator.sol"; 8 | 9 | /* 10 | 11 | ██████╗ ██████╗ █████╗ ██████╗ ███████╗ ███████╗██╗███╗ ██╗ █████╗ ███╗ ██╗ ██████╗███████╗ 12 | ██╔════╝ ██╔══██╗██╔══██╗██╔══██╗██╔════╝ ██╔════╝██║████╗ ██║██╔══██╗████╗ ██║██╔════╝██╔════╝ 13 | ██║ ███╗██████╔╝███████║██████╔╝█████╗ █████╗ ██║██╔██╗ ██║███████║██╔██╗ ██║██║ █████╗ 14 | ██║ ██║██╔══██╗██╔══██║██╔═══╝ ██╔══╝ ██╔══╝ ██║██║╚██╗██║██╔══██║██║╚██╗██║██║ ██╔══╝ 15 | ╚██████╔╝██║ ██║██║ ██║██║ ███████╗ ██║ ██║██║ ╚████║██║ ██║██║ ╚████║╚██████╗███████╗ 16 | ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚══════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝╚══════╝ 17 | 18 | */ 19 | 20 | 21 | contract GBond is ERC20Burnable, Operator { 22 | /** 23 | * @notice Constructs the GRAPE Bond ERC-20 contract. 24 | */ 25 | constructor() public ERC20("GBOND", "GBOND") {} 26 | 27 | /** 28 | * @notice Operator mints grape bonds to a recipient 29 | * @param recipient_ The address of recipient 30 | * @param amount_ The amount of grape bonds to mint to 31 | * @return whether the process has been done 32 | */ 33 | function mint(address recipient_, uint256 amount_) public onlyOperator returns (bool) { 34 | uint256 balanceBefore = balanceOf(recipient_); 35 | _mint(recipient_, amount_); 36 | uint256 balanceAfter = balanceOf(recipient_); 37 | 38 | return balanceAfter > balanceBefore; 39 | } 40 | 41 | function burn(uint256 amount) public override { 42 | super.burn(amount); 43 | } 44 | 45 | function burnFrom(address account, uint256 amount) public override onlyOperator { 46 | super.burnFrom(account, amount); 47 | } 48 | } -------------------------------------------------------------------------------- /Grape.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol"; 6 | import "@openzeppelin/contracts/math/Math.sol"; 7 | 8 | import "./lib/SafeMath8.sol"; 9 | import "./owner/Operator.sol"; 10 | import "./interfaces/IOracle.sol"; 11 | 12 | /* 13 | 14 | ██████╗ ██████╗ █████╗ ██████╗ ███████╗ ███████╗██╗███╗ ██╗ █████╗ ███╗ ██╗ ██████╗███████╗ 15 | ██╔════╝ ██╔══██╗██╔══██╗██╔══██╗██╔════╝ ██╔════╝██║████╗ ██║██╔══██╗████╗ ██║██╔════╝██╔════╝ 16 | ██║ ███╗██████╔╝███████║██████╔╝█████╗ █████╗ ██║██╔██╗ ██║███████║██╔██╗ ██║██║ █████╗ 17 | ██║ ██║██╔══██╗██╔══██║██╔═══╝ ██╔══╝ ██╔══╝ ██║██║╚██╗██║██╔══██║██║╚██╗██║██║ ██╔══╝ 18 | ╚██████╔╝██║ ██║██║ ██║██║ ███████╗ ██║ ██║██║ ╚████║██║ ██║██║ ╚████║╚██████╗███████╗ 19 | ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚══════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝╚══════╝ 20 | 21 | */ 22 | 23 | contract Grape is ERC20Burnable, Operator { 24 | using SafeMath8 for uint8; 25 | using SafeMath for uint256; 26 | 27 | // Initial distribution for the first 24h genesis pools 28 | uint256 public constant INITIAL_GENESIS_POOL_DISTRIBUTION = 2400 ether; 29 | // Initial distribution for the day 2-5 GRAPE-WETH LP -> GRAPE pool 30 | uint256 public constant INITIAL_GRAPE_POOL_DISTRIBUTION = 21600 ether; 31 | // Distribution for airdrops wallet 32 | uint256 public constant INITIAL_AIRDROP_WALLET_DISTRIBUTION = 1000 ether; 33 | 34 | // Have the rewards been distributed to the pools 35 | bool public rewardPoolDistributed = false; 36 | 37 | address public grapeOracle; 38 | 39 | /** 40 | * @notice Constructs the GRAPE ERC-20 contract. 41 | */ 42 | constructor() public ERC20("Grape Finance", "GRAPE") { 43 | // Mints 1 GRAPE to contract creator for initial pool setup 44 | _mint(msg.sender, 1 ether); 45 | } 46 | 47 | function _getGrapePrice() internal view returns (uint256 _grapePrice) { 48 | try IOracle(grapeOracle).consult(address(this), 1e18) returns (uint144 _price) { 49 | return uint256(_price); 50 | } catch { 51 | revert("Grape: failed to fetch GRAPE price from Oracle"); 52 | } 53 | } 54 | 55 | function setGrapeOracle(address _grapeOracle) public onlyOperator { 56 | require(_grapeOracle != address(0), "oracle address cannot be 0 address"); 57 | grapeOracle = _grapeOracle; 58 | } 59 | 60 | /** 61 | * @notice Operator mints GRAPE to a recipient 62 | * @param recipient_ The address of recipient 63 | * @param amount_ The amount of GRAPE to mint to 64 | * @return whether the process has been done 65 | */ 66 | function mint(address recipient_, uint256 amount_) public onlyOperator returns (bool) { 67 | uint256 balanceBefore = balanceOf(recipient_); 68 | _mint(recipient_, amount_); 69 | uint256 balanceAfter = balanceOf(recipient_); 70 | 71 | return balanceAfter > balanceBefore; 72 | } 73 | 74 | function burn(uint256 amount) public override { 75 | super.burn(amount); 76 | } 77 | 78 | function burnFrom(address account, uint256 amount) public override onlyOperator { 79 | super.burnFrom(account, amount); 80 | } 81 | 82 | function transferFrom( 83 | address sender, 84 | address recipient, 85 | uint256 amount 86 | ) public override returns (bool) { 87 | _transfer(sender, recipient, amount); 88 | _approve(sender, _msgSender(), allowance(sender, _msgSender()).sub(amount, "ERC20: transfer amount exceeds allowance")); 89 | return true; 90 | } 91 | 92 | /** 93 | * @notice distribute to reward pool (only once) 94 | */ 95 | function distributeReward( 96 | address _genesisPool, 97 | address _grapePool, 98 | address _airdropWallet 99 | ) external onlyOperator { 100 | require(!rewardPoolDistributed, "only can distribute once"); 101 | require(_genesisPool != address(0), "!_genesisPool"); 102 | require(_grapePool != address(0), "!_grapePool"); 103 | require(_airdropWallet != address(0), "!_airdropWallet"); 104 | rewardPoolDistributed = true; 105 | _mint(_genesisPool, INITIAL_GENESIS_POOL_DISTRIBUTION); 106 | _mint(_grapePool, INITIAL_GRAPE_POOL_DISTRIBUTION); 107 | _mint(_airdropWallet, INITIAL_AIRDROP_WALLET_DISTRIBUTION); 108 | } 109 | 110 | function governanceRecoverUnsupported( 111 | IERC20 _token, 112 | uint256 _amount, 113 | address _to 114 | ) external onlyOperator { 115 | _token.transfer(_to, _amount); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Grape Finance 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Migrations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.4.22 <0.8.0; 4 | 5 | contract Migrations { 6 | address public owner; 7 | uint256 public last_completed_migration; 8 | 9 | constructor() public { 10 | owner = msg.sender; 11 | } 12 | 13 | modifier restricted() { 14 | if (msg.sender == owner) _; 15 | } 16 | 17 | function setCompleted(uint256 completed) public restricted { 18 | last_completed_migration = completed; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Oracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | 7 | import "./lib/Babylonian.sol"; 8 | import "./lib/FixedPoint.sol"; 9 | import "./lib/UniswapV2OracleLibrary.sol"; 10 | import "./utils/Epoch.sol"; 11 | import "./interfaces/IUniswapV2Pair.sol"; 12 | 13 | 14 | // fixed window oracle that recomputes the average price for the entire period once every period 15 | // note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period 16 | contract Oracle is Epoch { 17 | using FixedPoint for *; 18 | using SafeMath for uint256; 19 | 20 | /* ========== STATE VARIABLES ========== */ 21 | 22 | // uniswap 23 | address public token0; 24 | address public token1; 25 | IUniswapV2Pair public pair; 26 | 27 | // oracle 28 | uint32 public blockTimestampLast; 29 | uint256 public price0CumulativeLast; 30 | uint256 public price1CumulativeLast; 31 | FixedPoint.uq112x112 public price0Average; 32 | FixedPoint.uq112x112 public price1Average; 33 | 34 | /* ========== CONSTRUCTOR ========== */ 35 | 36 | constructor( 37 | IUniswapV2Pair _pair, 38 | uint256 _period, 39 | uint256 _startTime 40 | ) public Epoch(_period, _startTime, 0) { 41 | pair = _pair; 42 | token0 = pair.token0(); 43 | token1 = pair.token1(); 44 | price0CumulativeLast = pair.price0CumulativeLast(); // fetch the current accumulated price value (1 / 0) 45 | price1CumulativeLast = pair.price1CumulativeLast(); // fetch the current accumulated price value (0 / 1) 46 | uint112 reserve0; 47 | uint112 reserve1; 48 | (reserve0, reserve1, blockTimestampLast) = pair.getReserves(); 49 | require(reserve0 != 0 && reserve1 != 0, "Oracle: NO_RESERVES"); // ensure that there's liquidity in the pair 50 | } 51 | 52 | /* ========== MUTABLE FUNCTIONS ========== */ 53 | 54 | /** @dev Updates 1-day EMA price from Uniswap. */ 55 | function update() external checkEpoch { 56 | (uint256 price0Cumulative, uint256 price1Cumulative, uint32 blockTimestamp) = UniswapV2OracleLibrary.currentCumulativePrices(address(pair)); 57 | uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired 58 | 59 | if (timeElapsed == 0) { 60 | // prevent divided by zero 61 | return; 62 | } 63 | 64 | // overflow is desired, casting never truncates 65 | // cumulative price is in (uq112x112 price * seconds) units so we simply wrap it after division by time elapsed 66 | price0Average = FixedPoint.uq112x112(uint224((price0Cumulative - price0CumulativeLast) / timeElapsed)); 67 | price1Average = FixedPoint.uq112x112(uint224((price1Cumulative - price1CumulativeLast) / timeElapsed)); 68 | 69 | price0CumulativeLast = price0Cumulative; 70 | price1CumulativeLast = price1Cumulative; 71 | blockTimestampLast = blockTimestamp; 72 | 73 | emit Updated(price0Cumulative, price1Cumulative); 74 | } 75 | 76 | // note this will always return 0 before update has been called successfully for the first time. 77 | function consult(address _token, uint256 _amountIn) external view returns (uint144 amountOut) { 78 | if (_token == token0) { 79 | amountOut = price0Average.mul(_amountIn).decode144(); 80 | } else { 81 | require(_token == token1, "Oracle: INVALID_TOKEN"); 82 | amountOut = price1Average.mul(_amountIn).decode144(); 83 | } 84 | } 85 | 86 | function twap(address _token, uint256 _amountIn) external view returns (uint144 _amountOut) { 87 | (uint256 price0Cumulative, uint256 price1Cumulative, uint32 blockTimestamp) = UniswapV2OracleLibrary.currentCumulativePrices(address(pair)); 88 | uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired 89 | if (_token == token0) { 90 | _amountOut = FixedPoint.uq112x112(uint224((price0Cumulative - price0CumulativeLast) / timeElapsed)).mul(_amountIn).decode144(); 91 | } else if (_token == token1) { 92 | _amountOut = FixedPoint.uq112x112(uint224((price1Cumulative - price1CumulativeLast) / timeElapsed)).mul(_amountIn).decode144(); 93 | } 94 | } 95 | 96 | event Updated(uint256 price0CumulativeLast, uint256 price1CumulativeLast); 97 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Grape Finance Solidity Contracts 2 | -Genesis pool contracts are in the distribution folder 3 | Many thanks to Tomb Finance and Basis Cash for their open source contributions 4 | Our code was forked and modified from Bomb.money contracts under MIT License 5 | -------------------------------------------------------------------------------- /SimpleERCFund.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 7 | 8 | import "./owner/Operator.sol"; 9 | import "./interfaces/ISimpleERCFund.sol"; 10 | 11 | contract SimpleERCFund is ISimpleERCFund, Operator { 12 | using SafeERC20 for IERC20; 13 | 14 | function deposit( 15 | address token, 16 | uint256 amount, 17 | string memory reason 18 | ) public override { 19 | IERC20(token).safeTransferFrom(msg.sender, address(this), amount); 20 | emit Deposit(msg.sender, now, reason); 21 | } 22 | 23 | function withdraw( 24 | address token, 25 | uint256 amount, 26 | address to, 27 | string memory reason 28 | ) public override onlyOperator { 29 | IERC20(token).safeTransfer(to, amount); 30 | emit Withdrawal(msg.sender, to, now, reason); 31 | } 32 | 33 | event Deposit(address indexed from, uint256 indexed at, string reason); 34 | event Withdrawal(address indexed from, address indexed to, uint256 indexed at, string reason); 35 | } 36 | -------------------------------------------------------------------------------- /TaxOffice.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "./owner/Operator.sol"; 6 | import "./interfaces/ITaxable.sol"; 7 | 8 | 9 | contract TaxOffice is Operator { 10 | address public grape; 11 | 12 | constructor(address _grape) public { 13 | require(_grape != address(0), "grape address cannot be 0"); 14 | grape = _grape; 15 | } 16 | 17 | function setTaxTiersTwap(uint8 _index, uint256 _value) public onlyOperator returns (bool) { 18 | return ITaxable(grape).setTaxTiersTwap(_index, _value); 19 | } 20 | 21 | function setTaxTiersRate(uint8 _index, uint256 _value) public onlyOperator returns (bool) { 22 | return ITaxable(grape).setTaxTiersRate(_index, _value); 23 | } 24 | 25 | function enableAutoCalculateTax() public onlyOperator { 26 | ITaxable(grape).enableAutoCalculateTax(); 27 | } 28 | 29 | function disableAutoCalculateTax() public onlyOperator { 30 | ITaxable(grape).disableAutoCalculateTax(); 31 | } 32 | 33 | function setTaxRate(uint256 _taxRate) public onlyOperator { 34 | ITaxable(grape).setTaxRate(_taxRate); 35 | } 36 | 37 | function setBurnThreshold(uint256 _burnThreshold) public onlyOperator { 38 | ITaxable(grape).setBurnThreshold(_burnThreshold); 39 | } 40 | 41 | function setTaxCollectorAddress(address _taxCollectorAddress) public onlyOperator { 42 | ITaxable(grape).setTaxCollectorAddress(_taxCollectorAddress); 43 | } 44 | 45 | function excludeAddressFromTax(address _address) external onlyOperator returns (bool) { 46 | return ITaxable(grape).excludeAddress(_address); 47 | } 48 | 49 | function includeAddressInTax(address _address) external onlyOperator returns (bool) { 50 | return ITaxable(grape).includeAddress(_address); 51 | } 52 | 53 | function setTaxableGrapeOracle(address _grapeOracle) external onlyOperator { 54 | ITaxable(grape).setGrapeOracle(_grapeOracle); 55 | } 56 | 57 | function transferTaxOffice(address _newTaxOffice) external onlyOperator { 58 | ITaxable(grape).setTaxOffice(_newTaxOffice); 59 | } 60 | } -------------------------------------------------------------------------------- /TaxOfficeV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | import "./owner/Operator.sol"; 7 | import "./interfaces/ITaxable.sol"; 8 | import "./interfaces/IUniswapV2Router.sol"; 9 | import "./interfaces/IERC20.sol"; 10 | 11 | contract TaxOfficeV2 is Operator { 12 | using SafeMath for uint256; 13 | 14 | address public grape = address(0x522348779DCb2911539e76A1042aA922F9C47Ee3); 15 | address public weth = address(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); 16 | address public uniRouter = address(0x10ED43C718714eb63d5aA57B78B54704E256024E); 17 | 18 | mapping(address => bool) public taxExclusionEnabled; 19 | 20 | function setTaxTiersTwap(uint8 _index, uint256 _value) public onlyOperator returns (bool) { 21 | return ITaxable(grape).setTaxTiersTwap(_index, _value); 22 | } 23 | 24 | function setTaxTiersRate(uint8 _index, uint256 _value) public onlyOperator returns (bool) { 25 | return ITaxable(grape).setTaxTiersRate(_index, _value); 26 | } 27 | 28 | function enableAutoCalculateTax() public onlyOperator { 29 | ITaxable(grape).enableAutoCalculateTax(); 30 | } 31 | 32 | function disableAutoCalculateTax() public onlyOperator { 33 | ITaxable(grape).disableAutoCalculateTax(); 34 | } 35 | 36 | function setTaxRate(uint256 _taxRate) public onlyOperator { 37 | ITaxable(grape).setTaxRate(_taxRate); 38 | } 39 | 40 | function setBurnThreshold(uint256 _burnThreshold) public onlyOperator { 41 | ITaxable(grape).setBurnThreshold(_burnThreshold); 42 | } 43 | 44 | function setTaxCollectorAddress(address _taxCollectorAddress) public onlyOperator { 45 | ITaxable(grape).setTaxCollectorAddress(_taxCollectorAddress); 46 | } 47 | 48 | function excludeAddressFromTax(address _address) external onlyOperator returns (bool) { 49 | return _excludeAddressFromTax(_address); 50 | } 51 | 52 | function _excludeAddressFromTax(address _address) private returns (bool) { 53 | if (!ITaxable(grape).isAddressExcluded(_address)) { 54 | return ITaxable(grape).excludeAddress(_address); 55 | } 56 | } 57 | 58 | function includeAddressInTax(address _address) external onlyOperator returns (bool) { 59 | return _includeAddressInTax(_address); 60 | } 61 | 62 | function _includeAddressInTax(address _address) private returns (bool) { 63 | if (ITaxable(grape).isAddressExcluded(_address)) { 64 | return ITaxable(grape).includeAddress(_address); 65 | } 66 | } 67 | 68 | function taxRate() external returns (uint256) { 69 | return ITaxable(grape).taxRate(); 70 | } 71 | 72 | function addLiquidityTaxFree( 73 | address token, 74 | uint256 amtGrape, 75 | uint256 amtToken, 76 | uint256 amtGrapeMin, 77 | uint256 amtTokenMin 78 | ) 79 | external 80 | returns ( 81 | uint256, 82 | uint256, 83 | uint256 84 | ) 85 | { 86 | require(amtGrape != 0 && amtToken != 0, "amounts can't be 0"); 87 | _excludeAddressFromTax(msg.sender); 88 | 89 | IERC20(grape).transferFrom(msg.sender, address(this), amtGrape); 90 | IERC20(token).transferFrom(msg.sender, address(this), amtToken); 91 | _approveTokenIfNeeded(grape, uniRouter); 92 | _approveTokenIfNeeded(token, uniRouter); 93 | 94 | _includeAddressInTax(msg.sender); 95 | 96 | uint256 resultAmtGrape; 97 | uint256 resultAmtToken; 98 | uint256 liquidity; 99 | (resultAmtGrape, resultAmtToken, liquidity) = IUniswapV2Router(uniRouter).addLiquidity( 100 | grape, 101 | token, 102 | amtGrape, 103 | amtToken, 104 | amtGrapeMin, 105 | amtTokenMin, 106 | msg.sender, 107 | block.timestamp 108 | ); 109 | 110 | if (amtGrape.sub(resultAmtGrape) > 0) { 111 | IERC20(grape).transfer(msg.sender, amtGrape.sub(resultAmtGrape)); 112 | } 113 | if (amtToken.sub(resultAmtToken) > 0) { 114 | IERC20(token).transfer(msg.sender, amtToken.sub(resultAmtToken)); 115 | } 116 | return (resultAmtGrape, resultAmtToken, liquidity); 117 | } 118 | 119 | function addLiquidityETHTaxFree( 120 | uint256 amtGrape, 121 | uint256 amtGrapeMin, 122 | uint256 amtEthMin 123 | ) 124 | external 125 | payable 126 | returns ( 127 | uint256, 128 | uint256, 129 | uint256 130 | ) 131 | { 132 | require(amtGrape != 0 && msg.value != 0, "amounts can't be 0"); 133 | _excludeAddressFromTax(msg.sender); 134 | 135 | IERC20(grape).transferFrom(msg.sender, address(this), amtGrape); 136 | _approveTokenIfNeeded(grape, uniRouter); 137 | 138 | _includeAddressInTax(msg.sender); 139 | 140 | uint256 resultAmtGrape; 141 | uint256 resultAmtEth; 142 | uint256 liquidity; 143 | (resultAmtGrape, resultAmtEth, liquidity) = IUniswapV2Router(uniRouter).addLiquidityETH{value: msg.value}( 144 | grape, 145 | amtGrape, 146 | amtGrapeMin, 147 | amtEthMin, 148 | msg.sender, 149 | block.timestamp 150 | ); 151 | 152 | if (amtGrape.sub(resultAmtGrape) > 0) { 153 | IERC20(grape).transfer(msg.sender, amtGrape.sub(resultAmtGrape)); 154 | } 155 | return (resultAmtGrape, resultAmtEth, liquidity); 156 | } 157 | 158 | function setTaxableGrapeOracle(address _grapeOracle) external onlyOperator { 159 | ITaxable(grape).setGrapeOracle(_grapeOracle); 160 | } 161 | 162 | function transferTaxOffice(address _newTaxOffice) external onlyOperator { 163 | ITaxable(grape).setTaxOffice(_newTaxOffice); 164 | } 165 | 166 | function taxFreeTransferFrom( 167 | address _sender, 168 | address _recipient, 169 | uint256 _amt 170 | ) external { 171 | require(taxExclusionEnabled[msg.sender], "Address not approved for tax free transfers"); 172 | _excludeAddressFromTax(_sender); 173 | IERC20(grape).transferFrom(_sender, _recipient, _amt); 174 | _includeAddressInTax(_sender); 175 | } 176 | 177 | function setTaxExclusionForAddress(address _address, bool _excluded) external onlyOperator { 178 | taxExclusionEnabled[_address] = _excluded; 179 | } 180 | 181 | function _approveTokenIfNeeded(address _token, address _router) private { 182 | if (IERC20(_token).allowance(address(this), _router) == 0) { 183 | IERC20(_token).approve(_router, type(uint256).max); 184 | } 185 | } 186 | } -------------------------------------------------------------------------------- /TaxOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | import "@openzeppelin/contracts/access/Ownable.sol"; 7 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 8 | 9 | contract TaxOracle is Ownable { 10 | using SafeMath for uint256; 11 | 12 | IERC20 public grape; 13 | IERC20 public mim; 14 | address public pair; 15 | 16 | constructor( 17 | address _grape, 18 | address _mim, 19 | address _pair 20 | ) public { 21 | require(_grape != address(0), "grape address cannot be 0"); 22 | require(_mim != address(0), "mim address cannot be 0"); 23 | require(_pair != address(0), "pair address cannot be 0"); 24 | grape = IERC20(_grape); 25 | mim = IERC20(_mim); 26 | pair = _pair; 27 | } 28 | 29 | function consult(address _token, uint256 _amountIn) external view returns (uint144 amountOut) { 30 | require(_token == address(grape), "token needs to be grape"); 31 | uint256 grapeBalance = grape.balanceOf(pair); 32 | uint256 mimBalance = mim.balanceOf(pair); 33 | return uint144(grapeBalance.mul(_amountIn).div(mimBalance)); 34 | } 35 | 36 | function getGrapeBalance() external view returns (uint256) { 37 | return grape.balanceOf(pair); 38 | } 39 | 40 | function getMimBalance() external view returns (uint256) { 41 | return mim.balanceOf(pair); 42 | } 43 | 44 | function getPrice() external view returns (uint256) { 45 | uint256 grapeBalance = grape.balanceOf(pair); 46 | uint256 mimBalance = mim.balanceOf(pair); 47 | return grapeBalance.mul(1e18).div(mimBalance); 48 | } 49 | 50 | 51 | function setGrape(address _grape) external onlyOwner { 52 | require(_grape != address(0), "grape address cannot be 0"); 53 | grape = IERC20(_grape); 54 | } 55 | 56 | function setMim(address _mim) external onlyOwner { 57 | require(_mim != address(0), "mim address cannot be 0"); 58 | mim = IERC20(_mim); 59 | } 60 | 61 | function setPair(address _pair) external onlyOwner { 62 | require(_pair != address(0), "pair address cannot be 0"); 63 | pair = _pair; 64 | } 65 | 66 | 67 | 68 | } -------------------------------------------------------------------------------- /Timelock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | /* 6 | * Copyright 2020 Compound Labs, Inc. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright notice, 15 | * this list of conditions and the following disclaimer in the documentation 16 | * and/or other materials provided with the distribution. 17 | * 18 | * 3. Neither the name of the copyright holder nor the names of its contributors 19 | * may be used to endorse or promote products derived from this software without 20 | * specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 24 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 26 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 27 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 29 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 31 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 32 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | import "@openzeppelin/contracts/math/SafeMath.sol"; 35 | 36 | contract Timelock { 37 | using SafeMath for uint256; 38 | 39 | event NewAdmin(address indexed newAdmin); 40 | event NewPendingAdmin(address indexed newPendingAdmin); 41 | event NewDelay(uint256 indexed newDelay); 42 | event CancelTransaction(bytes32 indexed txHash, address indexed target, uint256 value, string signature, bytes data, uint256 eta); 43 | event ExecuteTransaction(bytes32 indexed txHash, address indexed target, uint256 value, string signature, bytes data, uint256 eta); 44 | event QueueTransaction(bytes32 indexed txHash, address indexed target, uint256 value, string signature, bytes data, uint256 eta); 45 | 46 | uint256 public constant GRACE_PERIOD = 14 days; 47 | uint256 public constant MINIMUM_DELAY = 1 days; 48 | uint256 public constant MAXIMUM_DELAY = 30 days; 49 | 50 | address public admin; 51 | address public pendingAdmin; 52 | uint256 public delay; 53 | 54 | mapping(bytes32 => bool) public queuedTransactions; 55 | 56 | constructor(address admin_, uint256 delay_) public { 57 | require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay."); 58 | require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); 59 | 60 | admin = admin_; 61 | delay = delay_; 62 | } 63 | 64 | receive() external payable {} 65 | 66 | function setDelay(uint256 delay_) public { 67 | require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock."); 68 | require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay."); 69 | require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); 70 | delay = delay_; 71 | 72 | emit NewDelay(delay); 73 | } 74 | 75 | function acceptAdmin() public { 76 | require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin."); 77 | admin = msg.sender; 78 | pendingAdmin = address(0); 79 | 80 | emit NewAdmin(admin); 81 | } 82 | 83 | function setPendingAdmin(address pendingAdmin_) public { 84 | require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock."); 85 | pendingAdmin = pendingAdmin_; 86 | 87 | emit NewPendingAdmin(pendingAdmin); 88 | } 89 | 90 | function queueTransaction( 91 | address target, 92 | uint256 value, 93 | string memory signature, 94 | bytes memory data, 95 | uint256 eta 96 | ) public returns (bytes32) { 97 | require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin."); 98 | require(eta >= getBlockTimestamp().add(delay), "Timelock::queueTransaction: Estimated execution block must satisfy delay."); 99 | 100 | bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); 101 | queuedTransactions[txHash] = true; 102 | 103 | emit QueueTransaction(txHash, target, value, signature, data, eta); 104 | return txHash; 105 | } 106 | 107 | function cancelTransaction( 108 | address target, 109 | uint256 value, 110 | string memory signature, 111 | bytes memory data, 112 | uint256 eta 113 | ) public { 114 | require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin."); 115 | 116 | bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); 117 | queuedTransactions[txHash] = false; 118 | 119 | emit CancelTransaction(txHash, target, value, signature, data, eta); 120 | } 121 | 122 | function executeTransaction( 123 | address target, 124 | uint256 value, 125 | string memory signature, 126 | bytes memory data, 127 | uint256 eta 128 | ) public payable returns (bytes memory) { 129 | require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin."); 130 | 131 | bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); 132 | require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued."); 133 | require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock."); 134 | require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale."); 135 | 136 | queuedTransactions[txHash] = false; 137 | 138 | bytes memory callData; 139 | 140 | if (bytes(signature).length == 0) { 141 | callData = data; 142 | } else { 143 | callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data); 144 | } 145 | 146 | // solium-disable-next-line security/no-call-value 147 | (bool success, bytes memory returnData) = target.call{value: value}(callData); 148 | require(success, "Timelock::executeTransaction: Transaction execution reverted."); 149 | 150 | emit ExecuteTransaction(txHash, target, value, signature, data, eta); 151 | 152 | return returnData; 153 | } 154 | 155 | function getBlockTimestamp() internal view returns (uint256) { 156 | // solium-disable-next-line security/no-block-members 157 | return block.timestamp; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /Treasury.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/math/Math.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 8 | import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; 9 | 10 | import "./lib/Babylonian.sol"; 11 | import "./owner/Operator.sol"; 12 | import "./utils/ContractGuard.sol"; 13 | import "./interfaces/IBasisAsset.sol"; 14 | import "./interfaces/IOracle.sol"; 15 | import "./interfaces/IBoardroom.sol"; 16 | 17 | contract Treasury is ContractGuard { 18 | using SafeERC20 for IERC20; 19 | using Address for address; 20 | using SafeMath for uint256; 21 | 22 | /* ========= CONSTANT VARIABLES ======== */ 23 | 24 | uint256 public constant PERIOD = 6 hours; 25 | 26 | /* ========== STATE VARIABLES ========== */ 27 | 28 | // governance 29 | address public operator; 30 | 31 | // flags 32 | bool public initialized = false; 33 | 34 | // epoch 35 | uint256 public startTime; 36 | uint256 public epoch = 0; 37 | uint256 public epochSupplyContractionLeft = 0; 38 | 39 | // exclusions from total supply 40 | address[] public excludedFromTotalSupply = [ 41 | address(0xB7e1E341b2CBCc7d1EdF4DC6E5e962aE5C621ca5), // GrapeGenesisRewardPool 42 | address(0x04b79c851ed1A36549C6151189c79EC0eaBca745) // GrapeRewardPool 43 | ]; 44 | 45 | // core components 46 | address public grape; 47 | address public gbond; 48 | address public wine; 49 | 50 | address public boardroom; 51 | address public grapeOracle; 52 | 53 | // price 54 | uint256 public grapePriceOne; 55 | uint256 public grapePriceCeiling; 56 | 57 | uint256 public seigniorageSaved; 58 | 59 | uint256[] public supplyTiers; 60 | uint256[] public maxExpansionTiers; 61 | 62 | uint256 public maxSupplyExpansionPercent; 63 | uint256 public bondDepletionFloorPercent; 64 | uint256 public seigniorageExpansionFloorPercent; 65 | uint256 public maxSupplyContractionPercent; 66 | uint256 public maxDebtRatioPercent; 67 | 68 | // 28 first epochs (1 week) with 4.5% expansion regardless of GRAPE price 69 | uint256 public bootstrapEpochs; 70 | uint256 public bootstrapSupplyExpansionPercent; 71 | 72 | /* =================== Added variables =================== */ 73 | uint256 public previousEpochGrapePrice; 74 | uint256 public maxDiscountRate; // when purchasing bond 75 | uint256 public maxPremiumRate; // when redeeming bond 76 | uint256 public discountPercent; 77 | uint256 public premiumThreshold; 78 | uint256 public premiumPercent; 79 | uint256 public mintingFactorForPayingDebt; // print extra GRAPE during debt phase 80 | 81 | address public daoFund; 82 | uint256 public daoFundSharedPercent; 83 | 84 | address public devFund; 85 | uint256 public devFundSharedPercent; 86 | 87 | /* =================== Events =================== */ 88 | 89 | event Initialized(address indexed executor, uint256 at); 90 | event BurnedBonds(address indexed from, uint256 bondAmount); 91 | event RedeemedBonds(address indexed from, uint256 grapeAmount, uint256 bondAmount); 92 | event BoughtBonds(address indexed from, uint256 grapeAmount, uint256 bondAmount); 93 | event TreasuryFunded(uint256 timestamp, uint256 seigniorage); 94 | event BoardroomFunded(uint256 timestamp, uint256 seigniorage); 95 | event DaoFundFunded(uint256 timestamp, uint256 seigniorage); 96 | event DevFundFunded(uint256 timestamp, uint256 seigniorage); 97 | 98 | /* =================== Modifier =================== */ 99 | 100 | modifier onlyOperator() { 101 | require(operator == msg.sender, "Treasury: caller is not the operator"); 102 | _; 103 | } 104 | 105 | modifier checkCondition() { 106 | require(now >= startTime, "Treasury: not started yet"); 107 | 108 | _; 109 | } 110 | 111 | modifier checkEpoch() { 112 | require(now >= nextEpochPoint(), "Treasury: not opened yet"); 113 | 114 | _; 115 | 116 | epoch = epoch.add(1); 117 | epochSupplyContractionLeft = (getGrapePrice() > grapePriceCeiling) ? 0 : getGrapeCirculatingSupply().mul(maxSupplyContractionPercent).div(10000); 118 | } 119 | 120 | modifier checkOperator() { 121 | require( 122 | IBasisAsset(grape).operator() == address(this) && 123 | IBasisAsset(gbond).operator() == address(this) && 124 | IBasisAsset(wine).operator() == address(this) && 125 | Operator(boardroom).operator() == address(this), 126 | "Treasury: need more permission" 127 | ); 128 | 129 | _; 130 | } 131 | 132 | modifier notInitialized() { 133 | require(!initialized, "Treasury: already initialized"); 134 | 135 | _; 136 | } 137 | 138 | /* ========== VIEW FUNCTIONS ========== */ 139 | 140 | function isInitialized() public view returns (bool) { 141 | return initialized; 142 | } 143 | 144 | // epoch 145 | function nextEpochPoint() public view returns (uint256) { 146 | return startTime.add(epoch.mul(PERIOD)); 147 | } 148 | 149 | // oracle 150 | function getGrapePrice() public view returns (uint256 grapePrice) { 151 | try IOracle(grapeOracle).consult(grape, 1e18) returns (uint144 price) { 152 | return uint256(price); 153 | } catch { 154 | revert("Treasury: failed to consult grape price from the oracle"); 155 | } 156 | } 157 | 158 | function getGrapeUpdatedPrice() public view returns (uint256 _grapePrice) { 159 | try IOracle(grapeOracle).twap(grape, 1e18) returns (uint144 price) { 160 | return uint256(price); 161 | } catch { 162 | revert("Treasury: failed to consult grape price from the oracle"); 163 | } 164 | } 165 | 166 | // budget 167 | function getReserve() public view returns (uint256) { 168 | return seigniorageSaved; 169 | } 170 | 171 | function getBurnableGrapeLeft() public view returns (uint256 _burnableGrapeLeft) { 172 | uint256 _grapePrice = getGrapePrice(); 173 | if (_grapePrice <= grapePriceOne) { 174 | uint256 _grapeSupply = getGrapeCirculatingSupply(); 175 | uint256 _bondMaxSupply = _grapeSupply.mul(maxDebtRatioPercent).div(10000); 176 | uint256 _bondSupply = IERC20(gbond).totalSupply(); 177 | if (_bondMaxSupply > _bondSupply) { 178 | uint256 _maxMintableBond = _bondMaxSupply.sub(_bondSupply); 179 | uint256 _maxBurnableGrape = _maxMintableBond.mul(_grapePrice).div(1e18); 180 | _burnableGrapeLeft = Math.min(epochSupplyContractionLeft, _maxBurnableGrape); 181 | } 182 | } 183 | } 184 | 185 | function getRedeemableBonds() public view returns (uint256 _redeemableBonds) { 186 | uint256 _grapePrice = getGrapePrice(); 187 | if (_grapePrice > grapePriceCeiling) { 188 | uint256 _totalGrape = IERC20(grape).balanceOf(address(this)); 189 | uint256 _rate = getBondPremiumRate(); 190 | if (_rate > 0) { 191 | _redeemableBonds = _totalGrape.mul(1e18).div(_rate); 192 | } 193 | } 194 | } 195 | 196 | function getBondDiscountRate() public view returns (uint256 _rate) { 197 | uint256 _grapePrice = getGrapePrice(); 198 | if (_grapePrice <= grapePriceOne) { 199 | if (discountPercent == 0) { 200 | // no discount 201 | _rate = grapePriceOne; 202 | } else { 203 | uint256 _bondAmount = grapePriceOne.mul(1e18).div(_grapePrice); // to burn 1 GRAPE 204 | uint256 _discountAmount = _bondAmount.sub(grapePriceOne).mul(discountPercent).div(10000); 205 | _rate = grapePriceOne.add(_discountAmount); 206 | if (maxDiscountRate > 0 && _rate > maxDiscountRate) { 207 | _rate = maxDiscountRate; 208 | } 209 | } 210 | } 211 | } 212 | 213 | function getBondPremiumRate() public view returns (uint256 _rate) { 214 | uint256 _grapePrice = getGrapePrice(); 215 | if (_grapePrice > grapePriceCeiling) { 216 | uint256 _grapePricePremiumThreshold = grapePriceOne.mul(premiumThreshold).div(100); 217 | if (_grapePrice >= _grapePricePremiumThreshold) { 218 | //Price > 1.10 219 | uint256 _premiumAmount = _grapePrice.sub(grapePriceOne).mul(premiumPercent).div(10000); 220 | _rate = grapePriceOne.add(_premiumAmount); 221 | if (maxPremiumRate > 0 && _rate > maxPremiumRate) { 222 | _rate = maxPremiumRate; 223 | } 224 | } else { 225 | // no premium bonus 226 | _rate = grapePriceOne; 227 | } 228 | } 229 | } 230 | 231 | /* ========== GOVERNANCE ========== */ 232 | 233 | function initialize( 234 | address _grape, 235 | address _gbond, 236 | address _wine, 237 | address _grapeOracle, 238 | address _boardroom, 239 | uint256 _startTime 240 | ) public notInitialized { 241 | grape = _grape; 242 | gbond = _gbond; 243 | wine = _wine; 244 | grapeOracle = _grapeOracle; 245 | boardroom = _boardroom; 246 | startTime = _startTime; 247 | 248 | grapePriceOne = 10**18; // This is to allow a PEG of 1 GRAPE per MIM 249 | grapePriceCeiling = grapePriceOne.mul(101).div(100); 250 | 251 | // Dynamic max expansion percent 252 | supplyTiers = [0 ether, 10000 ether, 20000 ether, 30000 ether, 40000 ether, 50000 ether, 100000 ether, 200000 ether, 500000 ether]; 253 | maxExpansionTiers = [450, 400, 350, 300, 250, 200, 150, 125, 100]; 254 | 255 | maxSupplyExpansionPercent = 400; // Upto 4.0% supply for expansion 256 | 257 | bondDepletionFloorPercent = 10000; // 100% of Bond supply for depletion floor 258 | seigniorageExpansionFloorPercent = 3500; // At least 35% of expansion reserved for boardroom 259 | maxSupplyContractionPercent = 300; // Upto 3.0% supply for contraction (to burn GRAPE and mint GBOND) 260 | maxDebtRatioPercent = 4000; // Upto 40% supply of GBOND to purchase 261 | 262 | premiumThreshold = 110; 263 | premiumPercent = 7000; 264 | 265 | // First 14 epochs with 4.5% expansion 266 | bootstrapEpochs = 14; 267 | bootstrapSupplyExpansionPercent = 450; 268 | 269 | // set seigniorageSaved to it's balance 270 | seigniorageSaved = IERC20(grape).balanceOf(address(this)); 271 | 272 | initialized = true; 273 | operator = msg.sender; 274 | emit Initialized(msg.sender, block.number); 275 | } 276 | 277 | function setOperator(address _operator) external onlyOperator { 278 | operator = _operator; 279 | } 280 | 281 | function setBoardroom(address _boardroom) external onlyOperator { 282 | boardroom = _boardroom; 283 | } 284 | 285 | function setGrapeOracle(address _grapeOracle) external onlyOperator { 286 | grapeOracle = _grapeOracle; 287 | } 288 | 289 | function setGrapePriceCeiling(uint256 _grapePriceCeiling) external onlyOperator { 290 | require(_grapePriceCeiling >= grapePriceOne && _grapePriceCeiling <= grapePriceOne.mul(120).div(100), "out of range"); // [$1.0, $1.2] 291 | grapePriceCeiling = _grapePriceCeiling; 292 | } 293 | 294 | function setMaxSupplyExpansionPercents(uint256 _maxSupplyExpansionPercent) external onlyOperator { 295 | require(_maxSupplyExpansionPercent >= 10 && _maxSupplyExpansionPercent <= 1000, "_maxSupplyExpansionPercent: out of range"); // [0.1%, 10%] 296 | maxSupplyExpansionPercent = _maxSupplyExpansionPercent; 297 | } 298 | 299 | function setSupplyTiersEntry(uint8 _index, uint256 _value) external onlyOperator returns (bool) { 300 | require(_index >= 0, "Index has to be higher than 0"); 301 | require(_index < 9, "Index has to be lower than count of tiers"); 302 | if (_index > 0) { 303 | require(_value > supplyTiers[_index - 1]); 304 | } 305 | if (_index < 8) { 306 | require(_value < supplyTiers[_index + 1]); 307 | } 308 | supplyTiers[_index] = _value; 309 | return true; 310 | } 311 | 312 | function setMaxExpansionTiersEntry(uint8 _index, uint256 _value) external onlyOperator returns (bool) { 313 | require(_index >= 0, "Index has to be higher than 0"); 314 | require(_index < 9, "Index has to be lower than count of tiers"); 315 | require(_value >= 10 && _value <= 1000, "_value: out of range"); // [0.1%, 10%] 316 | maxExpansionTiers[_index] = _value; 317 | return true; 318 | } 319 | 320 | function setBondDepletionFloorPercent(uint256 _bondDepletionFloorPercent) external onlyOperator { 321 | require(_bondDepletionFloorPercent >= 500 && _bondDepletionFloorPercent <= 10000, "out of range"); // [5%, 100%] 322 | bondDepletionFloorPercent = _bondDepletionFloorPercent; 323 | } 324 | 325 | function setMaxSupplyContractionPercent(uint256 _maxSupplyContractionPercent) external onlyOperator { 326 | require(_maxSupplyContractionPercent >= 100 && _maxSupplyContractionPercent <= 1500, "out of range"); // [0.1%, 15%] 327 | maxSupplyContractionPercent = _maxSupplyContractionPercent; 328 | } 329 | 330 | function setMaxDebtRatioPercent(uint256 _maxDebtRatioPercent) external onlyOperator { 331 | require(_maxDebtRatioPercent >= 1000 && _maxDebtRatioPercent <= 10000, "out of range"); // [10%, 100%] 332 | maxDebtRatioPercent = _maxDebtRatioPercent; 333 | } 334 | 335 | function setBootstrap(uint256 _bootstrapEpochs, uint256 _bootstrapSupplyExpansionPercent) external onlyOperator { 336 | require(_bootstrapEpochs <= 120, "_bootstrapEpochs: out of range"); // <= 1 month 337 | require(_bootstrapSupplyExpansionPercent >= 100 && _bootstrapSupplyExpansionPercent <= 1000, "_bootstrapSupplyExpansionPercent: out of range"); // [1%, 10%] 338 | bootstrapEpochs = _bootstrapEpochs; 339 | bootstrapSupplyExpansionPercent = _bootstrapSupplyExpansionPercent; 340 | } 341 | 342 | function setExtraFunds( 343 | address _daoFund, 344 | uint256 _daoFundSharedPercent, 345 | address _devFund, 346 | uint256 _devFundSharedPercent 347 | ) external onlyOperator { 348 | require(_daoFund != address(0), "zero"); 349 | require(_daoFundSharedPercent <= 2500, "out of range"); // <= 25% 350 | require(_devFund != address(0), "zero"); 351 | require(_devFundSharedPercent <= 500, "out of range"); // <= 5% 352 | daoFund = _daoFund; 353 | daoFundSharedPercent = _daoFundSharedPercent; 354 | devFund = _devFund; 355 | devFundSharedPercent = _devFundSharedPercent; 356 | } 357 | 358 | function setMaxDiscountRate(uint256 _maxDiscountRate) external onlyOperator { 359 | maxDiscountRate = _maxDiscountRate; 360 | } 361 | 362 | function setMaxPremiumRate(uint256 _maxPremiumRate) external onlyOperator { 363 | maxPremiumRate = _maxPremiumRate; 364 | } 365 | 366 | function setDiscountPercent(uint256 _discountPercent) external onlyOperator { 367 | require(_discountPercent <= 20000, "_discountPercent is over 200%"); 368 | discountPercent = _discountPercent; 369 | } 370 | 371 | function setPremiumThreshold(uint256 _premiumThreshold) external onlyOperator { 372 | require(_premiumThreshold >= grapePriceCeiling, "_premiumThreshold exceeds grapePriceCeiling"); 373 | require(_premiumThreshold <= 150, "_premiumThreshold is higher than 1.5"); 374 | premiumThreshold = _premiumThreshold; 375 | } 376 | 377 | function setPremiumPercent(uint256 _premiumPercent) external onlyOperator { 378 | require(_premiumPercent <= 20000, "_premiumPercent is over 200%"); 379 | premiumPercent = _premiumPercent; 380 | } 381 | 382 | function setMintingFactorForPayingDebt(uint256 _mintingFactorForPayingDebt) external onlyOperator { 383 | require(_mintingFactorForPayingDebt >= 10000 && _mintingFactorForPayingDebt <= 20000, "_mintingFactorForPayingDebt: out of range"); // [100%, 200%] 384 | mintingFactorForPayingDebt = _mintingFactorForPayingDebt; 385 | } 386 | 387 | /* ========== MUTABLE FUNCTIONS ========== */ 388 | 389 | function _updateGrapePrice() internal { 390 | try IOracle(grapeOracle).update() {} catch {} 391 | } 392 | 393 | function getGrapeCirculatingSupply() public view returns (uint256) { 394 | IERC20 grapeErc20 = IERC20(grape); 395 | uint256 totalSupply = grapeErc20.totalSupply(); 396 | uint256 balanceExcluded = 0; 397 | for (uint8 entryId = 0; entryId < excludedFromTotalSupply.length; ++entryId) { 398 | balanceExcluded = balanceExcluded.add(grapeErc20.balanceOf(excludedFromTotalSupply[entryId])); 399 | } 400 | return totalSupply.sub(balanceExcluded); 401 | } 402 | 403 | function buyBonds(uint256 _grapeAmount, uint256 targetPrice) external onlyOneBlock checkCondition checkOperator { 404 | require(_grapeAmount > 0, "Treasury: cannot purchase bonds with zero amount"); 405 | 406 | uint256 grapePrice = getGrapePrice(); 407 | require(grapePrice == targetPrice, "Treasury: GRAPE price moved"); 408 | require( 409 | grapePrice < grapePriceOne, // price < $1 410 | "Treasury: grapePrice not eligible for bond purchase" 411 | ); 412 | 413 | require(_grapeAmount <= epochSupplyContractionLeft, "Treasury: not enough bond left to purchase"); 414 | 415 | uint256 _rate = getBondDiscountRate(); 416 | require(_rate > 0, "Treasury: invalid bond rate"); 417 | 418 | uint256 _bondAmount = _grapeAmount.mul(_rate).div(1e18); 419 | uint256 grapeSupply = getGrapeCirculatingSupply(); 420 | uint256 newBondSupply = IERC20(gbond).totalSupply().add(_bondAmount); 421 | require(newBondSupply <= grapeSupply.mul(maxDebtRatioPercent).div(10000), "over max debt ratio"); 422 | 423 | IBasisAsset(grape).burnFrom(msg.sender, _grapeAmount); 424 | IBasisAsset(gbond).mint(msg.sender, _bondAmount); 425 | 426 | epochSupplyContractionLeft = epochSupplyContractionLeft.sub(_grapeAmount); 427 | _updateGrapePrice(); 428 | 429 | emit BoughtBonds(msg.sender, _grapeAmount, _bondAmount); 430 | } 431 | 432 | function redeemBonds(uint256 _bondAmount, uint256 targetPrice) external onlyOneBlock checkCondition checkOperator { 433 | require(_bondAmount > 0, "Treasury: cannot redeem bonds with zero amount"); 434 | 435 | uint256 grapePrice = getGrapePrice(); 436 | require(grapePrice == targetPrice, "Treasury: GRAPE price moved"); 437 | require( 438 | grapePrice > grapePriceCeiling, // price > $1.01 439 | "Treasury: grapePrice not eligible for bond purchase" 440 | ); 441 | 442 | uint256 _rate = getBondPremiumRate(); 443 | require(_rate > 0, "Treasury: invalid bond rate"); 444 | 445 | uint256 _grapeAmount = _bondAmount.mul(_rate).div(1e18); 446 | require(IERC20(grape).balanceOf(address(this)) >= _grapeAmount, "Treasury: treasury has no more budget"); 447 | 448 | seigniorageSaved = seigniorageSaved.sub(Math.min(seigniorageSaved, _grapeAmount)); 449 | 450 | IBasisAsset(gbond).burnFrom(msg.sender, _bondAmount); 451 | IERC20(grape).safeTransfer(msg.sender, _grapeAmount); 452 | 453 | _updateGrapePrice(); 454 | 455 | emit RedeemedBonds(msg.sender, _grapeAmount, _bondAmount); 456 | } 457 | 458 | function _sendToBoardroom(uint256 _amount) internal { 459 | IBasisAsset(grape).mint(address(this), _amount); 460 | 461 | uint256 _daoFundSharedAmount = 0; 462 | if (daoFundSharedPercent > 0) { 463 | _daoFundSharedAmount = _amount.mul(daoFundSharedPercent).div(10000); 464 | IERC20(grape).transfer(daoFund, _daoFundSharedAmount); 465 | emit DaoFundFunded(now, _daoFundSharedAmount); 466 | } 467 | 468 | uint256 _devFundSharedAmount = 0; 469 | if (devFundSharedPercent > 0) { 470 | _devFundSharedAmount = _amount.mul(devFundSharedPercent).div(10000); 471 | IERC20(grape).transfer(devFund, _devFundSharedAmount); 472 | emit DevFundFunded(now, _devFundSharedAmount); 473 | } 474 | 475 | _amount = _amount.sub(_daoFundSharedAmount).sub(_devFundSharedAmount); 476 | 477 | IERC20(grape).safeApprove(boardroom, 0); 478 | IERC20(grape).safeApprove(boardroom, _amount); 479 | IBoardroom(boardroom).allocateSeigniorage(_amount); 480 | emit BoardroomFunded(now, _amount); 481 | } 482 | 483 | function _calculateMaxSupplyExpansionPercent(uint256 _grapeSupply) internal returns (uint256) { 484 | for (uint8 tierId = 8; tierId >= 0; --tierId) { 485 | if (_grapeSupply >= supplyTiers[tierId]) { 486 | maxSupplyExpansionPercent = maxExpansionTiers[tierId]; 487 | break; 488 | } 489 | } 490 | return maxSupplyExpansionPercent; 491 | } 492 | 493 | function allocateSeigniorage() external onlyOneBlock checkCondition checkEpoch checkOperator { 494 | _updateGrapePrice(); 495 | previousEpochGrapePrice = getGrapePrice(); 496 | uint256 grapeSupply = getGrapeCirculatingSupply().sub(seigniorageSaved); 497 | if (epoch < bootstrapEpochs) { 498 | // 28 first epochs with 4.5% expansion 499 | _sendToBoardroom(grapeSupply.mul(bootstrapSupplyExpansionPercent).div(10000)); 500 | } else { 501 | if (previousEpochGrapePrice > grapePriceCeiling) { 502 | // Expansion ($GRAPE Price > 1 $MIM): there is some seigniorage to be allocated 503 | uint256 bondSupply = IERC20(gbond).totalSupply(); 504 | uint256 _percentage = previousEpochGrapePrice.sub(grapePriceOne); 505 | uint256 _savedForBond; 506 | uint256 _savedForBoardroom; 507 | uint256 _mse = _calculateMaxSupplyExpansionPercent(grapeSupply).mul(1e14); 508 | if (_percentage > _mse) { 509 | _percentage = _mse; 510 | } 511 | if (seigniorageSaved >= bondSupply.mul(bondDepletionFloorPercent).div(10000)) { 512 | // saved enough to pay debt, mint as usual rate 513 | _savedForBoardroom = grapeSupply.mul(_percentage).div(1e18); 514 | } else { 515 | // have not saved enough to pay debt, mint more 516 | uint256 _seigniorage = grapeSupply.mul(_percentage).div(1e18); 517 | _savedForBoardroom = _seigniorage.mul(seigniorageExpansionFloorPercent).div(10000); 518 | _savedForBond = _seigniorage.sub(_savedForBoardroom); 519 | if (mintingFactorForPayingDebt > 0) { 520 | _savedForBond = _savedForBond.mul(mintingFactorForPayingDebt).div(10000); 521 | } 522 | } 523 | if (_savedForBoardroom > 0) { 524 | _sendToBoardroom(_savedForBoardroom); 525 | } 526 | if (_savedForBond > 0) { 527 | seigniorageSaved = seigniorageSaved.add(_savedForBond); 528 | IBasisAsset(grape).mint(address(this), _savedForBond); 529 | emit TreasuryFunded(now, _savedForBond); 530 | } 531 | } 532 | } 533 | } 534 | 535 | function governanceRecoverUnsupported( 536 | IERC20 _token, 537 | uint256 _amount, 538 | address _to 539 | ) external onlyOperator { 540 | // do not allow to drain core tokens 541 | require(address(_token) != address(grape), "grape"); 542 | require(address(_token) != address(gbond), "bond"); 543 | require(address(_token) != address(wine), "share"); 544 | _token.safeTransfer(_to, _amount); 545 | } 546 | 547 | function boardroomSetOperator(address _operator) external onlyOperator { 548 | IBoardroom(boardroom).setOperator(_operator); 549 | } 550 | 551 | function boardroomSetLockUp(uint256 _withdrawLockupEpochs, uint256 _rewardLockupEpochs) external onlyOperator { 552 | IBoardroom(boardroom).setLockUp(_withdrawLockupEpochs, _rewardLockupEpochs); 553 | } 554 | 555 | function boardroomAllocateSeigniorage(uint256 amount) external onlyOperator { 556 | IBoardroom(boardroom).allocateSeigniorage(amount); 557 | } 558 | 559 | function boardroomGovernanceRecoverUnsupported( 560 | address _token, 561 | uint256 _amount, 562 | address _to 563 | ) external onlyOperator { 564 | IBoardroom(boardroom).governanceRecoverUnsupported(_token, _amount, _to); 565 | } 566 | } 567 | -------------------------------------------------------------------------------- /Wine.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol"; 7 | 8 | import "./owner/Operator.sol"; 9 | 10 | contract Wine is ERC20Burnable, Operator { 11 | using SafeMath for uint256; 12 | 13 | // TOTAL MAX SUPPLY = 50,000 WINE 14 | uint256 public constant FARMING_POOL_REWARD_ALLOCATION = 41000 ether; 15 | uint256 public constant COMMUNITY_FUND_POOL_ALLOCATION = 4500 ether; 16 | uint256 public constant DEV_FUND_POOL_ALLOCATION = 4500 ether; 17 | 18 | uint256 public constant VESTING_DURATION = 300 days; 19 | uint256 public startTime; 20 | uint256 public endTime; 21 | 22 | uint256 public communityFundRewardRate; 23 | uint256 public devFundRewardRate; 24 | 25 | address public communityFund; 26 | address public devFund; 27 | 28 | uint256 public communityFundLastClaimed; 29 | uint256 public devFundLastClaimed; 30 | 31 | bool public rewardPoolDistributed = false; 32 | 33 | constructor(uint256 _startTime, address _communityFund, address _devFund) public ERC20("Wine Shares", "WINE") { 34 | _mint(msg.sender, 1 ether); // mint 1 GRAPE Share for initial pools deployment 35 | 36 | startTime = _startTime; 37 | endTime = startTime + VESTING_DURATION; 38 | 39 | communityFundLastClaimed = startTime; 40 | devFundLastClaimed = startTime; 41 | 42 | communityFundRewardRate = COMMUNITY_FUND_POOL_ALLOCATION.div(VESTING_DURATION); 43 | devFundRewardRate = DEV_FUND_POOL_ALLOCATION.div(VESTING_DURATION); 44 | 45 | require(_devFund != address(0), "Address cannot be 0"); 46 | devFund = _devFund; 47 | 48 | require(_communityFund != address(0), "Address cannot be 0"); 49 | communityFund = _communityFund; 50 | } 51 | 52 | function setTreasuryFund(address _communityFund) external { 53 | require(msg.sender == devFund, "!dev"); 54 | communityFund = _communityFund; 55 | } 56 | 57 | function setDevFund(address _devFund) external { 58 | require(msg.sender == devFund, "!dev"); 59 | require(_devFund != address(0), "zero"); 60 | devFund = _devFund; 61 | } 62 | 63 | function unclaimedTreasuryFund() public view returns (uint256 _pending) { 64 | uint256 _now = block.timestamp; 65 | if (_now > endTime) _now = endTime; 66 | if (communityFundLastClaimed >= _now) return 0; 67 | _pending = _now.sub(communityFundLastClaimed).mul(communityFundRewardRate); 68 | } 69 | 70 | function unclaimedDevFund() public view returns (uint256 _pending) { 71 | uint256 _now = block.timestamp; 72 | if (_now > endTime) _now = endTime; 73 | if (devFundLastClaimed >= _now) return 0; 74 | _pending = _now.sub(devFundLastClaimed).mul(devFundRewardRate); 75 | } 76 | 77 | /** 78 | * @dev Claim pending rewards to community and dev fund 79 | */ 80 | function claimRewards() external { 81 | uint256 _pending = unclaimedTreasuryFund(); 82 | if (_pending > 0 && communityFund != address(0)) { 83 | _mint(communityFund, _pending); 84 | communityFundLastClaimed = block.timestamp; 85 | } 86 | _pending = unclaimedDevFund(); 87 | if (_pending > 0 && devFund != address(0)) { 88 | _mint(devFund, _pending); 89 | devFundLastClaimed = block.timestamp; 90 | } 91 | } 92 | 93 | /** 94 | * @notice distribute to reward pool (only once) 95 | */ 96 | function distributeReward(address _farmingIncentiveFund) external onlyOperator { 97 | require(!rewardPoolDistributed, "only can distribute once"); 98 | require(_farmingIncentiveFund != address(0), "!_farmingIncentiveFund"); 99 | rewardPoolDistributed = true; 100 | _mint(_farmingIncentiveFund, FARMING_POOL_REWARD_ALLOCATION); 101 | } 102 | 103 | function burn(uint256 amount) public override { 104 | super.burn(amount); 105 | } 106 | 107 | function governanceRecoverUnsupported( 108 | IERC20 _token, 109 | uint256 _amount, 110 | address _to 111 | ) external onlyOperator { 112 | _token.transfer(_to, _amount); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /distribution/GrapeGenesisRewardPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 7 | import "@openzeppelin/contracts/math/SafeMath.sol"; 8 | 9 | 10 | // Note that this pool has no minter key of GRAPE (rewards). 11 | // Instead, the governance will call GRAPE distributeReward method and send reward to this pool at the beginning. 12 | contract GrapeGenesisRewardPool { 13 | using SafeMath for uint256; 14 | using SafeERC20 for IERC20; 15 | 16 | // governance 17 | address public operator; 18 | 19 | // Info of each user. 20 | struct UserInfo { 21 | uint256 amount; // How many tokens the user has provided. 22 | uint256 rewardDebt; // Reward debt. See explanation below. 23 | } 24 | 25 | // Info of each pool. 26 | struct PoolInfo { 27 | IERC20 token; // Address of LP token contract. 28 | uint256 allocPoint; // How many allocation points assigned to this pool. GRAPE to distribute. 29 | uint256 lastRewardTime; // Last time that GRAPE distribution occurs. 30 | uint256 accGrapePerShare; // Accumulated GRAPE per share, times 1e18. See below. 31 | bool isStarted; // if lastRewardBlock has passed 32 | } 33 | 34 | IERC20 public grape; 35 | address public mim; 36 | 37 | // Info of each pool. 38 | PoolInfo[] public poolInfo; 39 | 40 | // Info of each user that stakes LP tokens. 41 | mapping(uint256 => mapping(address => UserInfo)) public userInfo; 42 | 43 | // Total allocation points. Must be the sum of all allocation points in all pools. 44 | uint256 public totalAllocPoint = 0; 45 | 46 | // The time when GRAPE mining starts. 47 | uint256 public poolStartTime; 48 | 49 | // The time when GRAPE mining ends. 50 | uint256 public poolEndTime; 51 | 52 | // TESTNET 53 | uint256 public grapePerSecond = 0.66667 ether; // 2400 GRAPE / (1h * 60min * 60s) 54 | uint256 public runningTime = 1 hours; // 1 hours 55 | uint256 public constant TOTAL_REWARDS = 2400 ether; 56 | // END TESTNET 57 | 58 | // MAINNET 59 | //uint256 public grapePerSecond = 0.02777 ether; // 2400 GRAPE / (24h * 60min * 60s) 60 | //uint256 public runningTime = 1 days; // 1 days 61 | //uint256 public constant TOTAL_REWARDS = 2400 ether; 62 | // END MAINNET 63 | 64 | event Deposit(address indexed user, uint256 indexed pid, uint256 amount); 65 | event Withdraw(address indexed user, uint256 indexed pid, uint256 amount); 66 | event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount); 67 | event RewardPaid(address indexed user, uint256 amount); 68 | 69 | constructor( 70 | address _grape, 71 | address _mim, 72 | uint256 _poolStartTime 73 | ) public { 74 | require(block.timestamp < _poolStartTime, "late"); 75 | if (_grape != address(0)) grape = IERC20(_grape); 76 | if (_mim != address(0)) mim = _mim; 77 | poolStartTime = _poolStartTime; 78 | poolEndTime = poolStartTime + runningTime; 79 | operator = msg.sender; 80 | } 81 | 82 | modifier onlyOperator() { 83 | require(operator == msg.sender, "GrapeGenesisPool: caller is not the operator"); 84 | _; 85 | } 86 | 87 | function checkPoolDuplicate(IERC20 _token) internal view { 88 | uint256 length = poolInfo.length; 89 | for (uint256 pid = 0; pid < length; ++pid) { 90 | require(poolInfo[pid].token != _token, "GrapeGenesisPool: existing pool?"); 91 | } 92 | } 93 | 94 | // Add a new token to the pool. Can only be called by the owner. 95 | function add( 96 | uint256 _allocPoint, 97 | IERC20 _token, 98 | bool _withUpdate, 99 | uint256 _lastRewardTime 100 | ) public onlyOperator { 101 | checkPoolDuplicate(_token); 102 | if (_withUpdate) { 103 | massUpdatePools(); 104 | } 105 | if (block.timestamp < poolStartTime) { 106 | // chef is sleeping 107 | if (_lastRewardTime == 0) { 108 | _lastRewardTime = poolStartTime; 109 | } else { 110 | if (_lastRewardTime < poolStartTime) { 111 | _lastRewardTime = poolStartTime; 112 | } 113 | } 114 | } else { 115 | // chef is cooking 116 | if (_lastRewardTime == 0 || _lastRewardTime < block.timestamp) { 117 | _lastRewardTime = block.timestamp; 118 | } 119 | } 120 | bool _isStarted = (_lastRewardTime <= poolStartTime) || (_lastRewardTime <= block.timestamp); 121 | poolInfo.push(PoolInfo({token: _token, allocPoint: _allocPoint, lastRewardTime: _lastRewardTime, accGrapePerShare: 0, isStarted: _isStarted})); 122 | if (_isStarted) { 123 | totalAllocPoint = totalAllocPoint.add(_allocPoint); 124 | } 125 | } 126 | 127 | // Update the given pool's GRAPE allocation point. Can only be called by the owner. 128 | function set(uint256 _pid, uint256 _allocPoint) public onlyOperator { 129 | massUpdatePools(); 130 | PoolInfo storage pool = poolInfo[_pid]; 131 | if (pool.isStarted) { 132 | totalAllocPoint = totalAllocPoint.sub(pool.allocPoint).add(_allocPoint); 133 | } 134 | pool.allocPoint = _allocPoint; 135 | } 136 | 137 | // Return accumulate rewards over the given _from to _to block. 138 | function getGeneratedReward(uint256 _fromTime, uint256 _toTime) public view returns (uint256) { 139 | if (_fromTime >= _toTime) return 0; 140 | if (_toTime >= poolEndTime) { 141 | if (_fromTime >= poolEndTime) return 0; 142 | if (_fromTime <= poolStartTime) return poolEndTime.sub(poolStartTime).mul(grapePerSecond); 143 | return poolEndTime.sub(_fromTime).mul(grapePerSecond); 144 | } else { 145 | if (_toTime <= poolStartTime) return 0; 146 | if (_fromTime <= poolStartTime) return _toTime.sub(poolStartTime).mul(grapePerSecond); 147 | return _toTime.sub(_fromTime).mul(grapePerSecond); 148 | } 149 | } 150 | 151 | // View function to see pending GRAPE on frontend. 152 | function pendingGRAPE(uint256 _pid, address _user) external view returns (uint256) { 153 | PoolInfo storage pool = poolInfo[_pid]; 154 | UserInfo storage user = userInfo[_pid][_user]; 155 | uint256 accGrapePerShare = pool.accGrapePerShare; 156 | uint256 tokenSupply = pool.token.balanceOf(address(this)); 157 | if (block.timestamp > pool.lastRewardTime && tokenSupply != 0) { 158 | uint256 _generatedReward = getGeneratedReward(pool.lastRewardTime, block.timestamp); 159 | uint256 _grapeReward = _generatedReward.mul(pool.allocPoint).div(totalAllocPoint); 160 | accGrapePerShare = accGrapePerShare.add(_grapeReward.mul(1e18).div(tokenSupply)); 161 | } 162 | return user.amount.mul(accGrapePerShare).div(1e18).sub(user.rewardDebt); 163 | } 164 | 165 | // Update reward variables for all pools. Be careful of gas spending! 166 | function massUpdatePools() public { 167 | uint256 length = poolInfo.length; 168 | for (uint256 pid = 0; pid < length; ++pid) { 169 | updatePool(pid); 170 | } 171 | } 172 | 173 | // Update reward variables of the given pool to be up-to-date. 174 | function updatePool(uint256 _pid) public { 175 | PoolInfo storage pool = poolInfo[_pid]; 176 | if (block.timestamp <= pool.lastRewardTime) { 177 | return; 178 | } 179 | uint256 tokenSupply = pool.token.balanceOf(address(this)); 180 | if (tokenSupply == 0) { 181 | pool.lastRewardTime = block.timestamp; 182 | return; 183 | } 184 | if (!pool.isStarted) { 185 | pool.isStarted = true; 186 | totalAllocPoint = totalAllocPoint.add(pool.allocPoint); 187 | } 188 | if (totalAllocPoint > 0) { 189 | uint256 _generatedReward = getGeneratedReward(pool.lastRewardTime, block.timestamp); 190 | uint256 _grapeReward = _generatedReward.mul(pool.allocPoint).div(totalAllocPoint); 191 | pool.accGrapePerShare = pool.accGrapePerShare.add(_grapeReward.mul(1e18).div(tokenSupply)); 192 | } 193 | pool.lastRewardTime = block.timestamp; 194 | } 195 | 196 | // Deposit LP tokens. 197 | function deposit(uint256 _pid, uint256 _amount) public { 198 | address _sender = msg.sender; 199 | PoolInfo storage pool = poolInfo[_pid]; 200 | UserInfo storage user = userInfo[_pid][_sender]; 201 | updatePool(_pid); 202 | if (user.amount > 0) { 203 | uint256 _pending = user.amount.mul(pool.accGrapePerShare).div(1e18).sub(user.rewardDebt); 204 | if (_pending > 0) { 205 | safeGrapeTransfer(_sender, _pending); 206 | emit RewardPaid(_sender, _pending); 207 | } 208 | } 209 | if (_amount > 0) { 210 | pool.token.safeTransferFrom(_sender, address(this), _amount); 211 | if (address(pool.token) == mim) { 212 | user.amount = user.amount.add(_amount.mul(9900).div(10000)); 213 | } else { 214 | user.amount = user.amount.add(_amount); 215 | } 216 | } 217 | user.rewardDebt = user.amount.mul(pool.accGrapePerShare).div(1e18); 218 | emit Deposit(_sender, _pid, _amount); 219 | } 220 | 221 | // Withdraw LP tokens. 222 | function withdraw(uint256 _pid, uint256 _amount) public { 223 | address _sender = msg.sender; 224 | PoolInfo storage pool = poolInfo[_pid]; 225 | UserInfo storage user = userInfo[_pid][_sender]; 226 | require(user.amount >= _amount, "withdraw: not good"); 227 | updatePool(_pid); 228 | uint256 _pending = user.amount.mul(pool.accGrapePerShare).div(1e18).sub(user.rewardDebt); 229 | if (_pending > 0) { 230 | safeGrapeTransfer(_sender, _pending); 231 | emit RewardPaid(_sender, _pending); 232 | } 233 | if (_amount > 0) { 234 | user.amount = user.amount.sub(_amount); 235 | pool.token.safeTransfer(_sender, _amount); 236 | } 237 | user.rewardDebt = user.amount.mul(pool.accGrapePerShare).div(1e18); 238 | emit Withdraw(_sender, _pid, _amount); 239 | } 240 | 241 | // Withdraw without caring about rewards. EMERGENCY ONLY. 242 | function emergencyWithdraw(uint256 _pid) public { 243 | PoolInfo storage pool = poolInfo[_pid]; 244 | UserInfo storage user = userInfo[_pid][msg.sender]; 245 | uint256 _amount = user.amount; 246 | user.amount = 0; 247 | user.rewardDebt = 0; 248 | pool.token.safeTransfer(msg.sender, _amount); 249 | emit EmergencyWithdraw(msg.sender, _pid, _amount); 250 | } 251 | 252 | // Safe GRAPE transfer function, just in case a rounding error causes pool to not have enough GRAPEs. 253 | function safeGrapeTransfer(address _to, uint256 _amount) internal { 254 | uint256 _grapeBalance = grape.balanceOf(address(this)); 255 | if (_grapeBalance > 0) { 256 | if (_amount > _grapeBalance) { 257 | grape.safeTransfer(_to, _grapeBalance); 258 | } else { 259 | grape.safeTransfer(_to, _amount); 260 | } 261 | } 262 | } 263 | 264 | function setOperator(address _operator) external onlyOperator { 265 | operator = _operator; 266 | } 267 | 268 | function governanceRecoverUnsupported( 269 | IERC20 _token, 270 | uint256 amount, 271 | address to 272 | ) external onlyOperator { 273 | if (block.timestamp < poolEndTime + 90 days) { 274 | // do not allow to drain core token (GRAPE or lps) if less than 90 days after pool ends 275 | require(_token != grape, "grape"); 276 | uint256 length = poolInfo.length; 277 | for (uint256 pid = 0; pid < length; ++pid) { 278 | PoolInfo storage pool = poolInfo[pid]; 279 | require(_token != pool.token, "pool.token"); 280 | } 281 | } 282 | _token.safeTransfer(to, amount); 283 | } 284 | } -------------------------------------------------------------------------------- /distribution/GrapeRewardPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 7 | import "@openzeppelin/contracts/math/SafeMath.sol"; 8 | 9 | // Note that this pool has no minter key of GRAPE (rewards). 10 | // Instead, the governance will call GRAPE distributeReward method and send reward to this pool at the beginning. 11 | contract GrapeRewardPool { 12 | using SafeMath for uint256; 13 | using SafeERC20 for IERC20; 14 | 15 | // governance 16 | address public operator; 17 | 18 | // Info of each user. 19 | struct UserInfo { 20 | uint256 amount; // How many LP tokens the user has provided. 21 | uint256 rewardDebt; // Reward debt. See explanation below. 22 | } 23 | 24 | // Info of each pool. 25 | struct PoolInfo { 26 | IERC20 token; // Address of LP token contract. 27 | uint256 allocPoint; // How many allocation points assigned to this pool. Grapes to distribute in the pool. 28 | uint256 lastRewardTime; // Last time that Grapes distribution occurred. 29 | uint256 accGrapePerShare; // Accumulated Grapes per share, times 1e18. See below. 30 | bool isStarted; // if lastRewardTime has passed 31 | } 32 | 33 | IERC20 public grape; 34 | 35 | // Info of each pool. 36 | PoolInfo[] public poolInfo; 37 | 38 | // Info of each user that stakes LP tokens. 39 | mapping(uint256 => mapping(address => UserInfo)) public userInfo; 40 | 41 | // Total allocation points. Must be the sum of all allocation points in all pools. 42 | uint256 public totalAllocPoint = 0; 43 | 44 | // The time when GRAPE mining starts. 45 | uint256 public poolStartTime; 46 | 47 | uint256[] public epochTotalRewards = [10800 ether, 10800 ether]; 48 | 49 | // Time when each epoch ends. 50 | uint256[3] public epochEndTimes; 51 | 52 | // Reward per second for each of 2 epochs (last item is equal to 0 - for sanity). 53 | uint256[3] public epochGrapePerSecond; 54 | 55 | event Deposit(address indexed user, uint256 indexed pid, uint256 amount); 56 | event Withdraw(address indexed user, uint256 indexed pid, uint256 amount); 57 | event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount); 58 | event RewardPaid(address indexed user, uint256 amount); 59 | 60 | constructor(address _grape, uint256 _poolStartTime) public { 61 | require(block.timestamp < _poolStartTime, "late"); 62 | if (_grape != address(0)) grape = IERC20(_grape); 63 | 64 | poolStartTime = _poolStartTime; 65 | 66 | epochEndTimes[0] = poolStartTime + 4 days; // Day 2-5 67 | epochEndTimes[1] = epochEndTimes[0] + 5 days; // Day 6-10 68 | 69 | epochGrapePerSecond[0] = epochTotalRewards[0].div(4 days); 70 | epochGrapePerSecond[1] = epochTotalRewards[1].div(5 days); 71 | 72 | epochGrapePerSecond[2] = 0; 73 | operator = msg.sender; 74 | } 75 | 76 | modifier onlyOperator() { 77 | require(operator == msg.sender, "GrapeRewardPool: caller is not the operator"); 78 | _; 79 | } 80 | 81 | function checkPoolDuplicate(IERC20 _token) internal view { 82 | uint256 length = poolInfo.length; 83 | for (uint256 pid = 0; pid < length; ++pid) { 84 | require(poolInfo[pid].token != _token, "GrapeRewardPool: existing pool?"); 85 | } 86 | } 87 | 88 | // Add a new token to the pool. Can only be called by the owner. 89 | function add( 90 | uint256 _allocPoint, 91 | IERC20 _token, 92 | bool _withUpdate, 93 | uint256 _lastRewardTime 94 | ) public onlyOperator { 95 | checkPoolDuplicate(_token); 96 | if (_withUpdate) { 97 | massUpdatePools(); 98 | } 99 | if (block.timestamp < poolStartTime) { 100 | // chef is sleeping 101 | if (_lastRewardTime == 0) { 102 | _lastRewardTime = poolStartTime; 103 | } else { 104 | if (_lastRewardTime < poolStartTime) { 105 | _lastRewardTime = poolStartTime; 106 | } 107 | } 108 | } else { 109 | // chef is cooking 110 | if (_lastRewardTime == 0 || _lastRewardTime < block.timestamp) { 111 | _lastRewardTime = block.timestamp; 112 | } 113 | } 114 | bool _isStarted = (_lastRewardTime <= poolStartTime) || (_lastRewardTime <= block.timestamp); 115 | poolInfo.push(PoolInfo({token: _token, allocPoint: _allocPoint, lastRewardTime: _lastRewardTime, accGrapePerShare: 0, isStarted: _isStarted})); 116 | if (_isStarted) { 117 | totalAllocPoint = totalAllocPoint.add(_allocPoint); 118 | } 119 | } 120 | 121 | // Update the given pool's GRAPE allocation point. Can only be called by the owner. 122 | function set(uint256 _pid, uint256 _allocPoint) public onlyOperator { 123 | massUpdatePools(); 124 | PoolInfo storage pool = poolInfo[_pid]; 125 | if (pool.isStarted) { 126 | totalAllocPoint = totalAllocPoint.sub(pool.allocPoint).add(_allocPoint); 127 | } 128 | pool.allocPoint = _allocPoint; 129 | } 130 | 131 | // Return accumulate rewards over the given _fromTime to _toTime. 132 | function getGeneratedReward(uint256 _fromTime, uint256 _toTime) public view returns (uint256) { 133 | for (uint8 epochId = 2; epochId >= 1; --epochId) { 134 | if (_toTime >= epochEndTimes[epochId - 1]) { 135 | if (_fromTime >= epochEndTimes[epochId - 1]) { 136 | return _toTime.sub(_fromTime).mul(epochGrapePerSecond[epochId]); 137 | } 138 | 139 | uint256 _generatedReward = _toTime.sub(epochEndTimes[epochId - 1]).mul(epochGrapePerSecond[epochId]); 140 | if (epochId == 1) { 141 | return _generatedReward.add(epochEndTimes[0].sub(_fromTime).mul(epochGrapePerSecond[0])); 142 | } 143 | for (epochId = epochId - 1; epochId >= 1; --epochId) { 144 | if (_fromTime >= epochEndTimes[epochId - 1]) { 145 | return _generatedReward.add(epochEndTimes[epochId].sub(_fromTime).mul(epochGrapePerSecond[epochId])); 146 | } 147 | _generatedReward = _generatedReward.add(epochEndTimes[epochId].sub(epochEndTimes[epochId - 1]).mul(epochGrapePerSecond[epochId])); 148 | } 149 | return _generatedReward.add(epochEndTimes[0].sub(_fromTime).mul(epochGrapePerSecond[0])); 150 | } 151 | } 152 | return _toTime.sub(_fromTime).mul(epochGrapePerSecond[0]); 153 | } 154 | 155 | // View function to see pending GRAPEs on frontend. 156 | function pendingGRAPE(uint256 _pid, address _user) external view returns (uint256) { 157 | PoolInfo storage pool = poolInfo[_pid]; 158 | UserInfo storage user = userInfo[_pid][_user]; 159 | uint256 accGrapePerShare = pool.accGrapePerShare; 160 | uint256 tokenSupply = pool.token.balanceOf(address(this)); 161 | if (block.timestamp > pool.lastRewardTime && tokenSupply != 0) { 162 | uint256 _generatedReward = getGeneratedReward(pool.lastRewardTime, block.timestamp); 163 | uint256 _grapeReward = _generatedReward.mul(pool.allocPoint).div(totalAllocPoint); 164 | accGrapePerShare = accGrapePerShare.add(_grapeReward.mul(1e18).div(tokenSupply)); 165 | } 166 | return user.amount.mul(accGrapePerShare).div(1e18).sub(user.rewardDebt); 167 | } 168 | 169 | // Update reward variables for all pools. Expensive! 170 | function massUpdatePools() public { 171 | uint256 length = poolInfo.length; 172 | for (uint256 pid = 0; pid < length; ++pid) { 173 | updatePool(pid); 174 | } 175 | } 176 | 177 | // Update reward variables of the given pool to be up-to-date. 178 | function updatePool(uint256 _pid) public { 179 | PoolInfo storage pool = poolInfo[_pid]; 180 | if (block.timestamp <= pool.lastRewardTime) { 181 | return; 182 | } 183 | uint256 tokenSupply = pool.token.balanceOf(address(this)); 184 | if (tokenSupply == 0) { 185 | pool.lastRewardTime = block.timestamp; 186 | return; 187 | } 188 | if (!pool.isStarted) { 189 | pool.isStarted = true; 190 | totalAllocPoint = totalAllocPoint.add(pool.allocPoint); 191 | } 192 | if (totalAllocPoint > 0) { 193 | uint256 _generatedReward = getGeneratedReward(pool.lastRewardTime, block.timestamp); 194 | uint256 _grapeReward = _generatedReward.mul(pool.allocPoint).div(totalAllocPoint); 195 | pool.accGrapePerShare = pool.accGrapePerShare.add(_grapeReward.mul(1e18).div(tokenSupply)); 196 | } 197 | pool.lastRewardTime = block.timestamp; 198 | } 199 | 200 | // Deposit LP tokens. 201 | function deposit(uint256 _pid, uint256 _amount) public { 202 | address _sender = msg.sender; 203 | PoolInfo storage pool = poolInfo[_pid]; 204 | UserInfo storage user = userInfo[_pid][_sender]; 205 | updatePool(_pid); 206 | if (user.amount > 0) { 207 | uint256 _pending = user.amount.mul(pool.accGrapePerShare).div(1e18).sub(user.rewardDebt); 208 | if (_pending > 0) { 209 | safeGrapeTransfer(_sender, _pending); 210 | emit RewardPaid(_sender, _pending); 211 | } 212 | } 213 | if (_amount > 0) { 214 | pool.token.safeTransferFrom(_sender, address(this), _amount); 215 | user.amount = user.amount.add(_amount); 216 | } 217 | user.rewardDebt = user.amount.mul(pool.accGrapePerShare).div(1e18); 218 | emit Deposit(_sender, _pid, _amount); 219 | } 220 | 221 | // Withdraw LP tokens. 222 | function withdraw(uint256 _pid, uint256 _amount) public { 223 | address _sender = msg.sender; 224 | PoolInfo storage pool = poolInfo[_pid]; 225 | UserInfo storage user = userInfo[_pid][_sender]; 226 | require(user.amount >= _amount, "withdraw: not good"); 227 | updatePool(_pid); 228 | uint256 _pending = user.amount.mul(pool.accGrapePerShare).div(1e18).sub(user.rewardDebt); 229 | if (_pending > 0) { 230 | safeGrapeTransfer(_sender, _pending); 231 | emit RewardPaid(_sender, _pending); 232 | } 233 | if (_amount > 0) { 234 | user.amount = user.amount.sub(_amount); 235 | pool.token.safeTransfer(_sender, _amount); 236 | } 237 | user.rewardDebt = user.amount.mul(pool.accGrapePerShare).div(1e18); 238 | emit Withdraw(_sender, _pid, _amount); 239 | } 240 | 241 | // Withdraw without caring about rewards. EMERGENCY ONLY. 242 | function emergencyWithdraw(uint256 _pid) public { 243 | PoolInfo storage pool = poolInfo[_pid]; 244 | UserInfo storage user = userInfo[_pid][msg.sender]; 245 | uint256 _amount = user.amount; 246 | user.amount = 0; 247 | user.rewardDebt = 0; 248 | pool.token.safeTransfer(msg.sender, _amount); 249 | emit EmergencyWithdraw(msg.sender, _pid, _amount); 250 | } 251 | 252 | // Safe grape transfer function, just in case if rounding error causes pool to not have enough Grapes. 253 | function safeGrapeTransfer(address _to, uint256 _amount) internal { 254 | uint256 _grapeBal = grape.balanceOf(address(this)); 255 | if (_grapeBal > 0) { 256 | if (_amount > _grapeBal) { 257 | grape.safeTransfer(_to, _grapeBal); 258 | } else { 259 | grape.safeTransfer(_to, _amount); 260 | } 261 | } 262 | } 263 | 264 | function setOperator(address _operator) external onlyOperator { 265 | operator = _operator; 266 | } 267 | 268 | function governanceRecoverUnsupported( 269 | IERC20 _token, 270 | uint256 amount, 271 | address to 272 | ) external onlyOperator { 273 | if (block.timestamp < epochEndTimes[1] + 30 days) { 274 | // do not allow to drain token if less than 30 days after farming 275 | require(_token != grape, "!grape"); 276 | uint256 length = poolInfo.length; 277 | for (uint256 pid = 0; pid < length; ++pid) { 278 | PoolInfo storage pool = poolInfo[pid]; 279 | require(_token != pool.token, "!pool.token"); 280 | } 281 | } 282 | _token.safeTransfer(to, amount); 283 | } 284 | } -------------------------------------------------------------------------------- /distribution/WineRewardPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 7 | import "@openzeppelin/contracts/math/SafeMath.sol"; 8 | 9 | // Note that this pool has no minter key of wine (rewards). 10 | // Instead, the governance will call wine distributeReward method and send reward to this pool at the beginning. 11 | contract WineRewardPool { 12 | using SafeMath for uint256; 13 | using SafeERC20 for IERC20; 14 | 15 | // governance 16 | address public operator; 17 | 18 | // Info of each user. 19 | struct UserInfo { 20 | uint256 amount; // How many LP tokens the user has provided. 21 | uint256 rewardDebt; // Reward debt. See explanation below. 22 | } 23 | 24 | // Info of each pool. 25 | struct PoolInfo { 26 | IERC20 token; // Address of LP token contract. 27 | uint256 allocPoint; // How many allocation points assigned to this pool. Wine to distribute per block. 28 | uint256 lastRewardTime; // Last time that wine distribution occurs. 29 | uint256 accWinePerShare; // Accumulated wine per share, times 1e18. See below. 30 | bool isStarted; // if lastRewardTime has passed 31 | } 32 | 33 | IERC20 public wine; 34 | 35 | // Info of each pool. 36 | PoolInfo[] public poolInfo; 37 | 38 | // Info of each user that stakes LP tokens. 39 | mapping(uint256 => mapping(address => UserInfo)) public userInfo; 40 | 41 | // Total allocation points. Must be the sum of all allocation points in all pools. 42 | uint256 public totalAllocPoint = 0; 43 | 44 | // The time when wine mining starts. 45 | uint256 public poolStartTime; 46 | 47 | // The time when wine mining ends. 48 | uint256 public poolEndTime; 49 | 50 | uint256 public winePerSecond = 0.00128253 ether; // 41000 wine / (370 days * 24h * 60min * 60s) 51 | uint256 public runningTime = 370 days; // 370 days 52 | uint256 public constant TOTAL_REWARDS = 41000 ether; 53 | 54 | event Deposit(address indexed user, uint256 indexed pid, uint256 amount); 55 | event Withdraw(address indexed user, uint256 indexed pid, uint256 amount); 56 | event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount); 57 | event RewardPaid(address indexed user, uint256 amount); 58 | 59 | constructor( 60 | address _wine, 61 | uint256 _poolStartTime 62 | ) public { 63 | require(block.timestamp < _poolStartTime, "late"); 64 | if (_wine != address(0)) wine = IERC20(_wine); 65 | poolStartTime = _poolStartTime; 66 | poolEndTime = poolStartTime + runningTime; 67 | operator = msg.sender; 68 | } 69 | 70 | modifier onlyOperator() { 71 | require(operator == msg.sender, "WineRewardPool: caller is not the operator"); 72 | _; 73 | } 74 | 75 | function checkPoolDuplicate(IERC20 _token) internal view { 76 | uint256 length = poolInfo.length; 77 | for (uint256 pid = 0; pid < length; ++pid) { 78 | require(poolInfo[pid].token != _token, "WineRewardPool: existing pool?"); 79 | } 80 | } 81 | 82 | // Add a new lp to the pool. Can only be called by the owner. 83 | function add( 84 | uint256 _allocPoint, 85 | IERC20 _token, 86 | bool _withUpdate, 87 | uint256 _lastRewardTime 88 | ) public onlyOperator { 89 | checkPoolDuplicate(_token); 90 | if (_withUpdate) { 91 | massUpdatePools(); 92 | } 93 | if (block.timestamp < poolStartTime) { 94 | // chef is sleeping 95 | if (_lastRewardTime == 0) { 96 | _lastRewardTime = poolStartTime; 97 | } else { 98 | if (_lastRewardTime < poolStartTime) { 99 | _lastRewardTime = poolStartTime; 100 | } 101 | } 102 | } else { 103 | // chef is cooking 104 | if (_lastRewardTime == 0 || _lastRewardTime < block.timestamp) { 105 | _lastRewardTime = block.timestamp; 106 | } 107 | } 108 | bool _isStarted = 109 | (_lastRewardTime <= poolStartTime) || 110 | (_lastRewardTime <= block.timestamp); 111 | poolInfo.push(PoolInfo({ 112 | token : _token, 113 | allocPoint : _allocPoint, 114 | lastRewardTime : _lastRewardTime, 115 | accWinePerShare : 0, 116 | isStarted : _isStarted 117 | })); 118 | if (_isStarted) { 119 | totalAllocPoint = totalAllocPoint.add(_allocPoint); 120 | } 121 | } 122 | 123 | // Update the given pool's wine allocation point. Can only be called by the owner. 124 | function set(uint256 _pid, uint256 _allocPoint) public onlyOperator { 125 | massUpdatePools(); 126 | PoolInfo storage pool = poolInfo[_pid]; 127 | if (pool.isStarted) { 128 | totalAllocPoint = totalAllocPoint.sub(pool.allocPoint).add( 129 | _allocPoint 130 | ); 131 | } 132 | pool.allocPoint = _allocPoint; 133 | } 134 | 135 | // Return accumulate rewards over the given _from to _to block. 136 | function getGeneratedReward(uint256 _fromTime, uint256 _toTime) public view returns (uint256) { 137 | if (_fromTime >= _toTime) return 0; 138 | if (_toTime >= poolEndTime) { 139 | if (_fromTime >= poolEndTime) return 0; 140 | if (_fromTime <= poolStartTime) return poolEndTime.sub(poolStartTime).mul(winePerSecond); 141 | return poolEndTime.sub(_fromTime).mul(winePerSecond); 142 | } else { 143 | if (_toTime <= poolStartTime) return 0; 144 | if (_fromTime <= poolStartTime) return _toTime.sub(poolStartTime).mul(winePerSecond); 145 | return _toTime.sub(_fromTime).mul(winePerSecond); 146 | } 147 | } 148 | 149 | // View function to see pending Wine on frontend. 150 | function pendingShare(uint256 _pid, address _user) external view returns (uint256) { 151 | PoolInfo storage pool = poolInfo[_pid]; 152 | UserInfo storage user = userInfo[_pid][_user]; 153 | uint256 accWinePerShare = pool.accWinePerShare; 154 | uint256 tokenSupply = pool.token.balanceOf(address(this)); 155 | if (block.timestamp > pool.lastRewardTime && tokenSupply != 0) { 156 | uint256 _generatedReward = getGeneratedReward(pool.lastRewardTime, block.timestamp); 157 | uint256 _wineReward = _generatedReward.mul(pool.allocPoint).div(totalAllocPoint); 158 | accWinePerShare = accWinePerShare.add(_wineReward.mul(1e18).div(tokenSupply)); 159 | } 160 | return user.amount.mul(accWinePerShare).div(1e18).sub(user.rewardDebt); 161 | } 162 | 163 | // Update reward variables for all pools. Be careful of gas spending! 164 | function massUpdatePools() public { 165 | uint256 length = poolInfo.length; 166 | for (uint256 pid = 0; pid < length; ++pid) { 167 | updatePool(pid); 168 | } 169 | } 170 | 171 | // Update reward variables of the given pool to be up-to-date. 172 | function updatePool(uint256 _pid) public { 173 | PoolInfo storage pool = poolInfo[_pid]; 174 | if (block.timestamp <= pool.lastRewardTime) { 175 | return; 176 | } 177 | uint256 tokenSupply = pool.token.balanceOf(address(this)); 178 | if (tokenSupply == 0) { 179 | pool.lastRewardTime = block.timestamp; 180 | return; 181 | } 182 | if (!pool.isStarted) { 183 | pool.isStarted = true; 184 | totalAllocPoint = totalAllocPoint.add(pool.allocPoint); 185 | } 186 | if (totalAllocPoint > 0) { 187 | uint256 _generatedReward = getGeneratedReward(pool.lastRewardTime, block.timestamp); 188 | uint256 _wineReward = _generatedReward.mul(pool.allocPoint).div(totalAllocPoint); 189 | pool.accWinePerShare = pool.accWinePerShare.add(_wineReward.mul(1e18).div(tokenSupply)); 190 | } 191 | pool.lastRewardTime = block.timestamp; 192 | } 193 | 194 | // Deposit LP tokens. 195 | function deposit(uint256 _pid, uint256 _amount) public { 196 | address _sender = msg.sender; 197 | PoolInfo storage pool = poolInfo[_pid]; 198 | UserInfo storage user = userInfo[_pid][_sender]; 199 | updatePool(_pid); 200 | if (user.amount > 0) { 201 | uint256 _pending = user.amount.mul(pool.accWinePerShare).div(1e18).sub(user.rewardDebt); 202 | if (_pending > 0) { 203 | safeWineTransfer(_sender, _pending); 204 | emit RewardPaid(_sender, _pending); 205 | } 206 | } 207 | if (_amount > 0) { 208 | pool.token.safeTransferFrom(_sender, address(this), _amount); 209 | user.amount = user.amount.add(_amount); 210 | } 211 | user.rewardDebt = user.amount.mul(pool.accWinePerShare).div(1e18); 212 | emit Deposit(_sender, _pid, _amount); 213 | } 214 | 215 | // Withdraw LP tokens. 216 | function withdraw(uint256 _pid, uint256 _amount) public { 217 | address _sender = msg.sender; 218 | PoolInfo storage pool = poolInfo[_pid]; 219 | UserInfo storage user = userInfo[_pid][_sender]; 220 | require(user.amount >= _amount, "withdraw: not good"); 221 | updatePool(_pid); 222 | uint256 _pending = user.amount.mul(pool.accWinePerShare).div(1e18).sub(user.rewardDebt); 223 | if (_pending > 0) { 224 | safeWineTransfer(_sender, _pending); 225 | emit RewardPaid(_sender, _pending); 226 | } 227 | if (_amount > 0) { 228 | user.amount = user.amount.sub(_amount); 229 | pool.token.safeTransfer(_sender, _amount); 230 | } 231 | user.rewardDebt = user.amount.mul(pool.accWinePerShare).div(1e18); 232 | emit Withdraw(_sender, _pid, _amount); 233 | } 234 | 235 | // Withdraw without caring about rewards. EMERGENCY ONLY. 236 | function emergencyWithdraw(uint256 _pid) public { 237 | PoolInfo storage pool = poolInfo[_pid]; 238 | UserInfo storage user = userInfo[_pid][msg.sender]; 239 | uint256 _amount = user.amount; 240 | user.amount = 0; 241 | user.rewardDebt = 0; 242 | pool.token.safeTransfer(msg.sender, _amount); 243 | emit EmergencyWithdraw(msg.sender, _pid, _amount); 244 | } 245 | 246 | // Safe wine transfer function, just in case if rounding error causes pool to not have enough wine. 247 | function safeWineTransfer(address _to, uint256 _amount) internal { 248 | uint256 _wineBal = wine.balanceOf(address(this)); 249 | if (_wineBal > 0) { 250 | if (_amount > _wineBal) { 251 | wine.safeTransfer(_to, _wineBal); 252 | } else { 253 | wine.safeTransfer(_to, _amount); 254 | } 255 | } 256 | } 257 | 258 | function setOperator(address _operator) external onlyOperator { 259 | operator = _operator; 260 | } 261 | 262 | function governanceRecoverUnsupported(IERC20 _token, uint256 amount, address to) external onlyOperator { 263 | if (block.timestamp < poolEndTime + 90 days) { 264 | // do not allow to drain core token (wine or lps) if less than 90 days after pool ends 265 | require(_token != wine, "wine"); 266 | uint256 length = poolInfo.length; 267 | for (uint256 pid = 0; pid < length; ++pid) { 268 | PoolInfo storage pool = poolInfo[pid]; 269 | require(_token != pool.token, "pool.token"); 270 | } 271 | } 272 | _token.safeTransfer(to, amount); 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /interfaces/IBasisAsset.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | interface IBasisAsset { 6 | function mint(address recipient, uint256 amount) external returns (bool); 7 | 8 | function burn(uint256 amount) external; 9 | 10 | function burnFrom(address from, uint256 amount) external; 11 | 12 | function isOperator() external returns (bool); 13 | 14 | function operator() external view returns (address); 15 | 16 | function transferOperator(address newOperator_) external; 17 | } 18 | -------------------------------------------------------------------------------- /interfaces/IBoardroom.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | interface IBoardroom { 6 | function balanceOf(address _member) external view returns (uint256); 7 | 8 | function earned(address _member) external view returns (uint256); 9 | 10 | function canWithdraw(address _member) external view returns (bool); 11 | 12 | function canClaimReward(address _member) external view returns (bool); 13 | 14 | function epoch() external view returns (uint256); 15 | 16 | function nextEpochPoint() external view returns (uint256); 17 | 18 | function getGrapePrice() external view returns (uint256); 19 | 20 | function setOperator(address _operator) external; 21 | 22 | function setLockUp(uint256 _withdrawLockupEpochs, uint256 _rewardLockupEpochs) external; 23 | 24 | function stake(uint256 _amount) external; 25 | 26 | function withdraw(uint256 _amount) external; 27 | 28 | function exit() external; 29 | 30 | function claimReward() external; 31 | 32 | function allocateSeigniorage(uint256 _amount) external; 33 | 34 | function governanceRecoverUnsupported( 35 | address _token, 36 | uint256 _amount, 37 | address _to 38 | ) external; 39 | } 40 | -------------------------------------------------------------------------------- /interfaces/IDecimals.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | interface IDecimals { 6 | function decimals() external view returns (uint8); 7 | } 8 | -------------------------------------------------------------------------------- /interfaces/IDistributor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | interface IDistributor { 6 | function distribute() external; 7 | } 8 | -------------------------------------------------------------------------------- /interfaces/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | /** 6 | * @dev Interface of the ERC20 standard as defined in the EIP. 7 | */ 8 | interface IERC20 { 9 | /** 10 | * @dev Returns the amount of tokens in existence. 11 | */ 12 | function totalSupply() external view returns (uint256); 13 | 14 | /** 15 | * @dev Returns the amount of tokens owned by `account`. 16 | */ 17 | function balanceOf(address account) external view returns (uint256); 18 | 19 | /** 20 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 21 | * 22 | * Returns a boolean value indicating whether the operation succeeded. 23 | * 24 | * Emits a {Transfer} event. 25 | */ 26 | function transfer(address recipient, uint256 amount) external returns (bool); 27 | 28 | /** 29 | * @dev Returns the remaining number of tokens that `spender` will be 30 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 31 | * zero by default. 32 | * 33 | * This value changes when {approve} or {transferFrom} are called. 34 | */ 35 | function allowance(address owner, address spender) external view returns (uint256); 36 | 37 | /** 38 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 39 | * 40 | * Returns a boolean value indicating whether the operation succeeded. 41 | * 42 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 43 | * that someone may use both the old and the new allowance by unfortunate 44 | * transaction ordering. One possible solution to mitigate this race 45 | * condition is to first reduce the spender's allowance to 0 and set the 46 | * desired value afterwards: 47 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 48 | * 49 | * Emits an {Approval} event. 50 | */ 51 | function approve(address spender, uint256 amount) external returns (bool); 52 | 53 | /** 54 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 55 | * allowance mechanism. `amount` is then deducted from the caller's 56 | * allowance. 57 | * 58 | * Returns a boolean value indicating whether the operation succeeded. 59 | * 60 | * Emits a {Transfer} event. 61 | */ 62 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 63 | 64 | /** 65 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 66 | * another (`to`). 67 | * 68 | * Note that `value` may be zero. 69 | */ 70 | event Transfer(address indexed from, address indexed to, uint256 value); 71 | 72 | /** 73 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 74 | * a call to {approve}. `value` is the new allowance. 75 | */ 76 | event Approval(address indexed owner, address indexed spender, uint256 value); 77 | } 78 | -------------------------------------------------------------------------------- /interfaces/IGShareRewardPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | interface IGShareRewardPool { 6 | function deposit(uint256 _pid, uint256 _amount) external; 7 | 8 | function withdraw(uint256 _pid, uint256 _amount) external; 9 | 10 | function pendingShare(uint256 _pid, address _user) external view returns (uint256); 11 | 12 | function userInfo(uint _pid, address _user) external view returns (uint amount, uint rewardDebt); 13 | } 14 | -------------------------------------------------------------------------------- /interfaces/IOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | interface IOracle { 6 | function update() external; 7 | 8 | function consult(address _token, uint256 _amountIn) external view returns (uint144 amountOut); 9 | 10 | function twap(address _token, uint256 _amountIn) external view returns (uint144 _amountOut); 11 | } 12 | -------------------------------------------------------------------------------- /interfaces/IShare.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | interface IShare { 6 | function unclaimedTreasuryFund() external view returns (uint256 _pending); 7 | 8 | function claimRewards() external; 9 | } 10 | -------------------------------------------------------------------------------- /interfaces/ISimpleERCFund.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | interface ISimpleERCFund { 6 | function deposit( 7 | address token, 8 | uint256 amount, 9 | string memory reason 10 | ) external; 11 | 12 | function withdraw( 13 | address token, 14 | uint256 amount, 15 | address to, 16 | string memory reason 17 | ) external; 18 | } 19 | -------------------------------------------------------------------------------- /interfaces/ITaxable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | interface ITaxable { 6 | function setTaxTiersTwap(uint8 _index, uint256 _value) external returns (bool); 7 | 8 | function setTaxTiersRate(uint8 _index, uint256 _value) external returns (bool); 9 | 10 | function enableAutoCalculateTax() external; 11 | 12 | function disableAutoCalculateTax() external; 13 | 14 | function taxRate() external returns (uint256); 15 | 16 | function setTaxCollectorAddress(address _taxCollectorAddress) external; 17 | 18 | function setTaxRate(uint256 _taxRate) external; 19 | 20 | function setBurnThreshold(uint256 _burnThreshold) external; 21 | 22 | function excludeAddress(address _address) external returns (bool); 23 | 24 | function isAddressExcluded(address _address) external returns (bool); 25 | 26 | function includeAddress(address _address) external returns (bool); 27 | 28 | function setGrapeOracle(address _grapeOracle) external; 29 | 30 | function setTaxOffice(address _taxOffice) external; 31 | } 32 | -------------------------------------------------------------------------------- /interfaces/ITreasury.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | interface ITreasury { 6 | function epoch() external view returns (uint256); 7 | 8 | function nextEpochPoint() external view returns (uint256); 9 | 10 | function getGrapePrice() external view returns (uint256); 11 | 12 | function buyBonds(uint256 amount, uint256 targetPrice) external; 13 | 14 | function redeemBonds(uint256 amount, uint256 targetPrice) external; 15 | } 16 | -------------------------------------------------------------------------------- /interfaces/IUniswapV2Callee.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | interface IUniswapV2Callee { 6 | function uniswapV2Call( 7 | address sender, 8 | uint256 amount0, 9 | uint256 amount1, 10 | bytes calldata data 11 | ) external; 12 | } 13 | -------------------------------------------------------------------------------- /interfaces/IUniswapV2ERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | interface IUniswapV2ERC20 { 6 | event Approval(address indexed owner, address indexed spender, uint256 value); 7 | event Transfer(address indexed from, address indexed to, uint256 value); 8 | 9 | function name() external pure returns (string memory); 10 | 11 | function symbol() external pure returns (string memory); 12 | 13 | function decimals() external pure returns (uint8); 14 | 15 | function totalSupply() external view returns (uint256); 16 | 17 | function balanceOf(address owner) external view returns (uint256); 18 | 19 | function allowance(address owner, address spender) external view returns (uint256); 20 | 21 | function approve(address spender, uint256 value) external returns (bool); 22 | 23 | function transfer(address to, uint256 value) external returns (bool); 24 | 25 | function transferFrom( 26 | address from, 27 | address to, 28 | uint256 value 29 | ) external returns (bool); 30 | 31 | function DOMAIN_SEPARATOR() external view returns (bytes32); 32 | 33 | function PERMIT_TYPEHASH() external pure returns (bytes32); 34 | 35 | function nonces(address owner) external view returns (uint256); 36 | 37 | function permit( 38 | address owner, 39 | address spender, 40 | uint256 value, 41 | uint256 deadline, 42 | uint8 v, 43 | bytes32 r, 44 | bytes32 s 45 | ) external; 46 | } 47 | -------------------------------------------------------------------------------- /interfaces/IUniswapV2Factory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | interface IUniswapV2Factory { 6 | event PairCreated(address indexed token0, address indexed token1, address pair, uint256); 7 | 8 | function getPair(address tokenA, address tokenB) external view returns (address pair); 9 | 10 | function allPairs(uint256) external view returns (address pair); 11 | 12 | function allPairsLength() external view returns (uint256); 13 | 14 | function feeTo() external view returns (address); 15 | 16 | function feeToSetter() external view returns (address); 17 | 18 | function createPair(address tokenA, address tokenB) external returns (address pair); 19 | } 20 | -------------------------------------------------------------------------------- /interfaces/IUniswapV2Pair.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | interface IUniswapV2Pair { 6 | event Approval(address indexed owner, address indexed spender, uint256 value); 7 | event Transfer(address indexed from, address indexed to, uint256 value); 8 | 9 | function name() external pure returns (string memory); 10 | 11 | function symbol() external pure returns (string memory); 12 | 13 | function decimals() external pure returns (uint8); 14 | 15 | function totalSupply() external view returns (uint256); 16 | 17 | function balanceOf(address owner) external view returns (uint256); 18 | 19 | function allowance(address owner, address spender) external view returns (uint256); 20 | 21 | function approve(address spender, uint256 value) external returns (bool); 22 | 23 | function transfer(address to, uint256 value) external returns (bool); 24 | 25 | function transferFrom( 26 | address from, 27 | address to, 28 | uint256 value 29 | ) external returns (bool); 30 | 31 | function DOMAIN_SEPARATOR() external view returns (bytes32); 32 | 33 | function PERMIT_TYPEHASH() external pure returns (bytes32); 34 | 35 | function nonces(address owner) external view returns (uint256); 36 | 37 | function permit( 38 | address owner, 39 | address spender, 40 | uint256 value, 41 | uint256 deadline, 42 | uint8 v, 43 | bytes32 r, 44 | bytes32 s 45 | ) external; 46 | 47 | event Mint(address indexed sender, uint256 amount0, uint256 amount1); 48 | event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to); 49 | event Swap(address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to); 50 | event Sync(uint112 reserve0, uint112 reserve1); 51 | 52 | function MINIMUM_LIQUIDITY() external pure returns (uint256); 53 | 54 | function factory() external view returns (address); 55 | 56 | function token0() external view returns (address); 57 | 58 | function token1() external view returns (address); 59 | 60 | function getReserves() 61 | external 62 | view 63 | returns ( 64 | uint112 reserve0, 65 | uint112 reserve1, 66 | uint32 blockTimestampLast 67 | ); 68 | 69 | function price0CumulativeLast() external view returns (uint256); 70 | 71 | function price1CumulativeLast() external view returns (uint256); 72 | 73 | function kLast() external view returns (uint256); 74 | 75 | function mint(address to) external returns (uint256 liquidity); 76 | 77 | function burn(address to) external returns (uint256 amount0, uint256 amount1); 78 | 79 | function swap( 80 | uint256 amount0Out, 81 | uint256 amount1Out, 82 | address to, 83 | bytes calldata data 84 | ) external; 85 | 86 | function skim(address to) external; 87 | 88 | function sync() external; 89 | 90 | function initialize(address, address) external; 91 | } 92 | -------------------------------------------------------------------------------- /interfaces/IUniswapV2Router.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | interface IUniswapV2Router { 6 | function factory() external pure returns (address); 7 | 8 | function WETH() external pure returns (address); 9 | 10 | function addLiquidity( 11 | address tokenA, 12 | address tokenB, 13 | uint amountADesired, 14 | uint amountBDesired, 15 | uint amountAMin, 16 | uint amountBMin, 17 | address to, 18 | uint deadline 19 | ) external returns (uint amountA, uint amountB, uint liquidity); 20 | 21 | function addLiquidityETH( 22 | address token, 23 | uint amountTokenDesired, 24 | uint amountTokenMin, 25 | uint amountETHMin, 26 | address to, 27 | uint deadline 28 | ) external payable returns (uint amountToken, uint amountETH, uint liquidity); 29 | 30 | function removeLiquidity( 31 | address tokenA, 32 | address tokenB, 33 | uint liquidity, 34 | uint amountAMin, 35 | uint amountBMin, 36 | address to, 37 | uint deadline 38 | ) external returns (uint amountA, uint amountB); 39 | 40 | function removeLiquidityETH( 41 | address token, 42 | uint liquidity, 43 | uint amountTokenMin, 44 | uint amountETHMin, 45 | address to, 46 | uint deadline 47 | ) external returns (uint amountToken, uint amountETH); 48 | 49 | function removeLiquidityWithPermit( 50 | address tokenA, 51 | address tokenB, 52 | uint liquidity, 53 | uint amountAMin, 54 | uint amountBMin, 55 | address to, 56 | uint deadline, 57 | bool approveMax, uint8 v, bytes32 r, bytes32 s 58 | ) external returns (uint amountA, uint amountB); 59 | 60 | function removeLiquidityETHWithPermit( 61 | address token, 62 | uint liquidity, 63 | uint amountTokenMin, 64 | uint amountETHMin, 65 | address to, 66 | uint deadline, 67 | bool approveMax, uint8 v, bytes32 r, bytes32 s 68 | ) external returns (uint amountToken, uint amountETH); 69 | 70 | function swapExactTokensForTokens( 71 | uint amountIn, 72 | uint amountOutMin, 73 | address[] calldata path, 74 | address to, 75 | uint deadline 76 | ) external returns (uint[] memory amounts); 77 | 78 | function swapTokensForExactTokens( 79 | uint amountOut, 80 | uint amountInMax, 81 | address[] calldata path, 82 | address to, 83 | uint deadline 84 | ) external returns (uint[] memory amounts); 85 | 86 | function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) 87 | external 88 | payable 89 | returns (uint[] memory amounts); 90 | 91 | function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) 92 | external 93 | returns (uint[] memory amounts); 94 | 95 | function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) 96 | external 97 | returns (uint[] memory amounts); 98 | 99 | function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) external payable 100 | returns (uint[] memory amounts); 101 | 102 | function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); 103 | 104 | function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); 105 | 106 | function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); 107 | 108 | function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); 109 | 110 | function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); 111 | 112 | function removeLiquidityETHSupportingFeeOnTransferTokens( 113 | address token, 114 | uint liquidity, 115 | uint amountTokenMin, 116 | uint amountETHMin, 117 | address to, 118 | uint deadline 119 | ) external returns (uint amountETH); 120 | 121 | function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( 122 | address token, 123 | uint liquidity, 124 | uint amountTokenMin, 125 | uint amountETHMin, 126 | address to, 127 | uint deadline, 128 | bool approveMax, uint8 v, bytes32 r, bytes32 s 129 | ) external returns (uint amountETH); 130 | 131 | function swapExactTokensForTokensSupportingFeeOnTransferTokens( 132 | uint amountIn, 133 | uint amountOutMin, 134 | address[] calldata path, 135 | address to, 136 | uint deadline 137 | ) external; 138 | 139 | function swapExactETHForTokensSupportingFeeOnTransferTokens( 140 | uint amountOutMin, 141 | address[] calldata path, 142 | address to, 143 | uint deadline 144 | ) external payable; 145 | 146 | function swapExactTokensForETHSupportingFeeOnTransferTokens( 147 | uint amountIn, 148 | uint amountOutMin, 149 | address[] calldata path, 150 | address to, 151 | uint deadline 152 | ) external; 153 | } 154 | -------------------------------------------------------------------------------- /interfaces/IWrappedEth.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | interface IWrappedEth is IERC20 { 8 | function deposit() external payable returns (uint256); 9 | 10 | function withdraw(uint256 amount) external returns (uint256); 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /lib/Babylonian.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | library Babylonian { 6 | function sqrt(uint256 y) internal pure returns (uint256 z) { 7 | if (y > 3) { 8 | z = y; 9 | uint256 x = y / 2 + 1; 10 | while (x < z) { 11 | z = x; 12 | x = (y / x + x) / 2; 13 | } 14 | } else if (y != 0) { 15 | z = 1; 16 | } 17 | // else z = 0 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/FixedPoint.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | import "./Babylonian.sol"; 6 | 7 | // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format)) 8 | library FixedPoint { 9 | // range: [0, 2**112 - 1] 10 | // resolution: 1 / 2**112 11 | struct uq112x112 { 12 | uint224 _x; 13 | } 14 | 15 | // range: [0, 2**144 - 1] 16 | // resolution: 1 / 2**112 17 | struct uq144x112 { 18 | uint256 _x; 19 | } 20 | 21 | uint8 private constant RESOLUTION = 112; 22 | uint256 private constant Q112 = uint256(1) << RESOLUTION; 23 | uint256 private constant Q224 = Q112 << RESOLUTION; 24 | 25 | // encode a uint112 as a UQ112x112 26 | function encode(uint112 x) internal pure returns (uq112x112 memory) { 27 | return uq112x112(uint224(x) << RESOLUTION); 28 | } 29 | 30 | // encodes a uint144 as a UQ144x112 31 | function encode144(uint144 x) internal pure returns (uq144x112 memory) { 32 | return uq144x112(uint256(x) << RESOLUTION); 33 | } 34 | 35 | // divide a UQ112x112 by a uint112, returning a UQ112x112 36 | function div(uq112x112 memory self, uint112 x) internal pure returns (uq112x112 memory) { 37 | require(x != 0, "FixedPoint: DIV_BY_ZERO"); 38 | return uq112x112(self._x / uint224(x)); 39 | } 40 | 41 | // multiply a UQ112x112 by a uint, returning a UQ144x112 42 | // reverts on overflow 43 | function mul(uq112x112 memory self, uint256 y) internal pure returns (uq144x112 memory) { 44 | uint256 z; 45 | require(y == 0 || (z = uint256(self._x) * y) / y == uint256(self._x), "FixedPoint: MULTIPLICATION_OVERFLOW"); 46 | return uq144x112(z); 47 | } 48 | 49 | // returns a UQ112x112 which represents the ratio of the numerator to the denominator 50 | // equivalent to encode(numerator).div(denominator) 51 | function fraction(uint112 numerator, uint112 denominator) internal pure returns (uq112x112 memory) { 52 | require(denominator > 0, "FixedPoint: DIV_BY_ZERO"); 53 | return uq112x112((uint224(numerator) << RESOLUTION) / denominator); 54 | } 55 | 56 | // decode a UQ112x112 into a uint112 by truncating after the radix point 57 | function decode(uq112x112 memory self) internal pure returns (uint112) { 58 | return uint112(self._x >> RESOLUTION); 59 | } 60 | 61 | // decode a UQ144x112 into a uint144 by truncating after the radix point 62 | function decode144(uq144x112 memory self) internal pure returns (uint144) { 63 | return uint144(self._x >> RESOLUTION); 64 | } 65 | 66 | // take the reciprocal of a UQ112x112 67 | function reciprocal(uq112x112 memory self) internal pure returns (uq112x112 memory) { 68 | require(self._x != 0, "FixedPoint: ZERO_RECIPROCAL"); 69 | return uq112x112(uint224(Q224 / self._x)); 70 | } 71 | 72 | // square root of a UQ112x112 73 | function sqrt(uq112x112 memory self) internal pure returns (uq112x112 memory) { 74 | return uq112x112(uint224(Babylonian.sqrt(uint256(self._x)) << 56)); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/Safe112.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | library Safe112 { 6 | function add(uint112 a, uint112 b) internal pure returns (uint256) { 7 | uint256 c = a + b; 8 | require(c >= a, "Safe112: addition overflow"); 9 | 10 | return c; 11 | } 12 | 13 | function sub(uint112 a, uint112 b) internal pure returns (uint256) { 14 | return sub(a, b, "Safe112: subtraction overflow"); 15 | } 16 | 17 | function sub( 18 | uint112 a, 19 | uint112 b, 20 | string memory errorMessage 21 | ) internal pure returns (uint112) { 22 | require(b <= a, errorMessage); 23 | uint112 c = a - b; 24 | 25 | return c; 26 | } 27 | 28 | function mul(uint112 a, uint112 b) internal pure returns (uint256) { 29 | if (a == 0) { 30 | return 0; 31 | } 32 | 33 | uint256 c = a * b; 34 | require(c / a == b, "Safe112: multiplication overflow"); 35 | 36 | return c; 37 | } 38 | 39 | function div(uint112 a, uint112 b) internal pure returns (uint256) { 40 | return div(a, b, "Safe112: division by zero"); 41 | } 42 | 43 | function div( 44 | uint112 a, 45 | uint112 b, 46 | string memory errorMessage 47 | ) internal pure returns (uint112) { 48 | // Solidity only automatically asserts when dividing by 0 49 | require(b > 0, errorMessage); 50 | uint112 c = a / b; 51 | 52 | return c; 53 | } 54 | 55 | function mod(uint112 a, uint112 b) internal pure returns (uint256) { 56 | return mod(a, b, "Safe112: modulo by zero"); 57 | } 58 | 59 | function mod( 60 | uint112 a, 61 | uint112 b, 62 | string memory errorMessage 63 | ) internal pure returns (uint112) { 64 | require(b != 0, errorMessage); 65 | return a % b; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/SafeMath8.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | /** 6 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 7 | * checks. 8 | * 9 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 10 | * in bugs, because programmers usually assume that an overflow raises an 11 | * error, which is the standard behavior in high level programming languages. 12 | * `SafeMath` restores this intuition by reverting the transaction when an 13 | * operation overflows. 14 | * 15 | * Using this library instead of the unchecked operations eliminates an entire 16 | * class of bugs, so it's recommended to use it always. 17 | */ 18 | library SafeMath8 { 19 | /** 20 | * @dev Returns the addition of two unsigned integers, reverting on 21 | * overflow. 22 | * 23 | * Counterpart to Solidity's `+` operator. 24 | * 25 | * Requirements: 26 | * 27 | * - Addition cannot overflow. 28 | */ 29 | function add(uint8 a, uint8 b) internal pure returns (uint8) { 30 | uint8 c = a + b; 31 | require(c >= a, "SafeMath: addition overflow"); 32 | 33 | return c; 34 | } 35 | 36 | /** 37 | * @dev Returns the subtraction of two unsigned integers, reverting on 38 | * overflow (when the result is negative). 39 | * 40 | * Counterpart to Solidity's `-` operator. 41 | * 42 | * Requirements: 43 | * 44 | * - Subtraction cannot overflow. 45 | */ 46 | function sub(uint8 a, uint8 b) internal pure returns (uint8) { 47 | return sub(a, b, "SafeMath: subtraction overflow"); 48 | } 49 | 50 | /** 51 | * @dev Returns the subtraction of two unsigned integers, reverting with custom message on 52 | * overflow (when the result is negative). 53 | * 54 | * Counterpart to Solidity's `-` operator. 55 | * 56 | * Requirements: 57 | * 58 | * - Subtraction cannot overflow. 59 | */ 60 | function sub(uint8 a, uint8 b, string memory errorMessage) internal pure returns (uint8) { 61 | require(b <= a, errorMessage); 62 | uint8 c = a - b; 63 | 64 | return c; 65 | } 66 | 67 | /** 68 | * @dev Returns the multiplication of two unsigned integers, reverting on 69 | * overflow. 70 | * 71 | * Counterpart to Solidity's `*` operator. 72 | * 73 | * Requirements: 74 | * 75 | * - Multiplication cannot overflow. 76 | */ 77 | function mul(uint8 a, uint8 b) internal pure returns (uint8) { 78 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 79 | // benefit is lost if 'b' is also tested. 80 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 81 | if (a == 0) { 82 | return 0; 83 | } 84 | 85 | uint8 c = a * b; 86 | require(c / a == b, "SafeMath: multiplication overflow"); 87 | 88 | return c; 89 | } 90 | 91 | /** 92 | * @dev Returns the integer division of two unsigned integers. Reverts on 93 | * division by zero. The result is rounded towards zero. 94 | * 95 | * Counterpart to Solidity's `/` operator. Note: this function uses a 96 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 97 | * uses an invalid opcode to revert (consuming all remaining gas). 98 | * 99 | * Requirements: 100 | * 101 | * - The divisor cannot be zero. 102 | */ 103 | function div(uint8 a, uint8 b) internal pure returns (uint8) { 104 | return div(a, b, "SafeMath: division by zero"); 105 | } 106 | 107 | /** 108 | * @dev Returns the integer division of two unsigned integers. Reverts with custom message on 109 | * division by zero. The result is rounded towards zero. 110 | * 111 | * Counterpart to Solidity's `/` operator. Note: this function uses a 112 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 113 | * uses an invalid opcode to revert (consuming all remaining gas). 114 | * 115 | * Requirements: 116 | * 117 | * - The divisor cannot be zero. 118 | */ 119 | function div(uint8 a, uint8 b, string memory errorMessage) internal pure returns (uint8) { 120 | require(b > 0, errorMessage); 121 | uint8 c = a / b; 122 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 123 | 124 | return c; 125 | } 126 | 127 | /** 128 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 129 | * Reverts when dividing by zero. 130 | * 131 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 132 | * opcode (which leaves remaining gas untouched) while Solidity uses an 133 | * invalid opcode to revert (consuming all remaining gas). 134 | * 135 | * Requirements: 136 | * 137 | * - The divisor cannot be zero. 138 | */ 139 | function mod(uint8 a, uint8 b) internal pure returns (uint8) { 140 | return mod(a, b, "SafeMath: modulo by zero"); 141 | } 142 | 143 | /** 144 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 145 | * Reverts with custom message when dividing by zero. 146 | * 147 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 148 | * opcode (which leaves remaining gas untouched) while Solidity uses an 149 | * invalid opcode to revert (consuming all remaining gas). 150 | * 151 | * Requirements: 152 | * 153 | * - The divisor cannot be zero. 154 | */ 155 | function mod(uint8 a, uint8 b, string memory errorMessage) internal pure returns (uint8) { 156 | require(b != 0, errorMessage); 157 | return a % b; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /lib/UQ112x112.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity =0.6.12; 4 | 5 | // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format)) 6 | 7 | // range: [0, 2**112 - 1] 8 | // resolution: 1 / 2**112 9 | 10 | library UQ112x112 { 11 | uint224 constant Q112 = 2**112; 12 | 13 | // encode a uint112 as a UQ112x112 14 | function encode(uint112 y) internal pure returns (uint224 z) { 15 | z = uint224(y) * Q112; // never overflows 16 | } 17 | 18 | // divide a UQ112x112 by a uint112, returning a UQ112x112 19 | function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) { 20 | z = x / uint224(y); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/UniswapV2Library.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | import "../interfaces/IUniswapV2Pair.sol"; 7 | 8 | library UniswapV2Library { 9 | using SafeMath for uint256; 10 | 11 | // returns sorted token addresses, used to handle return values from pairs sorted in this order 12 | function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { 13 | require(tokenA != tokenB, "UniswapV2Library: IDENTICAL_ADDRESSES"); 14 | (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); 15 | require(token0 != address(0), "UniswapV2Library: ZERO_ADDRESS"); 16 | } 17 | 18 | // calculates the CREATE2 address for a pair without making any external calls 19 | function pairFor( 20 | address factory, 21 | address tokenA, 22 | address tokenB 23 | ) internal pure returns (address pair) { 24 | (address token0, address token1) = sortTokens(tokenA, tokenB); 25 | pair = address( 26 | uint256( 27 | keccak256( 28 | abi.encodePacked( 29 | hex"ff", 30 | factory, 31 | keccak256(abi.encodePacked(token0, token1)), 32 | hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" // init code hash 33 | ) 34 | ) 35 | ) 36 | ); 37 | } 38 | 39 | // fetches and sorts the reserves for a pair 40 | function getReserves( 41 | address factory, 42 | address tokenA, 43 | address tokenB 44 | ) internal view returns (uint256 reserveA, uint256 reserveB) { 45 | (address token0, ) = sortTokens(tokenA, tokenB); 46 | (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves(); 47 | (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0); 48 | } 49 | 50 | // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset 51 | function quote( 52 | uint256 amountA, 53 | uint256 reserveA, 54 | uint256 reserveB 55 | ) internal pure returns (uint256 amountB) { 56 | require(amountA > 0, "UniswapV2Library: INSUFFICIENT_AMOUNT"); 57 | require(reserveA > 0 && reserveB > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY"); 58 | amountB = amountA.mul(reserveB) / reserveA; 59 | } 60 | 61 | // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset 62 | function getAmountOut( 63 | uint256 amountIn, 64 | uint256 reserveIn, 65 | uint256 reserveOut 66 | ) internal pure returns (uint256 amountOut) { 67 | require(amountIn > 0, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT"); 68 | require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY"); 69 | uint256 amountInWithFee = amountIn.mul(997); 70 | uint256 numerator = amountInWithFee.mul(reserveOut); 71 | uint256 denominator = reserveIn.mul(1000).add(amountInWithFee); 72 | amountOut = numerator / denominator; 73 | } 74 | 75 | // given an output amount of an asset and pair reserves, returns a required input amount of the other asset 76 | function getAmountIn( 77 | uint256 amountOut, 78 | uint256 reserveIn, 79 | uint256 reserveOut 80 | ) internal pure returns (uint256 amountIn) { 81 | require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT"); 82 | require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY"); 83 | uint256 numerator = reserveIn.mul(amountOut).mul(1000); 84 | uint256 denominator = reserveOut.sub(amountOut).mul(997); 85 | amountIn = (numerator / denominator).add(1); 86 | } 87 | 88 | // performs chained getAmountOut calculations on any number of pairs 89 | function getAmountsOut( 90 | address factory, 91 | uint256 amountIn, 92 | address[] memory path 93 | ) internal view returns (uint256[] memory amounts) { 94 | require(path.length >= 2, "UniswapV2Library: INVALID_PATH"); 95 | amounts = new uint256[](path.length); 96 | amounts[0] = amountIn; 97 | for (uint256 i; i < path.length - 1; i++) { 98 | (uint256 reserveIn, uint256 reserveOut) = getReserves(factory, path[i], path[i + 1]); 99 | amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut); 100 | } 101 | } 102 | 103 | // performs chained getAmountIn calculations on any number of pairs 104 | function getAmountsIn( 105 | address factory, 106 | uint256 amountOut, 107 | address[] memory path 108 | ) internal view returns (uint256[] memory amounts) { 109 | require(path.length >= 2, "UniswapV2Library: INVALID_PATH"); 110 | amounts = new uint256[](path.length); 111 | amounts[amounts.length - 1] = amountOut; 112 | for (uint256 i = path.length - 1; i > 0; i--) { 113 | (uint256 reserveIn, uint256 reserveOut) = getReserves(factory, path[i - 1], path[i]); 114 | amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /lib/UniswapV2OracleLibrary.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | import "./FixedPoint.sol"; 6 | import "../interfaces/IUniswapV2Pair.sol"; 7 | 8 | // library with helper methods for oracles that are concerned with computing average prices 9 | library UniswapV2OracleLibrary { 10 | using FixedPoint for *; 11 | 12 | // helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - 1] 13 | function currentBlockTimestamp() internal view returns (uint32) { 14 | return uint32(block.timestamp % 2**32); 15 | } 16 | 17 | // produces the cumulative price using counterfactuals to save gas and avoid a call to sync. 18 | function currentCumulativePrices(address pair) 19 | internal 20 | view 21 | returns ( 22 | uint256 price0Cumulative, 23 | uint256 price1Cumulative, 24 | uint32 blockTimestamp 25 | ) 26 | { 27 | blockTimestamp = currentBlockTimestamp(); 28 | price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast(); 29 | price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast(); 30 | 31 | // if time has elapsed since the last update on the pair, mock the accumulated price values 32 | (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IUniswapV2Pair(pair).getReserves(); 33 | if (blockTimestampLast != blockTimestamp) { 34 | // subtraction overflow is desired 35 | uint32 timeElapsed = blockTimestamp - blockTimestampLast; 36 | // addition overflow is desired 37 | // counterfactual 38 | price0Cumulative += uint256(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsed; 39 | // counterfactual 40 | price1Cumulative += uint256(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsed; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /owner/Operator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/GSN/Context.sol"; 6 | import "@openzeppelin/contracts/access/Ownable.sol"; 7 | 8 | contract Operator is Context, Ownable { 9 | address private _operator; 10 | 11 | event OperatorTransferred(address indexed previousOperator, address indexed newOperator); 12 | 13 | constructor() internal { 14 | _operator = _msgSender(); 15 | emit OperatorTransferred(address(0), _operator); 16 | } 17 | 18 | function operator() public view returns (address) { 19 | return _operator; 20 | } 21 | 22 | modifier onlyOperator() { 23 | require(_operator == msg.sender, "operator: caller is not the operator"); 24 | _; 25 | } 26 | 27 | function isOperator() public view returns (bool) { 28 | return _msgSender() == _operator; 29 | } 30 | 31 | function transferOperator(address newOperator_) public onlyOwner { 32 | _transferOperator(newOperator_); 33 | } 34 | 35 | function _transferOperator(address newOperator_) internal { 36 | require(newOperator_ != address(0), "operator: zero address given for new operator"); 37 | emit OperatorTransferred(address(0), newOperator_); 38 | _operator = newOperator_; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /utils/ContractGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | contract ContractGuard { 6 | mapping(uint256 => mapping(address => bool)) private _status; 7 | 8 | function checkSameOriginReentranted() internal view returns (bool) { 9 | return _status[block.number][tx.origin]; 10 | } 11 | 12 | function checkSameSenderReentranted() internal view returns (bool) { 13 | return _status[block.number][msg.sender]; 14 | } 15 | 16 | modifier onlyOneBlock() { 17 | require(!checkSameOriginReentranted(), "ContractGuard: one block, one function"); 18 | require(!checkSameSenderReentranted(), "ContractGuard: one block, one function"); 19 | 20 | _; 21 | 22 | _status[block.number][tx.origin] = true; 23 | _status[block.number][msg.sender] = true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /utils/Epoch.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.0; 4 | 5 | import '@openzeppelin/contracts/math/SafeMath.sol'; 6 | 7 | import '../owner/Operator.sol'; 8 | 9 | contract Epoch is Operator { 10 | using SafeMath for uint256; 11 | 12 | uint256 private period; 13 | uint256 private startTime; 14 | uint256 private lastEpochTime; 15 | uint256 private epoch; 16 | 17 | /* ========== CONSTRUCTOR ========== */ 18 | 19 | constructor( 20 | uint256 _period, 21 | uint256 _startTime, 22 | uint256 _startEpoch 23 | ) public { 24 | period = _period; 25 | startTime = _startTime; 26 | epoch = _startEpoch; 27 | lastEpochTime = startTime.sub(period); 28 | } 29 | 30 | /* ========== Modifier ========== */ 31 | 32 | modifier checkStartTime { 33 | require(now >= startTime, 'Epoch: not started yet'); 34 | 35 | _; 36 | } 37 | 38 | modifier checkEpoch { 39 | uint256 _nextEpochPoint = nextEpochPoint(); 40 | if (now < _nextEpochPoint) { 41 | require(msg.sender == operator(), 'Epoch: only operator allowed for pre-epoch'); 42 | _; 43 | } else { 44 | _; 45 | 46 | for (;;) { 47 | lastEpochTime = _nextEpochPoint; 48 | ++epoch; 49 | _nextEpochPoint = nextEpochPoint(); 50 | if (now < _nextEpochPoint) break; 51 | } 52 | } 53 | } 54 | 55 | /* ========== VIEW FUNCTIONS ========== */ 56 | 57 | function getCurrentEpoch() public view returns (uint256) { 58 | return epoch; 59 | } 60 | 61 | function getPeriod() public view returns (uint256) { 62 | return period; 63 | } 64 | 65 | function getStartTime() public view returns (uint256) { 66 | return startTime; 67 | } 68 | 69 | function getLastEpochTime() public view returns (uint256) { 70 | return lastEpochTime; 71 | } 72 | 73 | function nextEpochPoint() public view returns (uint256) { 74 | return lastEpochTime.add(period); 75 | } 76 | 77 | /* ========== GOVERNANCE ========== */ 78 | 79 | function setPeriod(uint256 _period) external onlyOperator { 80 | require(_period >= 1 hours && _period <= 48 hours, '_period: out of range'); 81 | period = _period; 82 | } 83 | 84 | function setEpoch(uint256 _epoch) external onlyOperator { 85 | epoch = _epoch; 86 | } 87 | } 88 | --------------------------------------------------------------------------------