Skip to content

Commit

Permalink
Merge pull request #263 from pooltogether/pool-2263-migrate-drawcalcu…
Browse files Browse the repository at this point in the history
…lator

Add DrawCalculatorV3 and PrizeConfigHistory
  • Loading branch information
PierrickGT committed May 12, 2022
2 parents fc8a579 + 78164ce commit 5333dc3
Show file tree
Hide file tree
Showing 8 changed files with 1,017 additions and 0 deletions.
493 changes: 493 additions & 0 deletions contracts/DrawCalculatorV3.sol

Large diffs are not rendered by default.

154 changes: 154 additions & 0 deletions contracts/GaugeController.sol
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");
_;
}
}
182 changes: 182 additions & 0 deletions contracts/PrizeConfigHistory.sol
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);
}
}
Loading

0 comments on commit 5333dc3

Please sign in to comment.