Skip to content

Commit

Permalink
update(PrizeDistributorV2): add contracts and commented unit tests (#264
Browse files Browse the repository at this point in the history
)
  • Loading branch information
kamescg committed May 17, 2022
1 parent 9eb03ac commit 954c9c1
Show file tree
Hide file tree
Showing 4 changed files with 385 additions and 4 deletions.
7 changes: 5 additions & 2 deletions contracts/DrawCalculatorV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ contract DrawCalculatorV3 is IDrawCalculatorV3, Manageable {

/// @notice DrawBuffer address
IDrawBuffer public immutable drawBuffer;

IPrizeConfigHistory public immutable prizeConfigHistory;

/// @notice The tiers array length
uint8 public constant TIERS_LENGTH = 16;
Expand Down Expand Up @@ -59,6 +61,7 @@ contract DrawCalculatorV3 is IDrawCalculatorV3, Manageable {
constructor(
IGaugeController _gaugeController,
IDrawBuffer _drawBuffer,
IPrizeConfigHistory _prizeConfigHistory,
address _owner
) Ownable(_owner) {
require(address(_gaugeController) != address(0), "DrawCalc/GC-not-zero-address");
Expand All @@ -67,6 +70,7 @@ contract DrawCalculatorV3 is IDrawCalculatorV3, Manageable {

gaugeController = _gaugeController;
drawBuffer = _drawBuffer;
prizeConfigHistory = _prizeConfigHistory;

emit Deployed(_gaugeController, _drawBuffer);
}
Expand All @@ -76,7 +80,6 @@ contract DrawCalculatorV3 is IDrawCalculatorV3, Manageable {
/// @inheritdoc IDrawCalculatorV3
function calculate(
ITicket _ticket,
IPrizeConfigHistory _prizeConfigHistory,
address _user,
uint32[] calldata _drawIds,
bytes calldata _pickIndicesForDraws
Expand All @@ -89,7 +92,7 @@ contract DrawCalculatorV3 is IDrawCalculatorV3, Manageable {

return _calculatePrizesAwardable(
_ticket,
_prizeConfigHistory,
prizeConfigHistory,
_user,
_userRandomNumber,
_drawIds,
Expand Down
197 changes: 197 additions & 0 deletions contracts/PrizeDistributorV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@pooltogether/owner-manager-contracts/contracts/Manageable.sol";
import "./interfaces/IDrawCalculatorV3.sol";

/**
* @title PoolTogether V4 PrizeDistributorV2
* @author PoolTogether Inc Team
* @notice The PrizeDistributorV2 contract holds Tickets (captured interest) and distributes tickets to users with winning draw claims.
PrizeDistributorV2 uses an external IDrawCalculatorV3 to validate a users draw claim, before awarding payouts. To prevent users
from reclaiming prizes, a payout history for each draw claim is mapped to user accounts. Reclaiming a draw can occur
if an "optimal" prize was not included in previous claim pick indices and the new claims updated payout is greater then
the previous prize distributor claim payout.
*/
contract PrizeDistributorV2 is Manageable {
using SafeERC20 for IERC20;

/**
* @notice Emit when user has claimed token from the PrizeDistributorV2.
* @param user User address receiving draw claim payouts
* @param drawId Draw id that was paid out
* @param payout Payout for draw
*/
event ClaimedDraw(address indexed user, uint32 indexed drawId, uint256 payout);

/**
* @notice Emit when IDrawCalculatorV3 is set.
* @param calculator IDrawCalculatorV3 address
*/
event DrawCalculatorSet(IDrawCalculatorV3 indexed calculator);

/**
* @notice Emit when Token is set.
* @param token Token address
*/
event TokenSet(IERC20 indexed token);

/**
* @notice Emit when ERC20 tokens are withdrawn.
* @param token ERC20 token transferred.
* @param to Address that received funds.
* @param amount Amount of tokens transferred.
*/
event ERC20Withdrawn(IERC20 indexed token, address indexed to, uint256 amount);

/* ============ Global Variables ============ */

/// @notice IDrawCalculatorV3 address
IDrawCalculatorV3 internal drawCalculator;

/// @notice Token address
IERC20 internal immutable token;

/// @notice Maps users => drawId => paid out balance
mapping(address => mapping(uint256 => uint256)) internal userDrawPayouts;

/* ============ Initialize ============ */

/**
* @notice Initialize PrizeDistributorV2 smart contract.
* @param _owner Owner address
* @param _token Token address
* @param _drawCalculator IDrawCalculatorV3 address
*/
constructor(
address _owner,
IERC20 _token,
IDrawCalculatorV3 _drawCalculator
) Ownable(_owner) {
_setDrawCalculator(_drawCalculator);
require(address(_token) != address(0), "PrizeDistributorV2/token-not-zero-address");
token = _token;
emit TokenSet(_token);
}

/* ============ External Functions ============ */

function claim(
ITicket _ticket,
address _user,
uint32[] calldata _drawIds,
bytes calldata _data
) external returns (uint256) {

uint256 totalPayout;

(uint256[] memory drawPayouts, ) = drawCalculator.calculate(_ticket, _user, _drawIds, _data); // neglect the prizeCounts since we are not interested in them here

uint256 drawPayoutsLength = drawPayouts.length;
for (uint256 payoutIndex = 0; payoutIndex < drawPayoutsLength; payoutIndex++) {
uint32 drawId = _drawIds[payoutIndex];
uint256 payout = drawPayouts[payoutIndex];
uint256 oldPayout = _getDrawPayoutBalanceOf(_user, drawId);
uint256 payoutDiff = 0;

// helpfully short-circuit, in case the user screwed something up.
require(payout > oldPayout, "PrizeDistributorV2/zero-payout");

unchecked {
payoutDiff = payout - oldPayout;
}

_setDrawPayoutBalanceOf(_user, drawId, payout);

totalPayout += payoutDiff;

emit ClaimedDraw(_user, drawId, payoutDiff);
}

_awardPayout(_user, totalPayout);

return totalPayout;
}

function withdrawERC20(
IERC20 _erc20Token,
address _to,
uint256 _amount
) external onlyManagerOrOwner returns (bool) {
require(_to != address(0), "PrizeDistributorV2/recipient-not-zero-address");
require(address(_erc20Token) != address(0), "PrizeDistributorV2/ERC20-not-zero-address");

_erc20Token.safeTransfer(_to, _amount);

emit ERC20Withdrawn(_erc20Token, _to, _amount);

return true;
}

function getDrawCalculator() external view returns (IDrawCalculatorV3) {
return drawCalculator;
}

function getDrawPayoutBalanceOf(address _user, uint32 _drawId)
external
view
returns (uint256)
{
return _getDrawPayoutBalanceOf(_user, _drawId);
}

function getToken() external view returns (IERC20) {
return token;
}

function setDrawCalculator(IDrawCalculatorV3 _newCalculator)
external
onlyManagerOrOwner
returns (IDrawCalculatorV3)
{
_setDrawCalculator(_newCalculator);
return _newCalculator;
}

/* ============ Internal Functions ============ */

function _getDrawPayoutBalanceOf(address _user, uint32 _drawId)
internal
view
returns (uint256)
{
return userDrawPayouts[_user][_drawId];
}

function _setDrawPayoutBalanceOf(
address _user,
uint32 _drawId,
uint256 _payout
) internal {
userDrawPayouts[_user][_drawId] = _payout;
}

/**
* @notice Sets IDrawCalculatorV3 reference for individual draw id.
* @param _newCalculator IDrawCalculatorV3 address
*/
function _setDrawCalculator(IDrawCalculatorV3 _newCalculator) internal {
require(address(_newCalculator) != address(0), "PrizeDistributorV2/calc-not-zero");
drawCalculator = _newCalculator;

emit DrawCalculatorSet(_newCalculator);
}

/**
* @notice Transfer claimed draw(s) total payout to user.
* @param _to User address
* @param _amount Transfer amount
*/
function _awardPayout(address _to, uint256 _amount) internal {
token.safeTransfer(_to, _amount);
}

}
2 changes: 0 additions & 2 deletions contracts/interfaces/IDrawCalculatorV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ interface IDrawCalculatorV3 {
/**
* @notice Calculates the awardable prizes for a user for Multiple Draws. Typically called by a PrizeDistributor.
* @param ticket Address of the ticket to calculate awardable prizes for
* @param prizeConfigHistory Address of the prizeConfigHistory associated with the ticket
* @param user Address of the user for which to calculate awardable prizes for
* @param drawIds Array of DrawIds for which to calculate awardable prizes for
* @param data ABI encoded pick indices for all Draws. Expected to be winning picks. Pick indices must be less than the totalUserPicks.
Expand All @@ -25,7 +24,6 @@ interface IDrawCalculatorV3 {
*/
function calculate(
ITicket ticket,
IPrizeConfigHistory prizeConfigHistory,
address user,
uint32[] calldata drawIds,
bytes calldata data
Expand Down

0 comments on commit 954c9c1

Please sign in to comment.