-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #263 from pooltogether/pool-2263-migrate-drawcalcu…
…lator Add DrawCalculatorV3 and PrizeConfigHistory
- Loading branch information
Showing
8 changed files
with
1,017 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity 0.8.6; | ||
|
||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import "./interfaces/IGaugeController.sol"; | ||
import "./libraries/TwabLib.sol"; | ||
import "./libraries/ExtendedSafeCastLib.sol"; | ||
|
||
contract GaugeController is IGaugeController { | ||
using ExtendedSafeCastLib for uint256; | ||
|
||
struct GaugeInfo { | ||
uint256 weight; | ||
} | ||
|
||
IERC20 public token; | ||
address public rewardVault; | ||
mapping(address => uint256) public rewards; | ||
mapping(address => uint256) public balances; | ||
mapping(address => mapping(address => uint256)) public gaugeBalances; | ||
|
||
// records total voting power in a gauge | ||
mapping(address => TwabLib.Account) internal gaugeTwabs; | ||
|
||
// records scales for gauges. | ||
mapping(address => TwabLib.Account) internal gaugeScaleTwabs; | ||
|
||
constructor ( | ||
IERC20 _token, | ||
address _rewardVault | ||
) { | ||
token = _token; | ||
rewardVault = _rewardVault; | ||
} | ||
|
||
function deposit(address _to, uint256 _amount) public { | ||
balances[_to] += _amount; | ||
token.transferFrom(_to, address(this), _amount); | ||
} | ||
|
||
function withdraw(uint256 _amount) public { | ||
balances[msg.sender] -= _amount; | ||
token.transfer(msg.sender, _amount); | ||
} | ||
|
||
function increaseGauge(address _gauge, uint256 _amount) public requireGauge(_gauge) { | ||
balances[msg.sender] -= _amount; | ||
gaugeBalances[msg.sender][_gauge] += _amount; | ||
TwabLib.Account storage gaugeTwab = gaugeTwabs[_gauge]; | ||
( | ||
TwabLib.AccountDetails memory twabDetails,, | ||
) = TwabLib.increaseBalance(gaugeTwab, _amount.toUint208(), uint32(block.timestamp)); | ||
gaugeTwab.details = twabDetails; | ||
} | ||
|
||
function decreaseGauge(address _gauge, uint256 _amount) public requireGauge(_gauge) { | ||
balances[msg.sender] += _amount; | ||
gaugeBalances[msg.sender][_gauge] -= _amount; | ||
TwabLib.Account storage gaugeTwab = gaugeTwabs[_gauge]; | ||
( | ||
TwabLib.AccountDetails memory twabDetails,, | ||
) = TwabLib.decreaseBalance(gaugeTwab, _amount.toUint208(), "insuff", uint32(block.timestamp)); | ||
gaugeTwab.details = twabDetails; | ||
} | ||
|
||
function addGauge(address _gauge) public { | ||
addGaugeWithScale(_gauge, 1 ether); | ||
} | ||
|
||
function addGaugeWithScale(address _gauge, uint256 _scale) public { | ||
TwabLib.Account storage gaugeScaleTwab = gaugeScaleTwabs[_gauge]; | ||
( | ||
TwabLib.AccountDetails memory twabDetails,, | ||
) = TwabLib.increaseBalance(gaugeScaleTwab, _scale.toUint208(), uint32(block.timestamp)); | ||
gaugeScaleTwab.details = twabDetails; | ||
} | ||
|
||
function removeGauge(address _gauge) public { | ||
TwabLib.Account storage gaugeScaleTwab = gaugeScaleTwabs[_gauge]; | ||
TwabLib.AccountDetails memory twabDetails = gaugeScaleTwab.details; | ||
( | ||
twabDetails,, | ||
) = TwabLib.decreaseBalance(gaugeScaleTwab, twabDetails.balance, "insuff", uint32(block.timestamp)); | ||
gaugeScaleTwab.details = twabDetails; | ||
} | ||
|
||
function setGaugeScale(address _gauge, uint256 _scale) public { | ||
TwabLib.Account storage gaugeScaleTwab = gaugeScaleTwabs[_gauge]; | ||
TwabLib.AccountDetails memory twabDetails = gaugeScaleTwab.details; | ||
if (twabDetails.balance > _scale) { | ||
( | ||
twabDetails,, | ||
) = TwabLib.decreaseBalance(gaugeScaleTwab, twabDetails.balance - _scale.toUint208(), "insuff", uint32(block.timestamp)); | ||
} else { | ||
( | ||
twabDetails,, | ||
) = TwabLib.increaseBalance(gaugeScaleTwab, _scale.toUint208() - twabDetails.balance, uint32(block.timestamp)); | ||
} | ||
gaugeScaleTwab.details = twabDetails; | ||
} | ||
|
||
function getGauge(address _gauge) public view returns (uint256) { | ||
return gaugeTwabs[_gauge].details.balance; | ||
} | ||
|
||
function getGaugeScale(address _gauge) public view returns (uint256) { | ||
return gaugeScaleTwabs[_gauge].details.balance; | ||
} | ||
|
||
function getScaledAverageGaugeBetween(address _gauge, uint256 _startTime, uint256 _endTime) external override view returns (uint256) { | ||
uint256 gauge = _getAverageGaugeBetween(_gauge, _startTime, _endTime); | ||
uint256 gaugeScale = _getAverageGaugeScaleBetween(_gauge, _startTime, _endTime); | ||
return (gauge*gaugeScale) / 1 ether; | ||
} | ||
|
||
function getAverageGaugeBetween(address _gauge, uint256 _startTime, uint256 _endTime) external view returns (uint256) { | ||
return _getAverageGaugeBetween(_gauge, _startTime, _endTime); | ||
} | ||
|
||
function getAverageGaugeScaleBetween(address _gauge, uint256 _startTime, uint256 _endTime) external view returns (uint256) { | ||
return _getAverageGaugeScaleBetween(_gauge, _startTime, _endTime); | ||
} | ||
|
||
function _getAverageGaugeBetween(address _gauge, uint256 _startTime, uint256 _endTime) internal view returns (uint256) { | ||
TwabLib.AccountDetails memory gaugeDetails = gaugeTwabs[_gauge].details; | ||
return TwabLib.getAverageBalanceBetween( | ||
gaugeTwabs[_gauge].twabs, | ||
gaugeDetails, | ||
uint32(_startTime), | ||
uint32(_endTime), | ||
uint32(block.timestamp) | ||
); | ||
} | ||
|
||
function _getAverageGaugeScaleBetween(address _gauge, uint256 _startTime, uint256 _endTime) internal view returns (uint256) { | ||
TwabLib.AccountDetails memory gaugeScaleDetails = gaugeScaleTwabs[_gauge].details; | ||
return TwabLib.getAverageBalanceBetween( | ||
gaugeScaleTwabs[_gauge].twabs, | ||
gaugeScaleDetails, | ||
uint32(_startTime), | ||
uint32(_endTime), | ||
uint32(block.timestamp) | ||
); | ||
} | ||
|
||
function isGauge(address _gauge) public view returns (bool) { | ||
return gaugeScaleTwabs[_gauge].details.balance > 0; | ||
} | ||
|
||
modifier requireGauge(address _gauge) { | ||
require(isGauge(_gauge), "Gauge does not exist"); | ||
_; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
|
||
pragma solidity 0.8.6; | ||
|
||
import "@pooltogether/owner-manager-contracts/contracts/Manageable.sol"; | ||
import "@pooltogether/v4-periphery/contracts/libraries/BinarySearchLib.sol"; | ||
|
||
import "./interfaces/IPrizeConfigHistory.sol"; | ||
|
||
/** | ||
* @title PoolTogether V4 PrizeConfigHistory | ||
* @author PoolTogether Inc Team | ||
* @notice Contract to store prize configurations | ||
*/ | ||
contract PrizeConfigHistory is IPrizeConfigHistory, Manageable { | ||
/// @dev The uint32[] type is extended with a binarySearch(uint32) function. | ||
using BinarySearchLib for uint32[]; | ||
|
||
/* ============ Variables ============ */ | ||
|
||
/** | ||
* @notice Ordered array of Draw IDs. | ||
* @dev The history, with sequentially ordered ids, can be searched using binary search. | ||
The binary search will find index of a drawId (atOrBefore) using a specific drawId (at). | ||
When a new Draw ID is added to the history, a corresponding mapping of the ID is | ||
updated in the prizeConfigs mapping. | ||
*/ | ||
uint32[] internal history; | ||
|
||
/** | ||
* @notice Mapping of Draw ID to PrizeConfig struct. | ||
* @dev drawId -> PrizeConfig | ||
* @dev The prizeConfigs mapping is updated when a new Draw ID is added to the history. | ||
*/ | ||
mapping(uint32 => PrizeConfig) internal prizeConfigs; | ||
|
||
/* ============ Events ============ */ | ||
|
||
/** | ||
* @notice Emit when a new PrizeConfig is added to history | ||
* @param drawId Draw ID at which the PrizeConfig was pushed and is since valid | ||
* @param prizeConfig PrizeConfig struct | ||
*/ | ||
event PrizeConfigPushed(uint32 indexed drawId, PrizeConfig prizeConfig); | ||
|
||
/** | ||
* @notice Emit when existing PrizeConfig is updated in history | ||
* @param drawId Draw ID at which the PrizeConfig was set and is since valid | ||
* @param prizeConfig PrizeConfig struct | ||
*/ | ||
event PrizeConfigSet(uint32 indexed drawId, PrizeConfig prizeConfig); | ||
|
||
/* ============ Constructor ============ */ | ||
|
||
/** | ||
* @notice PrizeConfigHistory constructor | ||
* @param _owner Address of the owner | ||
*/ | ||
constructor(address _owner) Ownable(_owner) {} | ||
|
||
/* ============ External Functions ============ */ | ||
|
||
/// @inheritdoc IPrizeConfigHistory | ||
function count() external view override returns (uint256) { | ||
return history.length; | ||
} | ||
|
||
/// @inheritdoc IPrizeConfigHistory | ||
function getNewestDrawId() external view override returns (uint32) { | ||
return history[history.length - 1]; | ||
} | ||
|
||
/// @inheritdoc IPrizeConfigHistory | ||
function getOldestDrawId() external view override returns (uint32) { | ||
return history[0]; | ||
} | ||
|
||
/// @inheritdoc IPrizeConfigHistory | ||
function getPrizeConfig(uint32 _drawId) | ||
external | ||
view | ||
override | ||
returns (PrizeConfig memory prizeConfig) | ||
{ | ||
require(_drawId > 0, "PrizeConfHistory/draw-id-gt-zero"); | ||
return prizeConfigs[history.binarySearch(_drawId)]; | ||
} | ||
|
||
/// @inheritdoc IPrizeConfigHistory | ||
function getPrizeConfigAtIndex(uint256 _index) | ||
external | ||
view | ||
override | ||
returns (PrizeConfig memory prizeConfig) | ||
{ | ||
return prizeConfigs[uint32(_index)]; | ||
} | ||
|
||
// @inheritdoc IPrizeConfigHistory | ||
function getPrizeConfigList(uint32[] calldata _drawIds) | ||
external | ||
view | ||
override | ||
returns (PrizeConfig[] memory prizeConfigList) | ||
{ | ||
uint256 _length = _drawIds.length; | ||
PrizeConfig[] memory _data = new PrizeConfig[](_length); | ||
|
||
for (uint256 index = 0; index < _length; index++) { | ||
_data[index] = prizeConfigs[history.binarySearch(_drawIds[index])]; | ||
} | ||
|
||
return _data; | ||
} | ||
|
||
/// @inheritdoc IPrizeConfigHistory | ||
function popAndPush(PrizeConfig calldata _newPrizeConfig) | ||
external | ||
override | ||
onlyOwner | ||
returns (uint32) | ||
{ | ||
uint256 length = history.length; | ||
|
||
require(length > 0, "PrizeConfHistory/history-empty"); | ||
require(history[length - 1] == _newPrizeConfig.drawId, "PrizeConfHistory/invalid-draw-id"); | ||
|
||
_replace(_newPrizeConfig); | ||
|
||
return _newPrizeConfig.drawId; | ||
} | ||
|
||
/// @inheritdoc IPrizeConfigHistory | ||
function push(PrizeConfig calldata _nextPrizeConfig) external override onlyManagerOrOwner { | ||
_push(_nextPrizeConfig); | ||
} | ||
|
||
/// @inheritdoc IPrizeConfigHistory | ||
function replace(PrizeConfig calldata _newPrizeConfig) external override onlyOwner { | ||
_replace(_newPrizeConfig); | ||
} | ||
|
||
/* ============ Internal Functions ============ */ | ||
|
||
/** | ||
* @notice Push PrizeConfigHistory struct onto history array. | ||
* @param _prizeConfig New PrizeConfig struct to push onto history array | ||
*/ | ||
function _push(PrizeConfig memory _prizeConfig) internal { | ||
uint32 _historyLength = uint32(history.length); | ||
|
||
if (_historyLength > 0) { | ||
// TODO: Check if cheaper in gas to only cast to uint32 below | ||
uint32 _id = history[_historyLength - 1]; | ||
|
||
require(_prizeConfig.drawId > _id, "PrizeConfHistory/nonsequentialId"); | ||
} | ||
|
||
history.push(_prizeConfig.drawId); | ||
prizeConfigs[_historyLength] = _prizeConfig; | ||
|
||
emit PrizeConfigPushed(_prizeConfig.drawId, _prizeConfig); | ||
} | ||
|
||
/** | ||
* @notice Replace PrizeConfig struct from history array. | ||
* @dev Performs a binary search to find which index in the history array contains the drawId to replace. | ||
* @param _prizeConfig New PrizeConfig struct that will replace the previous PrizeConfig at the corresponding index. | ||
*/ | ||
function _replace(PrizeConfig calldata _prizeConfig) internal { | ||
require(history.length > 0, "PrizeConfHistory/no-prize-conf"); | ||
|
||
uint32 oldestDrawId = history[0]; | ||
require(_prizeConfig.drawId >= oldestDrawId, "PrizeConfHistory/drawId-beyond"); | ||
|
||
uint32 index = history.binarySearch(_prizeConfig.drawId); | ||
require(history[index] == _prizeConfig.drawId, "PrizeConfHistory/drawId-mismatch"); | ||
|
||
prizeConfigs[index] = _prizeConfig; | ||
emit PrizeConfigSet(_prizeConfig.drawId, _prizeConfig); | ||
} | ||
} |
Oops, something went wrong.