From 52272ecb867d50393b805fa43384d63904d61115 Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Wed, 23 Mar 2022 23:29:32 +0530 Subject: [PATCH 01/55] WIP: gauge-proxy-v2 --- src/dill/gauge-proxy-v2.sol | 931 +++++++++++++++++++++ src/dill/scripts/deploy_gauge_proxy_V2.js | 18 + src/dill/scripts/upgrade_gauge_proxy_v2.js | 13 + 3 files changed, 962 insertions(+) create mode 100644 src/dill/gauge-proxy-v2.sol create mode 100644 src/dill/scripts/deploy_gauge_proxy_V2.js create mode 100644 src/dill/scripts/upgrade_gauge_proxy_v2.js diff --git a/src/dill/gauge-proxy-v2.sol b/src/dill/gauge-proxy-v2.sol new file mode 100644 index 000000000..8ebd05a35 --- /dev/null +++ b/src/dill/gauge-proxy-v2.sol @@ -0,0 +1,931 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.7; //^0.7.5; + +library SafeMath { + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a, "add: +"); + + return c; + } + + function add( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a, errorMessage); + + return c; + } + + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + return sub(a, b, "sub: -"); + } + + function sub( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + require(b <= a, errorMessage); + uint256 c = a - b; + + return c; + } + + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b, "mul: *"); + + return c; + } + + function mul( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b, errorMessage); + + return c; + } + + function div(uint256 a, uint256 b) internal pure returns (uint256) { + return div(a, b, "div: /"); + } + + function div( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + require(b > 0, errorMessage); + uint256 c = a / b; + + return c; + } +} + +library Address { + function isContract(address account) internal view returns (bool) { + bytes32 codehash; + bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; + // solhint-disable-next-line no-inline-assembly + assembly { + codehash := extcodehash(account) + } + return (codehash != 0x0 && codehash != accountHash); + } + + function toPayable(address account) + internal + pure + returns (address payable) + { + return address(uint160(account)); + } + + function sendValue(address payable recipient, uint256 amount) internal { + require( + address(this).balance >= amount, + "Address: insufficient balance" + ); + + // solhint-disable-next-line avoid-call-value + (bool success, ) = recipient.call{value: amount}(""); + require( + success, + "Address: unable to send value, recipient may have reverted" + ); + } +} + +interface IERC20 { + function totalSupply() external view returns (uint256); + + function balanceOf(address account) external view returns (uint256); + + function transfer(address recipient, uint256 amount) + external + returns (bool); + + function allowance(address owner, address spender) + external + view + returns (uint256); + + function approve(address spender, uint256 amount) external returns (bool); + + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool); +} + +library SafeERC20 { + using SafeMath for uint256; + using Address for address; + + function safeTransfer( + IERC20 token, + address to, + uint256 value + ) internal { + callOptionalReturn( + token, + abi.encodeWithSelector(token.transfer.selector, to, value) + ); + } + + function safeTransferFrom( + IERC20 token, + address from, + address to, + uint256 value + ) internal { + callOptionalReturn( + token, + abi.encodeWithSelector(token.transferFrom.selector, from, to, value) + ); + } + + function safeApprove( + IERC20 token, + address spender, + uint256 value + ) internal { + require( + (value == 0) || (token.allowance(address(this), spender) == 0), + "SafeERC20: approve from non-zero to non-zero allowance" + ); + callOptionalReturn( + token, + abi.encodeWithSelector(token.approve.selector, spender, value) + ); + } + + function safeIncreaseAllowance( + IERC20 token, + address spender, + uint256 value + ) internal { + uint256 newAllowance = token.allowance(address(this), spender).add( + value + ); + callOptionalReturn( + token, + abi.encodeWithSelector( + token.approve.selector, + spender, + newAllowance + ) + ); + } + + function safeDecreaseAllowance( + IERC20 token, + address spender, + uint256 value + ) internal { + uint256 newAllowance = token.allowance(address(this), spender).sub( + value, + "SafeERC20: decreased allowance below zero" + ); + callOptionalReturn( + token, + abi.encodeWithSelector( + token.approve.selector, + spender, + newAllowance + ) + ); + } + + function callOptionalReturn(IERC20 token, bytes memory data) private { + require(address(token).isContract(), "SafeERC20: call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = address(token).call(data); + require(success, "SafeERC20: low-level call failed"); + + if (returndata.length > 0) { + // Return data is optional + // solhint-disable-next-line max-line-length + require( + abi.decode(returndata, (bool)), + "SafeERC20: ERC20 operation did not succeed" + ); + } + } +} + +library Math { + /** + * @dev Returns the largest of two numbers. + */ + function max(uint256 a, uint256 b) internal pure returns (uint256) { + return a >= b ? a : b; + } + + /** + * @dev Returns the smallest of two numbers. + */ + function min(uint256 a, uint256 b) internal pure returns (uint256) { + return a < b ? a : b; + } + + /** + * @dev Returns the average of two numbers. The result is rounded towards + * zero. + */ + function average(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b) / 2 can overflow, so we distribute + return (a / 2) + (b / 2) + (((a % 2) + (b % 2)) / 2); + } +} + +/** + * @dev Contract module that helps prevent reentrant calls to a function. + * + * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier + * available, which can be applied to functions to make sure there are no nested + * (reentrant) calls to them. + * + * Note that because there is a single `nonReentrant` guard, functions marked as + * `nonReentrant` may not call one another. This can be worked around by making + * those functions `private`, and then adding `external` `nonReentrant` entry + * points to them. + * + * TIP: If you would like to learn more about reentrancy and alternative ways + * to protect against it, check out our blog post + * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + */ +abstract contract ReentrancyGuard { + // Booleans are more expensive than uint256 or any type that takes up a full + // word because each write operation emits an extra SLOAD to first read the + // slot's contents, replace the bits taken up by the boolean, and then write + // back. This is the compiler's defense against contract upgrades and + // pointer aliasing, and it cannot be disabled. + + // The values being non-zero value makes deployment a bit more expensive, + // but in exchange the refund on every call to nonReentrant will be lower in + // amount. Since refunds are capped to a percentage of the total + // transaction's gas, it is best to keep them low in cases like this one, to + // increase the likelihood of the full refund coming into effect. + uint256 private constant _NOT_ENTERED = 1; + uint256 private constant _ENTERED = 2; + + uint256 private _status; + + constructor() public { + _status = _NOT_ENTERED; + } + + /** + * @dev Prevents a contract from calling itself, directly or indirectly. + * Calling a `nonReentrant` function from another `nonReentrant` + * function is not supported. It is possible to prevent this from happening + * by making the `nonReentrant` function external, and make it call a + * `private` function that does the actual work. + */ + modifier nonReentrant() { + // On the first call to nonReentrant, _notEntered will be true + require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); + + // Any calls to nonReentrant after this point will fail + _status = _ENTERED; + + _; + + // By storing the original value once again, a refund is triggered (see + // https://eips.ethereum.org/EIPS/eip-2200) + _status = _NOT_ENTERED; + } +} + +contract Gauge is ReentrancyGuard { + using SafeMath for uint256; + using SafeERC20 for IERC20; + + IERC20 public constant PICKLE = + IERC20(0x429881672B9AE42b8EbA0E26cD9C73711b891Ca5); + IERC20 public constant DILL = + IERC20(0xbBCf169eE191A1Ba7371F30A1C344bFC498b29Cf); + address public constant TREASURY = + address(0x066419EaEf5DE53cc5da0d8702b990c5bc7D1AB3); + + IERC20 public immutable TOKEN; + address public immutable DISTRIBUTION; + uint256 public constant DURATION = 7 days; + + uint256 public periodFinish = 0; + uint256 public rewardRate = 0; + uint256 public lastUpdateTime; + uint256 public rewardPerTokenStored; + + modifier onlyDistribution() { + require( + msg.sender == DISTRIBUTION, + "Caller is not RewardsDistribution contract" + ); + _; + } + + mapping(address => uint256) public userRewardPerTokenPaid; + mapping(address => uint256) public rewards; + + uint256 private _totalSupply; + uint256 public derivedSupply; + mapping(address => uint256) private _balances; + mapping(address => uint256) public derivedBalances; + mapping(address => uint256) private _base; + + constructor(address _token) public { + TOKEN = IERC20(_token); + DISTRIBUTION = msg.sender; + } + + function totalSupply() external view returns (uint256) { + return _totalSupply; + } + + function balanceOf(address account) external view returns (uint256) { + return _balances[account]; + } + + function lastTimeRewardApplicable() public view returns (uint256) { + return Math.min(block.timestamp, periodFinish); + } + + function rewardPerToken() public view returns (uint256) { + if (_totalSupply == 0) { + return rewardPerTokenStored; + } + return + rewardPerTokenStored.add( + lastTimeRewardApplicable() + .sub(lastUpdateTime) + .mul(rewardRate) + .mul(1e18) + .div(derivedSupply) + ); + } + + function derivedBalance(address account) public view returns (uint256) { + uint256 _balance = _balances[account]; + uint256 _derived = _balance.mul(40).div(100); + uint256 _adjusted = ( + _totalSupply.mul(DILL.balanceOf(account)).div(DILL.totalSupply()) + ).mul(60).div(100); + return Math.min(_derived.add(_adjusted), _balance); + } + + function kick(address account) public { + uint256 _derivedBalance = derivedBalances[account]; + derivedSupply = derivedSupply.sub(_derivedBalance); + _derivedBalance = derivedBalance(account); + derivedBalances[account] = _derivedBalance; + derivedSupply = derivedSupply.add(_derivedBalance); + } + + function earned(address account) public view returns (uint256) { + return + derivedBalances[account] + .mul(rewardPerToken().sub(userRewardPerTokenPaid[account])) + .div(1e18) + .add(rewards[account]); + } + + function getRewardForDuration() external view returns (uint256) { + return rewardRate.mul(DURATION); + } + + function depositAll() external { + _deposit(TOKEN.balanceOf(msg.sender), msg.sender); + } + + function deposit(uint256 amount) external { + _deposit(amount, msg.sender); + } + + function depositFor(uint256 amount, address account) external { + _deposit(amount, account); + } + + function _deposit(uint256 amount, address account) + internal + nonReentrant + updateReward(account) + { + require(amount > 0, "Cannot stake 0"); + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + emit Staked(account, amount); + TOKEN.safeTransferFrom(account, address(this), amount); + } + + function withdrawAll() external { + _withdraw(_balances[msg.sender]); + } + + function withdraw(uint256 amount) external { + _withdraw(amount); + } + + function _withdraw(uint256 amount) + internal + nonReentrant + updateReward(msg.sender) + { + require(amount > 0, "Cannot withdraw 0"); + _totalSupply = _totalSupply.sub(amount); + _balances[msg.sender] = _balances[msg.sender].sub(amount); + TOKEN.safeTransfer(msg.sender, amount); + emit Withdrawn(msg.sender, amount); + } + + function getReward() public nonReentrant updateReward(msg.sender) { + uint256 reward = rewards[msg.sender]; + if (reward > 0) { + rewards[msg.sender] = 0; + PICKLE.safeTransfer(msg.sender, reward); + emit RewardPaid(msg.sender, reward); + } + } + + function exit() external { + _withdraw(_balances[msg.sender]); + getReward(); + } + + function notifyRewardAmount(uint256 reward) + external + onlyDistribution + updateReward(address(0)) + { + PICKLE.safeTransferFrom(DISTRIBUTION, address(this), reward); + if (block.timestamp >= periodFinish) { + rewardRate = reward.div(DURATION); + } else { + uint256 remaining = periodFinish.sub(block.timestamp); + uint256 leftover = remaining.mul(rewardRate); + rewardRate = reward.add(leftover).div(DURATION); + } + + // Ensure the provided reward amount is not more than the balance in the contract. + // This keeps the reward rate in the right range, preventing overflows due to + // very high values of rewardRate in the earned and rewardsPerToken functions; + // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow. + uint256 balance = PICKLE.balanceOf(address(this)); + require( + rewardRate <= balance.div(DURATION), + "Provided reward too high" + ); + + lastUpdateTime = block.timestamp; + periodFinish = block.timestamp.add(DURATION); + emit RewardAdded(reward); + } + + modifier updateReward(address account) { + rewardPerTokenStored = rewardPerToken(); + lastUpdateTime = lastTimeRewardApplicable(); + if (account != address(0)) { + rewards[account] = earned(account); + userRewardPerTokenPaid[account] = rewardPerTokenStored; + } + _; + if (account != address(0)) { + kick(account); + } + } + + event RewardAdded(uint256 reward); + event Staked(address indexed user, uint256 amount); + event Withdrawn(address indexed user, uint256 amount); + event RewardPaid(address indexed user, uint256 reward); +} + +interface MasterChef { + function deposit(uint256, uint256) external; + + function withdraw(uint256, uint256) external; + + function userInfo(uint256, address) + external + view + returns (uint256, uint256); +} + +contract ProtocolGovernance { + /// @notice governance address for the governance contract + address public governance; + address public pendingGovernance; + + /** + * @notice Allows governance to change governance (for future upgradability) + * @param _governance new governance address to set + */ + function setGovernance(address _governance) external { + require(msg.sender == governance, "setGovernance: !gov"); + pendingGovernance = _governance; + } + + /** + * @notice Allows pendingGovernance to accept their role as governance (protection pattern) + */ + function acceptGovernance() external { + require( + msg.sender == pendingGovernance, + "acceptGovernance: !pendingGov" + ); + governance = pendingGovernance; + } +} + +contract MasterDill { + using SafeMath for uint256; + + /// @notice EIP-20 token name for this token + string public constant name = "Master DILL"; + + /// @notice EIP-20 token symbol for this token + string public constant symbol = "mDILL"; + + /// @notice EIP-20 token decimals for this token + uint8 public constant decimals = 18; + + /// @notice Total number of tokens in circulation + uint256 public totalSupply = 1e18; + + mapping(address => mapping(address => uint256)) internal allowances; + mapping(address => uint256) internal balances; + + /// @notice The standard EIP-20 transfer event + event Transfer(address indexed from, address indexed to, uint256 amount); + + /// @notice The standard EIP-20 approval event + event Approval( + address indexed owner, + address indexed spender, + uint256 amount + ); + + constructor() public { + balances[msg.sender] = 1e18; + emit Transfer(address(0x0), msg.sender, 1e18); + } + + /** + * @notice Get the number of tokens `spender` is approved to spend on behalf of `account` + * @param account The address of the account holding the funds + * @param spender The address of the account spending the funds + * @return The number of tokens approved + */ + function allowance(address account, address spender) + external + view + returns (uint256) + { + return allowances[account][spender]; + } + + /** + * @notice Approve `spender` to transfer up to `amount` from `src` + * @dev This will overwrite the approval amount for `spender` + * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) + * @param spender The address of the account which may transfer tokens + * @param amount The number of tokens that are approved (2^256-1 means infinite) + * @return Whether or not the approval succeeded + */ + function approve(address spender, uint256 amount) external returns (bool) { + allowances[msg.sender][spender] = amount; + + emit Approval(msg.sender, spender, amount); + return true; + } + + /** + * @notice Get the number of tokens held by the `account` + * @param account The address of the account to get the balance of + * @return The number of tokens held + */ + function balanceOf(address account) external view returns (uint256) { + return balances[account]; + } + + /** + * @notice Transfer `amount` tokens from `msg.sender` to `dst` + * @param dst The address of the destination account + * @param amount The number of tokens to transfer + * @return Whether or not the transfer succeeded + */ + function transfer(address dst, uint256 amount) external returns (bool) { + _transferTokens(msg.sender, dst, amount); + return true; + } + + /** + * @notice Transfer `amount` tokens from `src` to `dst` + * @param src The address of the source account + * @param dst The address of the destination account + * @param amount The number of tokens to transfer + * @return Whether or not the transfer succeeded + */ + function transferFrom( + address src, + address dst, + uint256 amount + ) external returns (bool) { + address spender = msg.sender; + uint256 spenderAllowance = allowances[src][spender]; + + if (spender != src && spenderAllowance != uint256(-1)) { + uint256 newAllowance = spenderAllowance.sub( + amount, + "transferFrom: exceeds spender allowance" + ); + allowances[src][spender] = newAllowance; + + emit Approval(src, spender, newAllowance); + } + + _transferTokens(src, dst, amount); + return true; + } + + function _transferTokens( + address src, + address dst, + uint256 amount + ) internal { + require(src != address(0), "_transferTokens: zero address"); + require(dst != address(0), "_transferTokens: zero address"); + + balances[src] = balances[src].sub( + amount, + "_transferTokens: exceeds balance" + ); + balances[dst] = balances[dst].add(amount, "_transferTokens: overflows"); + emit Transfer(src, dst, amount); + } +} + +/** + * @title Initializable + * + * @dev Helper contract to support initializer functions. To use it, replace + * the constructor with a function that has the `initializer` modifier. + * WARNING: Unlike constructors, initializer functions must be manually + * invoked. This applies both to deploying an Initializable contract, as well + * as extending an Initializable contract via inheritance. + * WARNING: When used with inheritance, manual care must be taken to not invoke + * a parent initializer twice, or ensure that all initializers are idempotent, + * because this is not dealt with automatically as with constructors. + */ +contract Initializable { + /** + * @dev Indicates that the contract has been initialized. + */ + bool private initialized; + + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool private initializing; + + /** + * @dev Modifier to use in the initializer function of a contract. + */ + modifier initializer() { + require( + initializing || isConstructor() || !initialized, + "Contract instance has already been initialized" + ); + + bool isTopLevelCall = !initializing; + if (isTopLevelCall) { + initializing = true; + initialized = true; + } + + _; + + if (isTopLevelCall) { + initializing = false; + } + } + + /// @dev Returns true if and only if the function is running in the constructor + function isConstructor() private view returns (bool) { + // extcodesize checks the size of the code stored in an address, and + // address returns the current address. Since the code is still not + // deployed when running a constructor, any checks on its code size will + // yield zero, making it an effective way to detect if a contract is + // under construction or not. + address self = address(this); + uint256 cs; + assembly { + cs := extcodesize(self) + } + return cs == 0; + } + + // Reserved storage space to allow for layout changes in the future. + uint256[50] private ______gap; +} + +contract GaugeProxyV2 is ProtocolGovernance, Initializable { + using SafeMath for uint256; + using SafeERC20 for IERC20; + + MasterChef public constant MASTER = + MasterChef(0xbD17B1ce622d73bD438b9E658acA5996dc394b0d); + IERC20 public constant DILL = + IERC20(0xbBCf169eE191A1Ba7371F30A1C344bFC498b29Cf); + IERC20 public constant PICKLE = + IERC20(0x429881672B9AE42b8EbA0E26cD9C73711b891Ca5); + + IERC20 public immutable TOKEN = IERC20(address(new MasterDill())); + + uint256 public pid; + uint256 public totalWeight; + + address[] internal _tokens; + mapping(address => address) public gauges; // token => gauge + mapping(address => uint256) public weights; // token => weight + mapping(address => mapping(address => uint256)) public votes; // msg.sender => votes + mapping(address => address[]) public tokenVote; // msg.sender => token + mapping(address => uint256) public usedWeights; // msg.sender => total voting weight of user + + function tokens() external view returns (address[] memory) { + return _tokens; + } + + function getGauge(address _token) external view returns (address) { + return gauges[_token]; + } + + function initialize() public initializer { + // TOKEN = IERC20(address(new MasterDill())); + governance = msg.sender; + } + + // Reset votes to 0 + function reset() external { + _reset(msg.sender); + } + + // Reset votes to 0 + function _reset(address _owner) internal { + address[] storage _tokenVote = tokenVote[_owner]; + uint256 _tokenVoteCnt = _tokenVote.length; + + for (uint256 i = 0; i < _tokenVoteCnt; i++) { + address _token = _tokenVote[i]; + uint256 _votes = votes[_owner][_token]; + + if (_votes > 0) { + totalWeight = totalWeight.sub(_votes); + weights[_token] = weights[_token].sub(_votes); + + votes[_owner][_token] = 0; + } + } + + delete tokenVote[_owner]; + } + + // Adjusts _owner's votes according to latest _owner's DILL balance + function poke(address _owner) public { + address[] memory _tokenVote = tokenVote[_owner]; + uint256 _tokenCnt = _tokenVote.length; + uint256[] memory _weights = new uint256[](_tokenCnt); + + uint256 _prevUsedWeight = usedWeights[_owner]; + uint256 _weight = DILL.balanceOf(_owner); + + for (uint256 i = 0; i < _tokenCnt; i++) { + uint256 _prevWeight = votes[_owner][_tokenVote[i]]; + _weights[i] = _prevWeight.mul(_weight).div(_prevUsedWeight); + } + + _vote(_owner, _tokenVote, _weights); + } + + function _vote( + address _owner, + address[] memory _tokenVote, + uint256[] memory _weights + ) internal { + // _weights[i] = percentage * 100 + _reset(_owner); + uint256 _tokenCnt = _tokenVote.length; + uint256 _weight = DILL.balanceOf(_owner); + uint256 _totalVoteWeight = 0; + uint256 _usedWeight = 0; + + for (uint256 i = 0; i < _tokenCnt; i++) { + _totalVoteWeight = _totalVoteWeight.add(_weights[i]); + } + + for (uint256 i = 0; i < _tokenCnt; i++) { + address _token = _tokenVote[i]; + address _gauge = gauges[_token]; + uint256 _tokenWeight = _weights[i].mul(_weight).div( + _totalVoteWeight + ); + + if (_gauge != address(0x0)) { + _usedWeight = _usedWeight.add(_tokenWeight); + totalWeight = totalWeight.add(_tokenWeight); + weights[_token] = weights[_token].add(_tokenWeight); + tokenVote[_owner].push(_token); + votes[_owner][_token] = _tokenWeight; + } + } + + usedWeights[_owner] = _usedWeight; + } + + // Vote with DILL on a gauge + function vote(address[] calldata _tokenVote, uint256[] calldata _weights) + external + { + require(_tokenVote.length == _weights.length); + _vote(msg.sender, _tokenVote, _weights); + } + + // Add new token gauge + function addGauge(address _token) external { + require(msg.sender == governance, "!gov"); + require(gauges[_token] == address(0x0), "exists"); + gauges[_token] = address(new Gauge(_token)); + _tokens.push(_token); + } + + // Sets MasterChef PID + function setPID(uint256 _pid) external { + require(msg.sender == governance, "!gov"); + require(pid == 0, "pid has already been set"); + require(_pid > 0, "invalid pid"); + pid = _pid; + } + + // Deposits mDILL into MasterChef + function deposit() public { + require(pid > 0, "pid not initialized"); + IERC20 _token = TOKEN; + uint256 _balance = _token.balanceOf(address(this)); + _token.safeApprove(address(MASTER), 0); + _token.safeApprove(address(MASTER), _balance); + MASTER.deposit(pid, _balance); + } + + // Fetches Pickle + function collect() public { + (uint256 _locked, ) = MASTER.userInfo(pid, address(this)); + MASTER.withdraw(pid, _locked); + deposit(); + } + + function length() external view returns (uint256) { + return _tokens.length; + } + + function distribute(uint256 _start, uint256 _end) external { + collect(); + require(_start < _end, "bad _start"); + require(_end <= _tokens.length, "bad _end"); + uint256 _balance = PICKLE.balanceOf(address(this)); + if (_balance > 0 && totalWeight > 0) { + for (uint256 i = 0; i < _tokens.length; i++) { + address _token = _tokens[i]; + address _gauge = gauges[_token]; + uint256 _reward = _balance.mul(weights[_token]).div( + totalWeight + ); + if (_reward > 0) { + PICKLE.safeApprove(_gauge, 0); + PICKLE.safeApprove(_gauge, _reward); + Gauge(_gauge).notifyRewardAmount(_reward); + } + } + } + } +} diff --git a/src/dill/scripts/deploy_gauge_proxy_V2.js b/src/dill/scripts/deploy_gauge_proxy_V2.js new file mode 100644 index 000000000..ccf52f120 --- /dev/null +++ b/src/dill/scripts/deploy_gauge_proxy_V2.js @@ -0,0 +1,18 @@ +const { ethers, upgrades } = require("hardhat"); + +async function main() { + console.log("Getting contract..."); + const gaugeProxyV2 = await ethers.getContractFactory("contracts/gauge-proxy-v2.sol:GaugeProxyV2"); + console.log("Deploying GaugeProxyV2..."); + const GaugeProxyV2 = await upgrades.deployProxy(gaugeProxyV2); + await GaugeProxyV2.deployed(); + console.log("GaugeProxyV2 deployed to:", GaugeProxyV2.address); +} + + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/src/dill/scripts/upgrade_gauge_proxy_v2.js b/src/dill/scripts/upgrade_gauge_proxy_v2.js new file mode 100644 index 000000000..3e8f7e742 --- /dev/null +++ b/src/dill/scripts/upgrade_gauge_proxy_v2.js @@ -0,0 +1,13 @@ +const { ethers, upgrades } = require("hardhat"); + +async function main() { + const GaugeProxyV2 = await ethers.getContractFactory("gaugeProxyV2"); + console.log("Upgrading GaugeProxy..."); + await upgrades.upgradeProxy( + "", + GaugeProxyV2 + ); + console.log("gaugeProxy upgraded"); +} + +main(); From 2047c4cbaf0b485325933231cfb8e0359ff3c0a2 Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Wed, 30 Mar 2022 18:14:56 +0530 Subject: [PATCH 02/55] feat: add periodization to voting --- src/dill/gauge-proxy-v2.sol | 51 +++++++++++++++++++--- src/dill/scripts/deploy_gauge_proxy_V2.js | 19 ++++++-- src/dill/scripts/upgrade_gauge_proxy_v2.js | 20 +++++++-- 3 files changed, 75 insertions(+), 15 deletions(-) diff --git a/src/dill/gauge-proxy-v2.sol b/src/dill/gauge-proxy-v2.sol index 8ebd05a35..f4f7d8787 100644 --- a/src/dill/gauge-proxy-v2.sol +++ b/src/dill/gauge-proxy-v2.sol @@ -761,7 +761,7 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { IERC20 public constant PICKLE = IERC20(0x429881672B9AE42b8EbA0E26cD9C73711b891Ca5); - IERC20 public immutable TOKEN = IERC20(address(new MasterDill())); + IERC20 public TOKEN; uint256 public pid; uint256 public totalWeight; @@ -773,6 +773,24 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { mapping(address => address[]) public tokenVote; // msg.sender => token mapping(address => uint256) public usedWeights; // msg.sender => total voting weight of user + uint256 public constant week = 7 days; + uint256 public firstDistribution; // epoch time stamp + uint256 public prevDistributionId; + uint256 public currentId; + mapping(uint256 => mapping(address => mapping(address => uint256))) + public ballot; // id(block.timestamp) => votes + + modifier epochDistribute() { + uint256 time = block.timestamp - + (prevDistributionId * 604800) + + firstDistribution; + require( + time > week, + "GaugeProxyV2: distribution already done for this week" + ); + _; + } + function tokens() external view returns (address[] memory) { return _tokens; } @@ -781,9 +799,10 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { return gauges[_token]; } - function initialize() public initializer { - // TOKEN = IERC20(address(new MasterDill())); + function initialize(uint256 _firstDistribution) public initializer { + TOKEN = IERC20(address(new MasterDill())); governance = msg.sender; + firstDistribution = _firstDistribution; } // Reset votes to 0 @@ -793,6 +812,7 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { // Reset votes to 0 function _reset(address _owner) internal { + // lastVote[_owner] = 0; address[] storage _tokenVote = tokenVote[_owner]; uint256 _tokenVoteCnt = _tokenVote.length; @@ -852,11 +872,19 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { ); if (_gauge != address(0x0)) { + if ( + (firstDistribution + (currentId * 604800)) > block.timestamp + ) { + currentId += 1; + } _usedWeight = _usedWeight.add(_tokenWeight); totalWeight = totalWeight.add(_tokenWeight); weights[_token] = weights[_token].add(_tokenWeight); tokenVote[_owner].push(_token); votes[_owner][_token] = _tokenWeight; + // check if voting cycle is over then update ballotId + // add uers vote to ballot + ballot[currentId][_owner][_token] = _tokenWeight; } } @@ -869,6 +897,7 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { { require(_tokenVote.length == _weights.length); _vote(msg.sender, _tokenVote, _weights); + // lastVote[msg.sender] = block.timestamp; } // Add new token gauge @@ -908,13 +937,21 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { return _tokens.length; } - function distribute(uint256 _start, uint256 _end) external { + function distribute(uint256 _start, uint256 _end) external epochDistribute { + require(_start < _end, "GaugeProxyV2: bad _start"); + require(_end <= _tokens.length, "GaugeProxyV2: bad _end"); + require( + msg.sender == governance, + "GaugeProxyV2: only governance can distribute" + ); + if (_tokens.length == _end) { + prevDistributionId += 1; + } + require(prevDistributionId < currentId,"GaugeProxyV2: voting for current period in progress"); collect(); - require(_start < _end, "bad _start"); - require(_end <= _tokens.length, "bad _end"); uint256 _balance = PICKLE.balanceOf(address(this)); if (_balance > 0 && totalWeight > 0) { - for (uint256 i = 0; i < _tokens.length; i++) { + for (uint256 i = _start; i < _end; i++) { address _token = _tokens[i]; address _gauge = gauges[_token]; uint256 _reward = _balance.mul(weights[_token]).div( diff --git a/src/dill/scripts/deploy_gauge_proxy_V2.js b/src/dill/scripts/deploy_gauge_proxy_V2.js index ccf52f120..a1e2935c1 100644 --- a/src/dill/scripts/deploy_gauge_proxy_V2.js +++ b/src/dill/scripts/deploy_gauge_proxy_V2.js @@ -1,15 +1,26 @@ const { ethers, upgrades } = require("hardhat"); - +/** + * not working on mainnet fork, tested on mumbai + */ async function main() { console.log("Getting contract..."); - const gaugeProxyV2 = await ethers.getContractFactory("contracts/gauge-proxy-v2.sol:GaugeProxyV2"); + const gaugeProxyV2 = await ethers.getContractFactory( + "contracts/gauge-proxy-v2.sol:GaugeProxyV2" + ); console.log("Deploying GaugeProxyV2..."); - const GaugeProxyV2 = await upgrades.deployProxy(gaugeProxyV2); + const GaugeProxyV2 = await upgrades.deployProxy(gaugeProxyV2, { + initializer: "initialize", + }); await GaugeProxyV2.deployed(); console.log("GaugeProxyV2 deployed to:", GaugeProxyV2.address); + // const GaugeProxyV2 = gaugeProxyV2.attach( + // "0xc6f33472A3d0E3450bd866Bf7286e5EF6dbD0157" + // ); + // console.log(await GaugeProxyV2.test()); + // const pickle = await GaugeProxyV2.PICKLE(); + // console.log(pickle); } - main() .then(() => process.exit(0)) .catch((error) => { diff --git a/src/dill/scripts/upgrade_gauge_proxy_v2.js b/src/dill/scripts/upgrade_gauge_proxy_v2.js index 3e8f7e742..cd3de8c40 100644 --- a/src/dill/scripts/upgrade_gauge_proxy_v2.js +++ b/src/dill/scripts/upgrade_gauge_proxy_v2.js @@ -1,13 +1,25 @@ const { ethers, upgrades } = require("hardhat"); async function main() { - const GaugeProxyV2 = await ethers.getContractFactory("gaugeProxyV2"); + console.log("Getting contract..."); + const GaugeProxyV2 = await ethers.getContractFactory( + "contracts/gauge-proxy-v2.sol:GaugeProxyV2" + ); console.log("Upgrading GaugeProxy..."); - await upgrades.upgradeProxy( - "", + const upgrade = await upgrades.upgradeProxy( + "0xc6f33472A3d0E3450bd866Bf7286e5EF6dbD0157", // address of proxy deployed GaugeProxyV2 ); console.log("gaugeProxy upgraded"); + console.log(await upgrade.test()); + const pickle = await upgrade.PICKLE(); + console.log(pickle); } -main(); + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); From 42e3521f17bdf03fabb02688611756ed32dafd2f Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Thu, 31 Mar 2022 10:56:55 +0530 Subject: [PATCH 03/55] fix: periodized totalWeight --- src/dill/gauge-proxy-v2.sol | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/dill/gauge-proxy-v2.sol b/src/dill/gauge-proxy-v2.sol index f4f7d8787..20ab1aafb 100644 --- a/src/dill/gauge-proxy-v2.sol +++ b/src/dill/gauge-proxy-v2.sol @@ -778,7 +778,8 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { uint256 public prevDistributionId; uint256 public currentId; mapping(uint256 => mapping(address => mapping(address => uint256))) - public ballot; // id(block.timestamp) => votes + public ballot; // periodId => votes + mapping(uint256 => uint256) public totalWeightsPerPeriod; // periodId => totalWeight modifier epochDistribute() { uint256 time = block.timestamp - @@ -844,7 +845,6 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { uint256 _prevWeight = votes[_owner][_tokenVote[i]]; _weights[i] = _prevWeight.mul(_weight).div(_prevUsedWeight); } - _vote(_owner, _tokenVote, _weights); } @@ -872,6 +872,7 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { ); if (_gauge != address(0x0)) { + // check if voting cycle is over then update ballotId if ( (firstDistribution + (currentId * 604800)) > block.timestamp ) { @@ -882,7 +883,7 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { weights[_token] = weights[_token].add(_tokenWeight); tokenVote[_owner].push(_token); votes[_owner][_token] = _tokenWeight; - // check if voting cycle is over then update ballotId + totalWeightsPerPeriod[currentId] = totalWeight; // add uers vote to ballot ballot[currentId][_owner][_token] = _tokenWeight; } @@ -947,15 +948,19 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { if (_tokens.length == _end) { prevDistributionId += 1; } - require(prevDistributionId < currentId,"GaugeProxyV2: voting for current period in progress"); + require( + prevDistributionId < currentId, + "GaugeProxyV2: voting for current period in progress" + ); collect(); uint256 _balance = PICKLE.balanceOf(address(this)); - if (_balance > 0 && totalWeight > 0) { + uint256 _totalWeight = totalWeightsPerPeriod[currentId]; + if (_balance > 0 && _totalWeight > 0) { for (uint256 i = _start; i < _end; i++) { address _token = _tokens[i]; address _gauge = gauges[_token]; uint256 _reward = _balance.mul(weights[_token]).div( - totalWeight + _totalWeight ); if (_reward > 0) { PICKLE.safeApprove(_gauge, 0); From 7394d85a168c583db31ef8d7da303a26d6cc4b0c Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Thu, 31 Mar 2022 19:17:54 +0530 Subject: [PATCH 04/55] added periodization to weights --- src/dill/gauge-proxy-v2.sol | 39 +++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/dill/gauge-proxy-v2.sol b/src/dill/gauge-proxy-v2.sol index 20ab1aafb..b278fd634 100644 --- a/src/dill/gauge-proxy-v2.sol +++ b/src/dill/gauge-proxy-v2.sol @@ -777,9 +777,17 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { uint256 public firstDistribution; // epoch time stamp uint256 public prevDistributionId; uint256 public currentId; - mapping(uint256 => mapping(address => mapping(address => uint256))) - public ballot; // periodId => votes - mapping(uint256 => uint256) public totalWeightsPerPeriod; // periodId => totalWeight + + // mapping(uint256 => mapping(address => mapping(address => uint256))) public ballot; // periodId => votes + // mapping(uint256 => mapping(address => uint256)) public weightsPerPeriod; // periodId => weights + // mapping(uint256 => uint256) public totalWeightsPerPeriod; // periodId => totalWeight + + struct periodData { + mapping(address => mapping(address => uint256)) votes_; + mapping(address => uint256) weights_; + uint256 totalWeight_; + } + mapping(uint256 => periodData) public periods; // periodId => periodData modifier epochDistribute() { uint256 time = block.timestamp - @@ -883,9 +891,17 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { weights[_token] = weights[_token].add(_tokenWeight); tokenVote[_owner].push(_token); votes[_owner][_token] = _tokenWeight; - totalWeightsPerPeriod[currentId] = totalWeight; - // add uers vote to ballot - ballot[currentId][_owner][_token] = _tokenWeight; + // store weights of current period + // weightsPerPeriod[currentId][_token] = weights[_token]; + periods[currentId].weights_[_token] = weights[_token]; + + // store total weight of current period + // totalWeightsPerPeriod[currentId] = totalWeight; + periods[currentId].totalWeight_ = totalWeight; + + // add users vote to ballot + // ballot[currentId][_owner][_token] = _tokenWeight; + periods[currentId].votes_[_owner][_token] = _tokenWeight; } } @@ -941,10 +957,7 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { function distribute(uint256 _start, uint256 _end) external epochDistribute { require(_start < _end, "GaugeProxyV2: bad _start"); require(_end <= _tokens.length, "GaugeProxyV2: bad _end"); - require( - msg.sender == governance, - "GaugeProxyV2: only governance can distribute" - ); + require(msg.sender == governance,"GaugeProxyV2: only governance can distribute"); if (_tokens.length == _end) { prevDistributionId += 1; } @@ -954,12 +967,14 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { ); collect(); uint256 _balance = PICKLE.balanceOf(address(this)); - uint256 _totalWeight = totalWeightsPerPeriod[currentId]; + periodData storage _periodData = periods[prevDistributionId + 1]; + // uint256 _totalWeight = totalWeightsPerPeriod[prevDistributionId + 1]; + uint256 _totalWeight = _periodData.totalWeight_; if (_balance > 0 && _totalWeight > 0) { for (uint256 i = _start; i < _end; i++) { address _token = _tokens[i]; address _gauge = gauges[_token]; - uint256 _reward = _balance.mul(weights[_token]).div( + uint256 _reward = _balance.mul(_periodData.weights_[_token]).div( _totalWeight ); if (_reward > 0) { From 1668db676cf3fcfa6018e7c1c4baafeb14ab831a Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Thu, 31 Mar 2022 22:02:36 +0530 Subject: [PATCH 05/55] fix: updated currentId increment --- src/dill/gauge-proxy-v2.sol | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/dill/gauge-proxy-v2.sol b/src/dill/gauge-proxy-v2.sol index b278fd634..58d1163c6 100644 --- a/src/dill/gauge-proxy-v2.sol +++ b/src/dill/gauge-proxy-v2.sol @@ -778,10 +778,6 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { uint256 public prevDistributionId; uint256 public currentId; - // mapping(uint256 => mapping(address => mapping(address => uint256))) public ballot; // periodId => votes - // mapping(uint256 => mapping(address => uint256)) public weightsPerPeriod; // periodId => weights - // mapping(uint256 => uint256) public totalWeightsPerPeriod; // periodId => totalWeight - struct periodData { mapping(address => mapping(address => uint256)) votes_; mapping(address => uint256) weights_; @@ -868,6 +864,12 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { uint256 _totalVoteWeight = 0; uint256 _usedWeight = 0; + // check if voting cycle is over then update ballotId + if ( + (firstDistribution + (currentId * 604800)) > block.timestamp + ) { + currentId += 1; + } for (uint256 i = 0; i < _tokenCnt; i++) { _totalVoteWeight = _totalVoteWeight.add(_weights[i]); } @@ -880,27 +882,16 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { ); if (_gauge != address(0x0)) { - // check if voting cycle is over then update ballotId - if ( - (firstDistribution + (currentId * 604800)) > block.timestamp - ) { - currentId += 1; - } _usedWeight = _usedWeight.add(_tokenWeight); totalWeight = totalWeight.add(_tokenWeight); weights[_token] = weights[_token].add(_tokenWeight); tokenVote[_owner].push(_token); votes[_owner][_token] = _tokenWeight; // store weights of current period - // weightsPerPeriod[currentId][_token] = weights[_token]; periods[currentId].weights_[_token] = weights[_token]; - // store total weight of current period - // totalWeightsPerPeriod[currentId] = totalWeight; periods[currentId].totalWeight_ = totalWeight; - // add users vote to ballot - // ballot[currentId][_owner][_token] = _tokenWeight; periods[currentId].votes_[_owner][_token] = _tokenWeight; } } @@ -914,7 +905,6 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { { require(_tokenVote.length == _weights.length); _vote(msg.sender, _tokenVote, _weights); - // lastVote[msg.sender] = block.timestamp; } // Add new token gauge @@ -968,7 +958,6 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { collect(); uint256 _balance = PICKLE.balanceOf(address(this)); periodData storage _periodData = periods[prevDistributionId + 1]; - // uint256 _totalWeight = totalWeightsPerPeriod[prevDistributionId + 1]; uint256 _totalWeight = _periodData.totalWeight_; if (_balance > 0 && _totalWeight > 0) { for (uint256 i = _start; i < _end; i++) { From b373608b43581a3b013f7654acbea4ef6bf5e418 Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Fri, 1 Apr 2022 16:55:52 +0530 Subject: [PATCH 06/55] chore: refactoring and logic improvements --- src/dill/gauge-proxy-v2.sol | 80 ++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/src/dill/gauge-proxy-v2.sol b/src/dill/gauge-proxy-v2.sol index 58d1163c6..2bb812063 100644 --- a/src/dill/gauge-proxy-v2.sol +++ b/src/dill/gauge-proxy-v2.sol @@ -764,30 +764,28 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { IERC20 public TOKEN; uint256 public pid; - uint256 public totalWeight; address[] internal _tokens; mapping(address => address) public gauges; // token => gauge - mapping(address => uint256) public weights; // token => weight - mapping(address => mapping(address => uint256)) public votes; // msg.sender => votes - mapping(address => address[]) public tokenVote; // msg.sender => token - mapping(address => uint256) public usedWeights; // msg.sender => total voting weight of user uint256 public constant week = 7 days; + uint256 public constant weekSeconds = 604800; uint256 public firstDistribution; // epoch time stamp uint256 public prevDistributionId; uint256 public currentId; struct periodData { - mapping(address => mapping(address => uint256)) votes_; - mapping(address => uint256) weights_; - uint256 totalWeight_; + mapping(address => mapping(address => uint256)) votes; // msg.sender => votes + mapping(address => uint256) weights; // token => weight + mapping(address => address[]) tokenVote; // msg.sender => token + mapping(address => uint256) usedWeights; // msg.sender => total voting weight of user + uint256 totalWeight; } mapping(uint256 => periodData) public periods; // periodId => periodData modifier epochDistribute() { uint256 time = block.timestamp - - (prevDistributionId * 604800) + + (prevDistributionId * weekSeconds) + firstDistribution; require( time > week, @@ -817,36 +815,50 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { // Reset votes to 0 function _reset(address _owner) internal { + if ( + (firstDistribution + (currentId * weekSeconds)) > block.timestamp + ) { + currentId += 1; + } + periodData storage _periodData = periods[currentId]; + // lastVote[_owner] = 0; - address[] storage _tokenVote = tokenVote[_owner]; + address[] storage _tokenVote = _periodData.tokenVote[_owner]; uint256 _tokenVoteCnt = _tokenVote.length; for (uint256 i = 0; i < _tokenVoteCnt; i++) { address _token = _tokenVote[i]; - uint256 _votes = votes[_owner][_token]; + uint256 _votes = _periodData.votes[_owner][_token]; if (_votes > 0) { - totalWeight = totalWeight.sub(_votes); - weights[_token] = weights[_token].sub(_votes); + _periodData.totalWeight = _periodData.totalWeight.sub(_votes); + _periodData.weights[_token] = _periodData.weights[_token].sub(_votes); - votes[_owner][_token] = 0; + _periodData.votes[_owner][_token] = 0; } } - delete tokenVote[_owner]; + delete _periodData.tokenVote[_owner]; } // Adjusts _owner's votes according to latest _owner's DILL balance function poke(address _owner) public { - address[] memory _tokenVote = tokenVote[_owner]; + if ( + (firstDistribution + (currentId * weekSeconds)) > block.timestamp + ) { + currentId += 1; + } + periodData storage _periodData = periods[currentId]; + + address[] memory _tokenVote = _periodData.tokenVote[_owner]; uint256 _tokenCnt = _tokenVote.length; uint256[] memory _weights = new uint256[](_tokenCnt); - uint256 _prevUsedWeight = usedWeights[_owner]; + uint256 _prevUsedWeight = _periodData.usedWeights[_owner]; uint256 _weight = DILL.balanceOf(_owner); for (uint256 i = 0; i < _tokenCnt; i++) { - uint256 _prevWeight = votes[_owner][_tokenVote[i]]; + uint256 _prevWeight = _periodData.votes[_owner][_tokenVote[i]]; _weights[i] = _prevWeight.mul(_weight).div(_prevUsedWeight); } _vote(_owner, _tokenVote, _weights); @@ -866,14 +878,17 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { // check if voting cycle is over then update ballotId if ( - (firstDistribution + (currentId * 604800)) > block.timestamp + (firstDistribution + (currentId * weekSeconds)) > block.timestamp ) { currentId += 1; } + for (uint256 i = 0; i < _tokenCnt; i++) { _totalVoteWeight = _totalVoteWeight.add(_weights[i]); } + periodData storage _periodData = periods[currentId]; + for (uint256 i = 0; i < _tokenCnt; i++) { address _token = _tokenVote[i]; address _gauge = gauges[_token]; @@ -883,20 +898,20 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { if (_gauge != address(0x0)) { _usedWeight = _usedWeight.add(_tokenWeight); - totalWeight = totalWeight.add(_tokenWeight); - weights[_token] = weights[_token].add(_tokenWeight); - tokenVote[_owner].push(_token); - votes[_owner][_token] = _tokenWeight; + _periodData.totalWeight = _periodData.totalWeight.add(_tokenWeight); + // weights[_token] = weights[_token].add(_tokenWeight); + _periodData.tokenVote[_owner].push(_token); + // votes[_owner][_token] = _tokenWeight; // store weights of current period - periods[currentId].weights_[_token] = weights[_token]; + _periodData.weights[_token] = _periodData.weights[_token].add(_tokenWeight); // store total weight of current period - periods[currentId].totalWeight_ = totalWeight; + _periodData.totalWeight = _periodData.totalWeight; // add users vote to ballot - periods[currentId].votes_[_owner][_token] = _tokenWeight; + _periodData.votes[_owner][_token] = _tokenWeight; } } - usedWeights[_owner] = _usedWeight; + _periodData.usedWeights[_owner] = _usedWeight; } // Vote with DILL on a gauge @@ -948,9 +963,6 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { require(_start < _end, "GaugeProxyV2: bad _start"); require(_end <= _tokens.length, "GaugeProxyV2: bad _end"); require(msg.sender == governance,"GaugeProxyV2: only governance can distribute"); - if (_tokens.length == _end) { - prevDistributionId += 1; - } require( prevDistributionId < currentId, "GaugeProxyV2: voting for current period in progress" @@ -958,12 +970,12 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { collect(); uint256 _balance = PICKLE.balanceOf(address(this)); periodData storage _periodData = periods[prevDistributionId + 1]; - uint256 _totalWeight = _periodData.totalWeight_; + uint256 _totalWeight = _periodData.totalWeight; if (_balance > 0 && _totalWeight > 0) { for (uint256 i = _start; i < _end; i++) { address _token = _tokens[i]; address _gauge = gauges[_token]; - uint256 _reward = _balance.mul(_periodData.weights_[_token]).div( + uint256 _reward = _balance.mul(_periodData.weights[_token]).div( _totalWeight ); if (_reward > 0) { @@ -973,5 +985,9 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { } } } + + if (_tokens.length == _end) { + prevDistributionId += 1; + } } } From 2120853b32ba591905851482b301eed8fd6f43dd Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Mon, 4 Apr 2022 10:58:49 +0530 Subject: [PATCH 07/55] chore: more refactoring of ballot id updation --- src/dill/gauge-proxy-v2.sol | 48 ++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/dill/gauge-proxy-v2.sol b/src/dill/gauge-proxy-v2.sol index 2bb812063..ced4b140f 100644 --- a/src/dill/gauge-proxy-v2.sol +++ b/src/dill/gauge-proxy-v2.sol @@ -810,16 +810,15 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { // Reset votes to 0 function reset() external { + // check if voting cycle is over then update ballotId + if ((firstDistribution + (currentId * weekSeconds)) > block.timestamp) { + currentId += 1; + } _reset(msg.sender); } // Reset votes to 0 function _reset(address _owner) internal { - if ( - (firstDistribution + (currentId * weekSeconds)) > block.timestamp - ) { - currentId += 1; - } periodData storage _periodData = periods[currentId]; // lastVote[_owner] = 0; @@ -832,7 +831,9 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { if (_votes > 0) { _periodData.totalWeight = _periodData.totalWeight.sub(_votes); - _periodData.weights[_token] = _periodData.weights[_token].sub(_votes); + _periodData.weights[_token] = _periodData.weights[_token].sub( + _votes + ); _periodData.votes[_owner][_token] = 0; } @@ -843,9 +844,8 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { // Adjusts _owner's votes according to latest _owner's DILL balance function poke(address _owner) public { - if ( - (firstDistribution + (currentId * weekSeconds)) > block.timestamp - ) { + // check if voting cycle is over then update ballotId + if ((firstDistribution + (currentId * weekSeconds)) > block.timestamp) { currentId += 1; } periodData storage _periodData = periods[currentId]; @@ -876,13 +876,6 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { uint256 _totalVoteWeight = 0; uint256 _usedWeight = 0; - // check if voting cycle is over then update ballotId - if ( - (firstDistribution + (currentId * weekSeconds)) > block.timestamp - ) { - currentId += 1; - } - for (uint256 i = 0; i < _tokenCnt; i++) { _totalVoteWeight = _totalVoteWeight.add(_weights[i]); } @@ -898,12 +891,14 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { if (_gauge != address(0x0)) { _usedWeight = _usedWeight.add(_tokenWeight); - _periodData.totalWeight = _periodData.totalWeight.add(_tokenWeight); - // weights[_token] = weights[_token].add(_tokenWeight); + _periodData.totalWeight = _periodData.totalWeight.add( + _tokenWeight + ); _periodData.tokenVote[_owner].push(_token); - // votes[_owner][_token] = _tokenWeight; // store weights of current period - _periodData.weights[_token] = _periodData.weights[_token].add(_tokenWeight); + _periodData.weights[_token] = _periodData.weights[_token].add( + _tokenWeight + ); // store total weight of current period _periodData.totalWeight = _periodData.totalWeight; // add users vote to ballot @@ -919,6 +914,12 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { external { require(_tokenVote.length == _weights.length); + + // check if voting cycle is over then update ballotId + if ((firstDistribution + (currentId * weekSeconds)) > block.timestamp) { + currentId += 1; + } + _vote(msg.sender, _tokenVote, _weights); } @@ -962,7 +963,10 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { function distribute(uint256 _start, uint256 _end) external epochDistribute { require(_start < _end, "GaugeProxyV2: bad _start"); require(_end <= _tokens.length, "GaugeProxyV2: bad _end"); - require(msg.sender == governance,"GaugeProxyV2: only governance can distribute"); + require( + msg.sender == governance, + "GaugeProxyV2: only governance can distribute" + ); require( prevDistributionId < currentId, "GaugeProxyV2: voting for current period in progress" @@ -985,7 +989,7 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { } } } - + if (_tokens.length == _end) { prevDistributionId += 1; } From 3a465a8f73c04630deda13da3cf19c829261a818 Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Mon, 4 Apr 2022 19:15:51 +0530 Subject: [PATCH 08/55] feat: censoring Gauges --- src/dill/gauge-proxy-v2.sol | 159 ++++++++++++++++++++++++++++-------- 1 file changed, 126 insertions(+), 33 deletions(-) diff --git a/src/dill/gauge-proxy-v2.sol b/src/dill/gauge-proxy-v2.sol index ced4b140f..0c0b33c59 100644 --- a/src/dill/gauge-proxy-v2.sol +++ b/src/dill/gauge-proxy-v2.sol @@ -80,6 +80,70 @@ library SafeMath { } } +/** + * @dev Wrappers over Solidity's arithmetic operations. + * + * NOTE: `SignedSafeMath` is no longer needed starting with Solidity 0.8. The compiler + * now has built in overflow checking. + */ +library SignedSafeMath { + /** + * @dev Returns the multiplication of two signed integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * + * - Multiplication cannot overflow. + */ + function mul(int256 a, int256 b) internal pure returns (int256) { + return a * b; + } + + /** + * @dev Returns the integer division of two signed integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(int256 a, int256 b) internal pure returns (int256) { + return a / b; + } + + /** + * @dev Returns the subtraction of two signed integers, reverting on + * overflow. + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(int256 a, int256 b) internal pure returns (int256) { + return a - b; + } + + /** + * @dev Returns the addition of two signed integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * + * - Addition cannot overflow. + */ + function add(int256 a, int256 b) internal pure returns (int256) { + return a + b; + } +} + library Address { function isContract(address account) internal view returns (bool) { bytes32 codehash; @@ -752,6 +816,7 @@ contract Initializable { contract GaugeProxyV2 is ProtocolGovernance, Initializable { using SafeMath for uint256; + using SignedSafeMath for int256; using SafeERC20 for IERC20; MasterChef public constant MASTER = @@ -767,6 +832,8 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { address[] internal _tokens; mapping(address => address) public gauges; // token => gauge + mapping(address => mapping(address => uint256)) + public gaugeWithNegativeWeight; uint256 public constant week = 7 days; uint256 public constant weekSeconds = 604800; @@ -775,11 +842,11 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { uint256 public currentId; struct periodData { - mapping(address => mapping(address => uint256)) votes; // msg.sender => votes - mapping(address => uint256) weights; // token => weight + mapping(address => mapping(address => int256)) votes; // msg.sender => votes + mapping(address => int256) weights; // token => weight mapping(address => address[]) tokenVote; // msg.sender => token - mapping(address => uint256) usedWeights; // msg.sender => total voting weight of user - uint256 totalWeight; + mapping(address => int256) usedWeights; // msg.sender => total voting weight of user + int256 totalWeight; } mapping(uint256 => periodData) public periods; // periodId => periodData @@ -808,12 +875,28 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { firstDistribution = _firstDistribution; } + function _updateCurrentId() internal { + uint256 lastPeriodTimestamp = (firstDistribution + + (currentId * weekSeconds)); + uint256 prevPeriodId; + if (lastPeriodTimestamp < block.timestamp) { + uint256 weeksElapsed = (block.timestamp - lastPeriodTimestamp) / + 60 / + 60 / + 24 / + 7; + prevPeriodId = currentId; + currentId = currentId + weeksElapsed + 1; + for (uint256 i = prevPeriodId + 1; i <= currentId; i++) { + periods[i] = periods[i - 1]; + } + } + } + // Reset votes to 0 function reset() external { // check if voting cycle is over then update ballotId - if ((firstDistribution + (currentId * weekSeconds)) > block.timestamp) { - currentId += 1; - } + _updateCurrentId(); _reset(msg.sender); } @@ -827,7 +910,7 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { for (uint256 i = 0; i < _tokenVoteCnt; i++) { address _token = _tokenVote[i]; - uint256 _votes = _periodData.votes[_owner][_token]; + int256 _votes = _periodData.votes[_owner][_token]; if (_votes > 0) { _periodData.totalWeight = _periodData.totalWeight.sub(_votes); @@ -844,21 +927,18 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { // Adjusts _owner's votes according to latest _owner's DILL balance function poke(address _owner) public { - // check if voting cycle is over then update ballotId - if ((firstDistribution + (currentId * weekSeconds)) > block.timestamp) { - currentId += 1; - } + _updateCurrentId(); periodData storage _periodData = periods[currentId]; address[] memory _tokenVote = _periodData.tokenVote[_owner]; uint256 _tokenCnt = _tokenVote.length; - uint256[] memory _weights = new uint256[](_tokenCnt); + int256[] memory _weights = new int256[](_tokenCnt); - uint256 _prevUsedWeight = _periodData.usedWeights[_owner]; - uint256 _weight = DILL.balanceOf(_owner); + int256 _prevUsedWeight = _periodData.usedWeights[_owner]; + int256 _weight = int256(DILL.balanceOf(_owner)); for (uint256 i = 0; i < _tokenCnt; i++) { - uint256 _prevWeight = _periodData.votes[_owner][_tokenVote[i]]; + int256 _prevWeight = _periodData.votes[_owner][_tokenVote[i]]; _weights[i] = _prevWeight.mul(_weight).div(_prevUsedWeight); } _vote(_owner, _tokenVote, _weights); @@ -867,17 +947,19 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { function _vote( address _owner, address[] memory _tokenVote, - uint256[] memory _weights + int256[] memory _weights ) internal { // _weights[i] = percentage * 100 _reset(_owner); uint256 _tokenCnt = _tokenVote.length; - uint256 _weight = DILL.balanceOf(_owner); - uint256 _totalVoteWeight = 0; - uint256 _usedWeight = 0; + int256 _weight = int256(DILL.balanceOf(_owner)); + int256 _totalVoteWeight = 0; + int256 _usedWeight = 0; for (uint256 i = 0; i < _tokenCnt; i++) { - _totalVoteWeight = _totalVoteWeight.add(_weights[i]); + _totalVoteWeight = _totalVoteWeight.add( + _weights[i] > 0 ? _weights[i] : -_weights[i] + ); } periodData storage _periodData = periods[currentId]; @@ -885,7 +967,7 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { for (uint256 i = 0; i < _tokenCnt; i++) { address _token = _tokenVote[i]; address _gauge = gauges[_token]; - uint256 _tokenWeight = _weights[i].mul(_weight).div( + int256 _tokenWeight = _weights[i].mul(_weight).div( _totalVoteWeight ); @@ -910,16 +992,11 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { } // Vote with DILL on a gauge - function vote(address[] calldata _tokenVote, uint256[] calldata _weights) + function vote(address[] calldata _tokenVote, int256[] calldata _weights) external { require(_tokenVote.length == _weights.length); - - // check if voting cycle is over then update ballotId - if ((firstDistribution + (currentId * weekSeconds)) > block.timestamp) { - currentId += 1; - } - + _updateCurrentId(); _vote(msg.sender, _tokenVote, _weights); } @@ -931,6 +1008,20 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { _tokens.push(_token); } + function removeAllCensoredGauges() external { + require( + msg.sender == governance, + "GaugeProxyV2: only governance can delist gauge" + ); + for (uint256 i = 0; i < _tokens.length; i++) { + address _token = _tokens[i]; + address _gauge = gauges[_token]; + if (gaugeWithNegativeWeight[_token][_gauge] >= 5) { + gauges[_token] = address(0x0); + } + } + } + // Sets MasterChef PID function setPID(uint256 _pid) external { require(msg.sender == governance, "!gov"); @@ -972,20 +1063,22 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { "GaugeProxyV2: voting for current period in progress" ); collect(); - uint256 _balance = PICKLE.balanceOf(address(this)); + int256 _balance = int256(PICKLE.balanceOf(address(this))); periodData storage _periodData = periods[prevDistributionId + 1]; - uint256 _totalWeight = _periodData.totalWeight; + int256 _totalWeight = _periodData.totalWeight; if (_balance > 0 && _totalWeight > 0) { for (uint256 i = _start; i < _end; i++) { address _token = _tokens[i]; address _gauge = gauges[_token]; - uint256 _reward = _balance.mul(_periodData.weights[_token]).div( - _totalWeight + uint256 _reward = uint256( + _balance.mul(_periodData.weights[_token]).div(_totalWeight) ); if (_reward > 0) { PICKLE.safeApprove(_gauge, 0); PICKLE.safeApprove(_gauge, _reward); Gauge(_gauge).notifyRewardAmount(_reward); + } else if (_reward < 0) { + gaugeWithNegativeWeight[_token][_gauge] += 1; } } } From 05fe6298f3c8dedccfb8f16f18b40f6cfa835622 Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Tue, 5 Apr 2022 14:42:04 +0530 Subject: [PATCH 09/55] feat: added more features for censorship --- src/dill/gauge-proxy-v2.sol | 117 ++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 52 deletions(-) diff --git a/src/dill/gauge-proxy-v2.sol b/src/dill/gauge-proxy-v2.sol index 0c0b33c59..dee2063fe 100644 --- a/src/dill/gauge-proxy-v2.sol +++ b/src/dill/gauge-proxy-v2.sol @@ -832,14 +832,13 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { address[] internal _tokens; mapping(address => address) public gauges; // token => gauge - mapping(address => mapping(address => uint256)) - public gaugeWithNegativeWeight; + mapping(address => uint256) public gaugeWithNegativeWeight; uint256 public constant week = 7 days; uint256 public constant weekSeconds = 604800; uint256 public firstDistribution; // epoch time stamp - uint256 public prevDistributionId; - uint256 public currentId; + uint256 public prevDistributionId = 0; + uint256 public currentId = 1; struct periodData { mapping(address => mapping(address => int256)) votes; // msg.sender => votes @@ -850,15 +849,18 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { } mapping(uint256 => periodData) public periods; // periodId => periodData - modifier epochDistribute() { - uint256 time = block.timestamp - - (prevDistributionId * weekSeconds) + - firstDistribution; - require( - time > week, - "GaugeProxyV2: distribution already done for this week" - ); - _; + function getActualCurrentPeriodId() public view returns (uint256) { + uint256 lastPeriodEndTimestamp = (firstDistribution + + (currentId * weekSeconds)); + if (lastPeriodEndTimestamp < block.timestamp) { + uint256 weeksElapsed = (block.timestamp - lastPeriodEndTimestamp) / + 60 / + 60 / + 24 / + 7; + + return currentId + weeksElapsed + 1; + } } function tokens() external view returns (address[] memory) { @@ -876,20 +878,10 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { } function _updateCurrentId() internal { - uint256 lastPeriodTimestamp = (firstDistribution + - (currentId * weekSeconds)); - uint256 prevPeriodId; - if (lastPeriodTimestamp < block.timestamp) { - uint256 weeksElapsed = (block.timestamp - lastPeriodTimestamp) / - 60 / - 60 / - 24 / - 7; - prevPeriodId = currentId; - currentId = currentId + weeksElapsed + 1; - for (uint256 i = prevPeriodId + 1; i <= currentId; i++) { - periods[i] = periods[i - 1]; - } + uint256 prevPeriodId = currentId; + currentId = getActualCurrentPeriodId(); + for (uint256 i = prevPeriodId + 1; i <= currentId; i++) { + periods[i] = periods[i - 1]; } } @@ -912,8 +904,10 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { address _token = _tokenVote[i]; int256 _votes = _periodData.votes[_owner][_token]; - if (_votes > 0) { - _periodData.totalWeight = _periodData.totalWeight.sub(_votes); + if (_votes != 0) { + _periodData.totalWeight = _periodData.totalWeight.sub( + _votes > 0 ? _votes : -_votes + ); _periodData.weights[_token] = _periodData.weights[_token].sub( _votes ); @@ -949,7 +943,6 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { address[] memory _tokenVote, int256[] memory _weights ) internal { - // _weights[i] = percentage * 100 _reset(_owner); uint256 _tokenCnt = _tokenVote.length; int256 _weight = int256(DILL.balanceOf(_owner)); @@ -972,19 +965,20 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { ); if (_gauge != address(0x0)) { - _usedWeight = _usedWeight.add(_tokenWeight); - _periodData.totalWeight = _periodData.totalWeight.add( + _periodData.weights[_token] = _periodData.weights[_token].add( _tokenWeight ); + _periodData.votes[_owner][_token] = _tokenWeight; _periodData.tokenVote[_owner].push(_token); - // store weights of current period - _periodData.weights[_token] = _periodData.weights[_token].add( + + if (_tokenWeight < 0) { + _tokenWeight = -_tokenWeight; + } + + _usedWeight = _usedWeight.add(_tokenWeight); + _periodData.totalWeight = _periodData.totalWeight.add( _tokenWeight ); - // store total weight of current period - _periodData.totalWeight = _periodData.totalWeight; - // add users vote to ballot - _periodData.votes[_owner][_token] = _tokenWeight; } } @@ -1008,18 +1002,31 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { _tokens.push(_token); } - function removeAllCensoredGauges() external { + function delistGauge(address _token) external { + require(msg.sender == governance, "!gov"); + require(gauges[_token] != address(0x0), "!exists"); + + uint256 _actualCurrentId = getActualCurrentPeriodId(); require( - msg.sender == governance, - "GaugeProxyV2: only governance can delist gauge" + prevDistributionId == _actualCurrentId - 1, + "! all distributions completed" ); - for (uint256 i = 0; i < _tokens.length; i++) { - address _token = _tokens[i]; - address _gauge = gauges[_token]; - if (gaugeWithNegativeWeight[_token][_gauge] >= 5) { - gauges[_token] = address(0x0); - } + + address _gauge = gauges[_token]; + + require(gaugeWithNegativeWeight[_gauge] >= 5, "censors < 5"); + + delete gauges[_token]; + + address[] storage newTokenArray; + uint256 tokensLength = _tokens.length; + + for (uint i = 0; i < tokensLength; i++){ + if(_tokens[i] != _token) + newTokenArray.push(_tokens[i]); } + + _tokens = newTokenArray; } // Sets MasterChef PID @@ -1051,17 +1058,21 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { return _tokens.length; } - function distribute(uint256 _start, uint256 _end) external epochDistribute { + function distribute(uint256 _start, uint256 _end) external { require(_start < _end, "GaugeProxyV2: bad _start"); require(_end <= _tokens.length, "GaugeProxyV2: bad _end"); require( msg.sender == governance, "GaugeProxyV2: only governance can distribute" ); + + _updateCurrentId(); + require( - prevDistributionId < currentId, - "GaugeProxyV2: voting for current period in progress" + prevDistributionId < currentId - 1, + "GaugeProxyV2: all period distributions complete" ); + collect(); int256 _balance = int256(PICKLE.balanceOf(address(this))); periodData storage _periodData = periods[prevDistributionId + 1]; @@ -1077,8 +1088,10 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { PICKLE.safeApprove(_gauge, 0); PICKLE.safeApprove(_gauge, _reward); Gauge(_gauge).notifyRewardAmount(_reward); - } else if (_reward < 0) { - gaugeWithNegativeWeight[_token][_gauge] += 1; + } + + if (_reward < 0) { + gaugeWithNegativeWeight[_gauge] += 1; } } } From f9a7be35562b727b8841b2ded47cd34b15449c65 Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Wed, 6 Apr 2022 17:04:06 +0530 Subject: [PATCH 10/55] feat: deligation of votes --- src/dill/gauge-proxy-v2.sol | 41 +++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/dill/gauge-proxy-v2.sol b/src/dill/gauge-proxy-v2.sol index dee2063fe..8bf207772 100644 --- a/src/dill/gauge-proxy-v2.sol +++ b/src/dill/gauge-proxy-v2.sol @@ -837,8 +837,8 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { uint256 public constant week = 7 days; uint256 public constant weekSeconds = 604800; uint256 public firstDistribution; // epoch time stamp - uint256 public prevDistributionId = 0; - uint256 public currentId = 1; + uint256 public prevDistributionId; + uint256 public currentId; struct periodData { mapping(address => mapping(address => int256)) votes; // msg.sender => votes @@ -849,6 +849,24 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { } mapping(uint256 => periodData) public periods; // periodId => periodData + struct deligateData { + address deligate; + uint256 periods; + bool canVoteForCurrentPeriod; + } + mapping (address => deligateData) public deligations; + // no. of periods 0 = none, 1 = indefinate , n = n-1 periods + + function setVotingDelicate(address _delicate, uint256 _periodsCount) public { + require(_delicate != address(0), "GaugeProxyV2: cannot delicate zero address"); + require(_delicate != msg.sender, "GaugeProxyV2: delicating address cannot be delicated"); + deligations[msg.sender].deligate = _delicate; + deligations[msg.sender].canVoteForCurrentPeriod = true; + if( _periodCount > 1) { + deligations[msg.sender].periods = _periodsCount + 1; + } + } + function getActualCurrentPeriodId() public view returns (uint256) { uint256 lastPeriodEndTimestamp = (firstDistribution + (currentId * weekSeconds)); @@ -875,6 +893,8 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { TOKEN = IERC20(address(new MasterDill())); governance = msg.sender; firstDistribution = _firstDistribution; + currentId = 1; + prevDistributionId = 0; } function _updateCurrentId() internal { @@ -992,6 +1012,23 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { require(_tokenVote.length == _weights.length); _updateCurrentId(); _vote(msg.sender, _tokenVote, _weights); + deligations[msg.sender].canVoteForCurrentPeriod = false; + } + + function voteFor(address _owner, address[] calldata _tokenVote, int256[] calldata _weights) + external{ + require(_tokenVote.length == _weights.length); + _updateCurrentId(); + deligateData memory deligate = deligations[_owner]; + if(deligate.deligate == msg.sender && deligate.periods > 0 && deligate.canVoteForCurrentPeriod == true) { + _vote(_owner, _tokenVote, _weights); + if(deligate.periods > 2) { + deligate.periods -= 1; + } + if(deligate.periods == 2) { + deligate.periods = 0; + } + } } // Add new token gauge From 9f99894c760fee0b5e49e45a4629c5d64ba94cfe Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Wed, 6 Apr 2022 17:29:48 +0530 Subject: [PATCH 11/55] fix: fixed deligation --- src/dill/gauge-proxy-v2.sol | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/dill/gauge-proxy-v2.sol b/src/dill/gauge-proxy-v2.sol index 8bf207772..fdd7038fe 100644 --- a/src/dill/gauge-proxy-v2.sol +++ b/src/dill/gauge-proxy-v2.sol @@ -851,20 +851,18 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { struct deligateData { address deligate; - uint256 periods; + int256 periods; bool canVoteForCurrentPeriod; } mapping (address => deligateData) public deligations; - // no. of periods 0 = none, 1 = indefinate , n = n-1 periods + // no. of periods 0 = none, <0 indefinate , >0 periods - function setVotingDelicate(address _delicate, uint256 _periodsCount) public { + function setVotingDelicate(address _delicate, int256 _periodsCount) public { require(_delicate != address(0), "GaugeProxyV2: cannot delicate zero address"); require(_delicate != msg.sender, "GaugeProxyV2: delicating address cannot be delicated"); deligations[msg.sender].deligate = _delicate; deligations[msg.sender].canVoteForCurrentPeriod = true; - if( _periodCount > 1) { - deligations[msg.sender].periods = _periodsCount + 1; - } + deligations[msg.sender].periods = _periodsCount; } function getActualCurrentPeriodId() public view returns (uint256) { @@ -903,6 +901,7 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { for (uint256 i = prevPeriodId + 1; i <= currentId; i++) { periods[i] = periods[i - 1]; } + deligations[msg.sender].canVoteForCurrentPeriod = true; } // Reset votes to 0 @@ -1020,14 +1019,11 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { require(_tokenVote.length == _weights.length); _updateCurrentId(); deligateData memory deligate = deligations[_owner]; - if(deligate.deligate == msg.sender && deligate.periods > 0 && deligate.canVoteForCurrentPeriod == true) { + if(deligate.deligate == msg.sender && deligate.periods != 0 && deligate.canVoteForCurrentPeriod == true) { _vote(_owner, _tokenVote, _weights); - if(deligate.periods > 2) { + if(deligate.periods > 0) { deligate.periods -= 1; } - if(deligate.periods == 2) { - deligate.periods = 0; - } } } From 7f87274de2072b9b90ba19c4df2c4adf09dea8bf Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Thu, 7 Apr 2022 14:41:44 +0530 Subject: [PATCH 12/55] feat: updates to delegation --- src/dill/gauge-proxy-v2.sol | 60 +++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/src/dill/gauge-proxy-v2.sol b/src/dill/gauge-proxy-v2.sol index fdd7038fe..ee8f9370f 100644 --- a/src/dill/gauge-proxy-v2.sol +++ b/src/dill/gauge-proxy-v2.sol @@ -849,20 +849,28 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { } mapping(uint256 => periodData) public periods; // periodId => periodData - struct deligateData { - address deligate; - int256 periods; - bool canVoteForCurrentPeriod; + struct delegateData { + address delegate; + uint256 endPeriod; + mapping(uint256 => bool) blockDelegate; // Period => Boolean (if delegate address can vote in that period) } - mapping (address => deligateData) public deligations; - // no. of periods 0 = none, <0 indefinate , >0 periods - function setVotingDelicate(address _delicate, int256 _periodsCount) public { - require(_delicate != address(0), "GaugeProxyV2: cannot delicate zero address"); - require(_delicate != msg.sender, "GaugeProxyV2: delicating address cannot be delicated"); - deligations[msg.sender].deligate = _delicate; - deligations[msg.sender].canVoteForCurrentPeriod = true; - deligations[msg.sender].periods = _periodsCount; + mapping(address => delegateData) public delegations; + + // no. of periods 0 = none, <0 indefinate , >0 periods + function setVotingDelegate(address _delegate, uint256 _periodsCount) + external + { + require( + _delegate != address(0), + "GaugeProxyV2: cannot delegate zero address" + ); + require( + _delegate != msg.sender, + "GaugeProxyV2: delegate address cannot be delegating" + ); + delegations[msg.sender].delegate = _delegate; + delegations[msg.sender].endPeriod = getActualCurrentPeriodId() + _periodsCount; } function getActualCurrentPeriodId() public view returns (uint256) { @@ -901,7 +909,6 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { for (uint256 i = prevPeriodId + 1; i <= currentId; i++) { periods[i] = periods[i - 1]; } - deligations[msg.sender].canVoteForCurrentPeriod = true; } // Reset votes to 0 @@ -1011,20 +1018,22 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { require(_tokenVote.length == _weights.length); _updateCurrentId(); _vote(msg.sender, _tokenVote, _weights); - deligations[msg.sender].canVoteForCurrentPeriod = false; + delegations[msg.sender].blockDelegate[currentId] = true; } - function voteFor(address _owner, address[] calldata _tokenVote, int256[] calldata _weights) - external{ + function voteFor( + address _owner, + address[] calldata _tokenVote, + int256[] calldata _weights + ) external { require(_tokenVote.length == _weights.length); _updateCurrentId(); - deligateData memory deligate = deligations[_owner]; - if(deligate.deligate == msg.sender && deligate.periods != 0 && deligate.canVoteForCurrentPeriod == true) { - _vote(_owner, _tokenVote, _weights); - if(deligate.periods > 0) { - deligate.periods -= 1; - } - } + delegateData storage _delegate = delegations[_owner]; + require(_delegate.delegate == msg.sender, "Sender not authorized"); + require(_delegate.blockDelegate[currentId] == false, "Delegating address has already voted"); + require(_delegate.endPeriod <= currentId, "Delegating period expired"); + + _vote(_owner, _tokenVote, _weights); } // Add new token gauge @@ -1054,9 +1063,8 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { address[] storage newTokenArray; uint256 tokensLength = _tokens.length; - for (uint i = 0; i < tokensLength; i++){ - if(_tokens[i] != _token) - newTokenArray.push(_tokens[i]); + for (uint256 i = 0; i < tokensLength; i++) { + if (_tokens[i] != _token) newTokenArray.push(_tokens[i]); } _tokens = newTokenArray; From 5bfb06eb989c186a544bb63d83f548614adebde9 Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Thu, 7 Apr 2022 15:57:27 +0530 Subject: [PATCH 13/55] feat: add indefinite delegation --- src/dill/gauge-proxy-v2.sol | 63 ++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/src/dill/gauge-proxy-v2.sol b/src/dill/gauge-proxy-v2.sol index ee8f9370f..fceb1f27c 100644 --- a/src/dill/gauge-proxy-v2.sol +++ b/src/dill/gauge-proxy-v2.sol @@ -831,36 +831,52 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { uint256 public pid; address[] internal _tokens; - mapping(address => address) public gauges; // token => gauge + + // token => gauge + mapping(address => address) public gauges; + mapping(address => uint256) public gaugeWithNegativeWeight; uint256 public constant week = 7 days; uint256 public constant weekSeconds = 604800; - uint256 public firstDistribution; // epoch time stamp + // epoch time stamp + uint256 public firstDistribution; uint256 public prevDistributionId; uint256 public currentId; struct periodData { - mapping(address => mapping(address => int256)) votes; // msg.sender => votes - mapping(address => int256) weights; // token => weight - mapping(address => address[]) tokenVote; // msg.sender => token - mapping(address => int256) usedWeights; // msg.sender => total voting weight of user + // msg.sender => votes + mapping(address => mapping(address => int256)) votes; + // token => weight + mapping(address => int256) weights; + // msg.sender => token + mapping(address => address[]) tokenVote; + // msg.sender => total voting weight of user + mapping(address => int256) usedWeights; int256 totalWeight; } - mapping(uint256 => periodData) public periods; // periodId => periodData + + // periodId => periodData + mapping(uint256 => periodData) public periods; struct delegateData { + // delegated address address delegate; + // endPeriod if defined. Else 0. uint256 endPeriod; - mapping(uint256 => bool) blockDelegate; // Period => Boolean (if delegate address can vote in that period) + // If no endPeriod + bool indefinite; + // Period => Boolean (if delegate address can vote in that period) + mapping(uint256 => bool) blockDelegate; } mapping(address => delegateData) public delegations; - // no. of periods 0 = none, <0 indefinate , >0 periods - function setVotingDelegate(address _delegate, uint256 _periodsCount) - external - { + function setVotingDelegate( + address _delegate, + uint256 _periodsCount, + bool _indefinite + ) external { require( _delegate != address(0), "GaugeProxyV2: cannot delegate zero address" @@ -870,7 +886,14 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { "GaugeProxyV2: delegate address cannot be delegating" ); delegations[msg.sender].delegate = _delegate; - delegations[msg.sender].endPeriod = getActualCurrentPeriodId() + _periodsCount; + + if (_indefinite == true) { + delegations[msg.sender].indefinite = true; + } else { + delegations[msg.sender].endPeriod = + getActualCurrentPeriodId() + + _periodsCount; + } } function getActualCurrentPeriodId() public view returns (uint256) { @@ -921,8 +944,6 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { // Reset votes to 0 function _reset(address _owner) internal { periodData storage _periodData = periods[currentId]; - - // lastVote[_owner] = 0; address[] storage _tokenVote = _periodData.tokenVote[_owner]; uint256 _tokenVoteCnt = _tokenVote.length; @@ -1030,9 +1051,15 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { _updateCurrentId(); delegateData storage _delegate = delegations[_owner]; require(_delegate.delegate == msg.sender, "Sender not authorized"); - require(_delegate.blockDelegate[currentId] == false, "Delegating address has already voted"); - require(_delegate.endPeriod <= currentId, "Delegating period expired"); - + require( + _delegate.blockDelegate[currentId] == false, + "Delegating address has already voted" + ); + require( + _delegate.indefinite == true || _delegate.endPeriod <= currentId, + "Delegating period expired" + ); + _vote(_owner, _tokenVote, _weights); } From d8eab0605685d392ba89a68e2af937f788bc1a72 Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Thu, 7 Apr 2022 16:40:07 +0530 Subject: [PATCH 14/55] feat: new delegate address can only vote after currentId --- src/dill/gauge-proxy-v2.sol | 68 +++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/src/dill/gauge-proxy-v2.sol b/src/dill/gauge-proxy-v2.sol index fceb1f27c..3c095c02c 100644 --- a/src/dill/gauge-proxy-v2.sol +++ b/src/dill/gauge-proxy-v2.sol @@ -831,7 +831,7 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { uint256 public pid; address[] internal _tokens; - + // token => gauge mapping(address => address) public gauges; @@ -862,6 +862,10 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { struct delegateData { // delegated address address delegate; + // previous delegated address if updated, else zero address + address prevDelegate; + // period id when delegate address was updated + uint256 updatePeriodId; // endPeriod if defined. Else 0. uint256 endPeriod; // If no endPeriod @@ -872,30 +876,6 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { mapping(address => delegateData) public delegations; - function setVotingDelegate( - address _delegate, - uint256 _periodsCount, - bool _indefinite - ) external { - require( - _delegate != address(0), - "GaugeProxyV2: cannot delegate zero address" - ); - require( - _delegate != msg.sender, - "GaugeProxyV2: delegate address cannot be delegating" - ); - delegations[msg.sender].delegate = _delegate; - - if (_indefinite == true) { - delegations[msg.sender].indefinite = true; - } else { - delegations[msg.sender].endPeriod = - getActualCurrentPeriodId() + - _periodsCount; - } - } - function getActualCurrentPeriodId() public view returns (uint256) { uint256 lastPeriodEndTimestamp = (firstDistribution + (currentId * weekSeconds)); @@ -1042,6 +1022,36 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { delegations[msg.sender].blockDelegate[currentId] = true; } + function setVotingDelegate( + address _delegateAddress, + uint256 _periodsCount, + bool _indefinite + ) external { + require( + _delegateAddress != address(0), + "GaugeProxyV2: cannot delegate zero address" + ); + require( + _delegateAddress != msg.sender, + "GaugeProxyV2: delegate address cannot be delegating" + ); + + delegateData storage _delegate = delegations[msg.sender]; + + uint256 actualCurrentPeriodId = getActualCurrentPeriodId(); + + address currentDelegate = _delegate.delegate; + _delegate.delegate = _delegateAddress; + _delegate.prevDelegate = currentDelegate; + _delegate.updatePeriodId = actualCurrentPeriodId; + + if (_indefinite == true) { + _delegate.indefinite = true; + } else { + _delegate.endPeriod = actualCurrentPeriodId + _periodsCount; + } + } + function voteFor( address _owner, address[] calldata _tokenVote, @@ -1050,7 +1060,13 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { require(_tokenVote.length == _weights.length); _updateCurrentId(); delegateData storage _delegate = delegations[_owner]; - require(_delegate.delegate == msg.sender, "Sender not authorized"); + require( + (_delegate.delegate == msg.sender && + currentId > _delegate.updatePeriodId) || + (_delegate.prevDelegate == msg.sender && + currentId == _delegate.updatePeriodId), + "Sender not authorized" + ); require( _delegate.blockDelegate[currentId] == false, "Delegating address has already voted" From 0bad1a2c543bb24854b065817c61f794b79e3092 Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Wed, 13 Apr 2022 11:55:01 +0530 Subject: [PATCH 15/55] feat: upgraded to v 0.8.1 --- src/dill/gauge-proxy-v2.sol | 326 ++++++++++-------------------------- 1 file changed, 88 insertions(+), 238 deletions(-) diff --git a/src/dill/gauge-proxy-v2.sol b/src/dill/gauge-proxy-v2.sol index 3c095c02c..842618a1e 100644 --- a/src/dill/gauge-proxy-v2.sol +++ b/src/dill/gauge-proxy-v2.sol @@ -1,148 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.6.7; //^0.7.5; - -library SafeMath { - function add(uint256 a, uint256 b) internal pure returns (uint256) { - uint256 c = a + b; - require(c >= a, "add: +"); - - return c; - } - - function add( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - uint256 c = a + b; - require(c >= a, errorMessage); - - return c; - } - - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - return sub(a, b, "sub: -"); - } - - function sub( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - require(b <= a, errorMessage); - uint256 c = a - b; - - return c; - } - - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - // Gas optimization: this is cheaper than requiring 'a' not being zero, but the - // benefit is lost if 'b' is also tested. - // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 - if (a == 0) { - return 0; - } - - uint256 c = a * b; - require(c / a == b, "mul: *"); - - return c; - } - - function mul( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - if (a == 0) { - return 0; - } - - uint256 c = a * b; - require(c / a == b, errorMessage); - - return c; - } - - function div(uint256 a, uint256 b) internal pure returns (uint256) { - return div(a, b, "div: /"); - } - - function div( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - require(b > 0, errorMessage); - uint256 c = a / b; - - return c; - } -} - -/** - * @dev Wrappers over Solidity's arithmetic operations. - * - * NOTE: `SignedSafeMath` is no longer needed starting with Solidity 0.8. The compiler - * now has built in overflow checking. - */ -library SignedSafeMath { - /** - * @dev Returns the multiplication of two signed integers, reverting on - * overflow. - * - * Counterpart to Solidity's `*` operator. - * - * Requirements: - * - * - Multiplication cannot overflow. - */ - function mul(int256 a, int256 b) internal pure returns (int256) { - return a * b; - } - - /** - * @dev Returns the integer division of two signed integers. Reverts on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div(int256 a, int256 b) internal pure returns (int256) { - return a / b; - } - - /** - * @dev Returns the subtraction of two signed integers, reverting on - * overflow. - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub(int256 a, int256 b) internal pure returns (int256) { - return a - b; - } - - /** - * @dev Returns the addition of two signed integers, reverting on - * overflow. - * - * Counterpart to Solidity's `+` operator. - * - * Requirements: - * - * - Addition cannot overflow. - */ - function add(int256 a, int256 b) internal pure returns (int256) { - return a + b; - } -} +pragma solidity ^0.8.1; // ^0.6.7; //^0.7.5; library Address { function isContract(address account) internal view returns (bool) { @@ -160,7 +17,9 @@ library Address { pure returns (address payable) { - return address(uint160(account)); + // return address(uint160(account)); + return payable(address(uint160(account))); + } function sendValue(address payable recipient, uint256 amount) internal { @@ -202,7 +61,7 @@ interface IERC20 { } library SafeERC20 { - using SafeMath for uint256; + using Address for address; function safeTransfer( @@ -248,7 +107,7 @@ library SafeERC20 { address spender, uint256 value ) internal { - uint256 newAllowance = token.allowance(address(this), spender).add( + uint256 newAllowance = token.allowance(address(this), spender) + ( value ); callOptionalReturn( @@ -266,9 +125,10 @@ library SafeERC20 { address spender, uint256 value ) internal { - uint256 newAllowance = token.allowance(address(this), spender).sub( - value, - "SafeERC20: decreased allowance below zero" + uint256 newAllowance = token.allowance(address(this), spender) - ( + value + // , + // "SafeERC20: decreased allowance below zero" ); callOptionalReturn( token, @@ -356,7 +216,8 @@ abstract contract ReentrancyGuard { uint256 private _status; - constructor() public { + // constructor() public { + constructor() { _status = _NOT_ENTERED; } @@ -383,7 +244,7 @@ abstract contract ReentrancyGuard { } contract Gauge is ReentrancyGuard { - using SafeMath for uint256; + using SafeERC20 for IERC20; IERC20 public constant PICKLE = @@ -441,42 +302,42 @@ contract Gauge is ReentrancyGuard { return rewardPerTokenStored; } return - rewardPerTokenStored.add( - lastTimeRewardApplicable() - .sub(lastUpdateTime) - .mul(rewardRate) - .mul(1e18) - .div(derivedSupply) + rewardPerTokenStored + ( + ((lastTimeRewardApplicable() + - (lastUpdateTime)) + * (rewardRate) + * (1e18)) + / (derivedSupply) ); } function derivedBalance(address account) public view returns (uint256) { uint256 _balance = _balances[account]; - uint256 _derived = _balance.mul(40).div(100); + uint256 _derived = _balance * (40) / (100); uint256 _adjusted = ( - _totalSupply.mul(DILL.balanceOf(account)).div(DILL.totalSupply()) - ).mul(60).div(100); - return Math.min(_derived.add(_adjusted), _balance); + _totalSupply * (DILL.balanceOf(account)) / (DILL.totalSupply()) + ) * (60) / (100); + return Math.min(_derived + (_adjusted), _balance); } function kick(address account) public { uint256 _derivedBalance = derivedBalances[account]; - derivedSupply = derivedSupply.sub(_derivedBalance); + derivedSupply -= (_derivedBalance); _derivedBalance = derivedBalance(account); derivedBalances[account] = _derivedBalance; - derivedSupply = derivedSupply.add(_derivedBalance); + derivedSupply += (_derivedBalance); } function earned(address account) public view returns (uint256) { return - derivedBalances[account] - .mul(rewardPerToken().sub(userRewardPerTokenPaid[account])) - .div(1e18) - .add(rewards[account]); + (derivedBalances[account] + * (rewardPerToken() - (userRewardPerTokenPaid[account])) + / (1e18)) + + (rewards[account]); } function getRewardForDuration() external view returns (uint256) { - return rewardRate.mul(DURATION); + return rewardRate * (DURATION); } function depositAll() external { @@ -497,8 +358,8 @@ contract Gauge is ReentrancyGuard { updateReward(account) { require(amount > 0, "Cannot stake 0"); - _totalSupply = _totalSupply.add(amount); - _balances[account] = _balances[account].add(amount); + _totalSupply += (amount); + _balances[account] += (amount); emit Staked(account, amount); TOKEN.safeTransferFrom(account, address(this), amount); } @@ -517,8 +378,8 @@ contract Gauge is ReentrancyGuard { updateReward(msg.sender) { require(amount > 0, "Cannot withdraw 0"); - _totalSupply = _totalSupply.sub(amount); - _balances[msg.sender] = _balances[msg.sender].sub(amount); + _totalSupply -= (amount); + _balances[msg.sender] -= (amount); TOKEN.safeTransfer(msg.sender, amount); emit Withdrawn(msg.sender, amount); } @@ -544,11 +405,11 @@ contract Gauge is ReentrancyGuard { { PICKLE.safeTransferFrom(DISTRIBUTION, address(this), reward); if (block.timestamp >= periodFinish) { - rewardRate = reward.div(DURATION); + rewardRate = reward / (DURATION); } else { - uint256 remaining = periodFinish.sub(block.timestamp); - uint256 leftover = remaining.mul(rewardRate); - rewardRate = reward.add(leftover).div(DURATION); + uint256 remaining = periodFinish - (block.timestamp); + uint256 leftover = remaining * (rewardRate); + rewardRate = (reward + leftover) / DURATION; } // Ensure the provided reward amount is not more than the balance in the contract. @@ -557,12 +418,12 @@ contract Gauge is ReentrancyGuard { // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow. uint256 balance = PICKLE.balanceOf(address(this)); require( - rewardRate <= balance.div(DURATION), + rewardRate <= balance / (DURATION), "Provided reward too high" ); lastUpdateTime = block.timestamp; - periodFinish = block.timestamp.add(DURATION); + periodFinish = block.timestamp + DURATION; emit RewardAdded(reward); } @@ -623,7 +484,7 @@ contract ProtocolGovernance { } contract MasterDill { - using SafeMath for uint256; + /// @notice EIP-20 token name for this token string public constant name = "Master DILL"; @@ -719,10 +580,12 @@ contract MasterDill { address spender = msg.sender; uint256 spenderAllowance = allowances[src][spender]; - if (spender != src && spenderAllowance != uint256(-1)) { - uint256 newAllowance = spenderAllowance.sub( - amount, - "transferFrom: exceeds spender allowance" + // if (spender != src && spenderAllowance != uint256(-1)) { // type(uint256).max + if (spender != src && spenderAllowance != type(uint256).max) { + uint256 newAllowance = spenderAllowance - ( + amount + // , + // "transferFrom: exceeds spender allowance" ); allowances[src][spender] = newAllowance; @@ -741,11 +604,15 @@ contract MasterDill { require(src != address(0), "_transferTokens: zero address"); require(dst != address(0), "_transferTokens: zero address"); - balances[src] = balances[src].sub( - amount, - "_transferTokens: exceeds balance" + balances[src] = balances[src] - ( + amount + // , + // "_transferTokens: exceeds balance" ); - balances[dst] = balances[dst].add(amount, "_transferTokens: overflows"); + balances[dst] += (amount + // , + // "_transferTokens: overflows" + ); emit Transfer(src, dst, amount); } } @@ -815,8 +682,7 @@ contract Initializable { } contract GaugeProxyV2 is ProtocolGovernance, Initializable { - using SafeMath for uint256; - using SignedSafeMath for int256; + using SafeERC20 for IERC20; MasterChef public constant MASTER = @@ -843,21 +709,13 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { uint256 public firstDistribution; uint256 public prevDistributionId; uint256 public currentId; + address[] internal _tokensTemp; - struct periodData { - // msg.sender => votes - mapping(address => mapping(address => int256)) votes; - // token => weight - mapping(address => int256) weights; - // msg.sender => token - mapping(address => address[]) tokenVote; - // msg.sender => total voting weight of user - mapping(address => int256) usedWeights; - int256 totalWeight; - } - - // periodId => periodData - mapping(uint256 => periodData) public periods; + mapping(address => int256) public usedWeights; // msg.sender => total voting weight of user + mapping(address => address[]) public tokenVote; // msg.sender => token + mapping(address => mapping(address => int256)) public votes; // msg.sender => votes + mapping(address => int256[]) public weights; // token => weight [period id] + int256[] public totalWeight; // [period id] struct delegateData { // delegated address @@ -902,7 +760,7 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { TOKEN = IERC20(address(new MasterDill())); governance = msg.sender; firstDistribution = _firstDistribution; - currentId = 1; + currentId = 0; //1; prevDistributionId = 0; } @@ -910,7 +768,7 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { uint256 prevPeriodId = currentId; currentId = getActualCurrentPeriodId(); for (uint256 i = prevPeriodId + 1; i <= currentId; i++) { - periods[i] = periods[i - 1]; + totalWeight[i] = totalWeight[i-1]; } } @@ -923,44 +781,40 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { // Reset votes to 0 function _reset(address _owner) internal { - periodData storage _periodData = periods[currentId]; - address[] storage _tokenVote = _periodData.tokenVote[_owner]; + address[] storage _tokenVote = tokenVote[_owner]; uint256 _tokenVoteCnt = _tokenVote.length; - for (uint256 i = 0; i < _tokenVoteCnt; i++) { address _token = _tokenVote[i]; - int256 _votes = _periodData.votes[_owner][_token]; + int256 _votes = votes[_owner][_token]; if (_votes != 0) { - _periodData.totalWeight = _periodData.totalWeight.sub( + totalWeight[currentId] -= ( _votes > 0 ? _votes : -_votes ); - _periodData.weights[_token] = _periodData.weights[_token].sub( + weights[_token][currentId] -= ( _votes ); - _periodData.votes[_owner][_token] = 0; + votes[_owner][_token] = 0; } } - delete _periodData.tokenVote[_owner]; + delete tokenVote[_owner]; } // Adjusts _owner's votes according to latest _owner's DILL balance function poke(address _owner) public { _updateCurrentId(); - periodData storage _periodData = periods[currentId]; - - address[] memory _tokenVote = _periodData.tokenVote[_owner]; + address[] memory _tokenVote = tokenVote[_owner]; uint256 _tokenCnt = _tokenVote.length; int256[] memory _weights = new int256[](_tokenCnt); - int256 _prevUsedWeight = _periodData.usedWeights[_owner]; + int256 _prevUsedWeight = usedWeights[_owner]; int256 _weight = int256(DILL.balanceOf(_owner)); for (uint256 i = 0; i < _tokenCnt; i++) { - int256 _prevWeight = _periodData.votes[_owner][_tokenVote[i]]; - _weights[i] = _prevWeight.mul(_weight).div(_prevUsedWeight); + int256 _prevWeight = votes[_owner][_tokenVote[i]]; + _weights[i] = _prevWeight * (_weight) / (_prevUsedWeight); } _vote(_owner, _tokenVote, _weights); } @@ -977,39 +831,37 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { int256 _usedWeight = 0; for (uint256 i = 0; i < _tokenCnt; i++) { - _totalVoteWeight = _totalVoteWeight.add( + _totalVoteWeight += ( _weights[i] > 0 ? _weights[i] : -_weights[i] ); } - periodData storage _periodData = periods[currentId]; - for (uint256 i = 0; i < _tokenCnt; i++) { address _token = _tokenVote[i]; address _gauge = gauges[_token]; - int256 _tokenWeight = _weights[i].mul(_weight).div( + int256 _tokenWeight = _weights[i] * (_weight) / ( _totalVoteWeight ); - + weights[_token][currentId] = weights[_token][currentId - 1]; if (_gauge != address(0x0)) { - _periodData.weights[_token] = _periodData.weights[_token].add( + weights[_token][currentId] += ( _tokenWeight ); - _periodData.votes[_owner][_token] = _tokenWeight; - _periodData.tokenVote[_owner].push(_token); + votes[_owner][_token] = _tokenWeight; + tokenVote[_owner].push(_token); if (_tokenWeight < 0) { _tokenWeight = -_tokenWeight; } - _usedWeight = _usedWeight.add(_tokenWeight); - _periodData.totalWeight = _periodData.totalWeight.add( + _usedWeight += (_tokenWeight); + totalWeight[currentId] += ( _tokenWeight ); } } - _periodData.usedWeights[_owner] = _usedWeight; + usedWeights[_owner] = _usedWeight; } // Vote with DILL on a gauge @@ -1103,14 +955,13 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { delete gauges[_token]; - address[] storage newTokenArray; + // address[] storage newTokenArray = new address[](); uint256 tokensLength = _tokens.length; for (uint256 i = 0; i < tokensLength; i++) { - if (_tokens[i] != _token) newTokenArray.push(_tokens[i]); + if (_tokens[i] != _token) _tokensTemp.push(_tokens[i]); } - - _tokens = newTokenArray; + _tokens = _tokensTemp; } // Sets MasterChef PID @@ -1159,14 +1010,13 @@ contract GaugeProxyV2 is ProtocolGovernance, Initializable { collect(); int256 _balance = int256(PICKLE.balanceOf(address(this))); - periodData storage _periodData = periods[prevDistributionId + 1]; - int256 _totalWeight = _periodData.totalWeight; + int256 _totalWeight = totalWeight[prevDistributionId + 1]; if (_balance > 0 && _totalWeight > 0) { for (uint256 i = _start; i < _end; i++) { address _token = _tokens[i]; address _gauge = gauges[_token]; uint256 _reward = uint256( - _balance.mul(_periodData.weights[_token]).div(_totalWeight) + _balance * (weights[_token][prevDistributionId + 1]) / (_totalWeight) ); if (_reward > 0) { PICKLE.safeApprove(_gauge, 0); From 717caa227e871b7f781165f55b36968c617733cc Mon Sep 17 00:00:00 2001 From: Aditya Rout Date: Thu, 21 Apr 2022 20:16:42 +0530 Subject: [PATCH 16/55] WIP: gaugeProxyV2 tests --- .../dill-test/5.vote-and-distribute-v2.js | 117 ++++++++++++++++++ src/dill/gauge-proxy-v2.sol | 6 +- 2 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 hardhat-scripts/dill-test/5.vote-and-distribute-v2.js diff --git a/hardhat-scripts/dill-test/5.vote-and-distribute-v2.js b/hardhat-scripts/dill-test/5.vote-and-distribute-v2.js new file mode 100644 index 000000000..a397385ba --- /dev/null +++ b/hardhat-scripts/dill-test/5.vote-and-distribute-v2.js @@ -0,0 +1,117 @@ +// We require the Hardhat Runtime Environment explicitly here. This is optional +// but useful for running the script in a standalone fashion through `node