Skip to content

Commit

Permalink
Merge b79273c into 435e91a
Browse files Browse the repository at this point in the history
  • Loading branch information
aodhgan committed Oct 6, 2021
2 parents 435e91a + b79273c commit 694ff0a
Show file tree
Hide file tree
Showing 15 changed files with 248 additions and 251 deletions.
109 changes: 53 additions & 56 deletions contracts/DrawCalculator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ contract DrawCalculator is IDrawCalculator, Ownable {
/// @notice The stored history of draw settings. Stored as ring buffer.
IPrizeDistributionHistory public immutable prizeDistributionHistory;

/// @notice The distributions array length
uint8 public constant DISTRIBUTIONS_LENGTH = 16;
/// @notice The tiers array length
uint8 public constant TIERS_LENGTH = 16;

/* ============ Constructor ============ */

Expand Down Expand Up @@ -124,7 +124,7 @@ contract DrawCalculator is IDrawCalculator, Ownable {
}

/// @inheritdoc IDrawCalculator
function checkPrizeDistributionIndicesForDrawId(
function checkPrizeTierIndexForDrawId(
address _user,
uint64[] calldata _pickIndices,
uint32 _drawId
Expand Down Expand Up @@ -159,16 +159,15 @@ contract DrawCalculator is IDrawCalculator, Ownable {

require(_pickIndices[i] < totalUserPicks, "DrawCalc/insufficient-user-picks");

uint256 distributionIndex = _calculateDistributionIndex(
uint256 tierIndex = _calculateTierIndex(
randomNumberThisPick,
_draws[0].winningRandomNumber,
masks
);

pickPrizes[i] = PickPrize({
won: distributionIndex < _prizeDistributions[0].distributions.length &&
_prizeDistributions[0].distributions[distributionIndex] > 0,
distributionIndex: uint8(distributionIndex)
won: tierIndex < _prizeDistributions[0].tiers.length && _prizeDistributions[0].tiers[tierIndex] > 0,
tierIndex: uint8(tierIndex)
});
}

Expand Down Expand Up @@ -217,7 +216,7 @@ contract DrawCalculator is IDrawCalculator, Ownable {
/**
* @notice Calculates the number of picks a user gets for a Draw, considering the normalized user balance and the PrizeDistribution.
* @dev Divided by 1e18 since the normalized user balance is stored as a fixed point 18 number
* @param _prizeDistribution The prize distribution to consider
* @param _prizeDistribution The PrizeDistribution to consider
* @param _normalizedUserBalance The normalized user balances to consider
* @return The number of picks a user gets for a Draw
*/
Expand All @@ -232,7 +231,7 @@ contract DrawCalculator is IDrawCalculator, Ownable {
* @notice Calculates the normalized balance of a user against the total supply for timestamps
* @param _user The user to consider
* @param _draws The draws we are looking at
* @param _prizeDistributions The prize distributions to consider (needed for draw timestamp offsets)
* @param _prizeDistributions The prize tiers to consider (needed for draw timestamp offsets)
* @return An array of normalized balances
*/
function _getNormalizedBalancesAt(
Expand Down Expand Up @@ -294,13 +293,13 @@ contract DrawCalculator is IDrawCalculator, Ownable {
DrawLib.PrizeDistribution memory _prizeDistribution
) internal pure returns (uint256) {
// prizeCounts stores the number of wins at a distribution index
uint256[] memory prizeCounts = new uint256[](DISTRIBUTIONS_LENGTH);
uint256[] memory prizeCounts = new uint256[](_prizeDistribution.tiers.length);

// create bitmasks for the PrizeDistribution
uint256[] memory masks = _createBitMasks(_prizeDistribution);
uint32 picksLength = uint32(_picks.length);

uint8 maxWinningDistributionIndex = 0;
uint8 maxWinningTierIndex = 0;

require(
picksLength <= _prizeDistribution.maxPicksPerUser,
Expand All @@ -320,52 +319,51 @@ contract DrawCalculator is IDrawCalculator, Ownable {
keccak256(abi.encode(_userRandomNumber, _picks[index]))
);

uint8 distributionIndex = _calculateDistributionIndex(
uint8 tiersIndex = _calculateTierIndex(
randomNumberThisPick,
_winningRandomNumber,
masks
);

// if there is a prize for this distribution index,
// update the maxWinningDistributionIndex and increment prizeCounts for that distribution index
if (distributionIndex < DISTRIBUTIONS_LENGTH) {
if (distributionIndex > maxWinningDistributionIndex) {
maxWinningDistributionIndex = distributionIndex;
// there is prize for this tier index
if (tiersIndex < TIERS_LENGTH) {
if (tiersIndex > maxWinningTierIndex) {
maxWinningTierIndex = tiersIndex;
}
prizeCounts[distributionIndex]++;
prizeCounts[tiersIndex]++;
}
}

// now calculate prizeFraction given prizeCounts
uint256 prizeFraction = 0;
uint256[] memory prizeDistributionFractions = _calculatePrizeDistributionFractions(
uint256[] memory prizeTiersFractions = _calculatePrizeTierFractions(
_prizeDistribution,
maxWinningDistributionIndex
maxWinningTierIndex
);

// multiple the fractions by the prizeCounts and add them up
for (
uint256 prizeCountIndex = 0;
prizeCountIndex <= maxWinningDistributionIndex;
prizeCountIndex <= maxWinningTierIndex;
prizeCountIndex++
) {
if (prizeCounts[prizeCountIndex] > 0) {
prizeFraction +=
prizeDistributionFractions[prizeCountIndex] *
prizeTiersFractions[prizeCountIndex] *
prizeCounts[prizeCountIndex];
}
}

// return the absolute amount of prize
return (prizeFraction * _prizeDistribution.prize) / 1e9; // div by 1e9 as prize distributions are fixed point 1e9
// return the absolute amount of prize awardable
return (prizeFraction * _prizeDistribution.prize) / 1e9; // div by 1e9 as prize tiers are base 1e9
}

///@notice Calculates the distribution index given the random numbers and masks
///@param _randomNumberThisPick Users random number for this Pick
///@param _winningRandomNumber The winning number for this Draw
///@param _masks The pre-calculated bitmasks for the PrizeDistribution
///@return The position within the prize distribution array (0 = top prize, 1 = runner-up prize, etc)
function _calculateDistributionIndex(
///@notice Calculates the tier index given the random numbers and masks
///@param _randomNumberThisPick users random number for this Pick
///@param _winningRandomNumber The winning number for this draw
///@param _masks The pre-calculate bitmasks for the prizeDistributions
///@return The position within the prize tier array (0 = top prize, 1 = runner-up prize, etc)
function _calculateTierIndex(
uint256 _randomNumberThisPick,
uint256 _winningRandomNumber,
uint256[] memory _masks
Expand Down Expand Up @@ -415,42 +413,41 @@ contract DrawCalculator is IDrawCalculator, Ownable {
/**
* @notice Calculates the expected prize fraction per PrizeDistributions and distributionIndex
* @param _prizeDistribution prizeDistribution struct for Draw
* @param _distributionIndex Index of the prize distributions array to calculate
* @return returns the fraction of the total prize (fixed point 1e9)
* @param _prizeTierIndex Index of the prize tiers array to calculate
* @return returns the fraction of the total prize (base 1e18)
*/
function _calculatePrizeDistributionFraction(
function _calculatePrizeTierFraction(
DrawLib.PrizeDistribution memory _prizeDistribution,
uint256 _distributionIndex
uint256 _prizeTierIndex
) internal pure returns (uint256) {

// get the distribution at that index
uint256 prizeFraction = _prizeDistribution.distributions[_distributionIndex];
// get the prize fraction at that index
uint256 prizeFraction = _prizeDistribution.tiers[_prizeTierIndex];

// calculate number of prizes for that index
uint256 numberOfPrizesForIndex = _numberOfPrizesForIndex(
_prizeDistribution.bitRangeSize,
_distributionIndex
_prizeTierIndex
);

return prizeFraction / numberOfPrizesForIndex;
}

/**
* @notice Generates an array of prize distributions fractions
* @param _prizeDistribution PrizeDistribution struct for Draw
* @param _maxWinningDistributionIndex Max length of the prize distributions array
* @return returns an array of prize distributions fractions
* @notice Generates an array of prize tiers fractions
* @param _prizeDistribution prizeDistribution struct for Draw
* @param maxWinningTierIndex Max length of the prize tiers array
* @return returns an array of prize tiers fractions
*/
function _calculatePrizeDistributionFractions(
function _calculatePrizeTierFractions(
DrawLib.PrizeDistribution memory _prizeDistribution,
uint8 _maxWinningDistributionIndex
uint8 maxWinningTierIndex
) internal pure returns (uint256[] memory) {
uint256[] memory prizeDistributionFractions = new uint256[](
_maxWinningDistributionIndex + 1
maxWinningTierIndex + 1
);

for (uint8 i = 0; i <= _maxWinningDistributionIndex; i++) {
prizeDistributionFractions[i] = _calculatePrizeDistributionFraction(
for (uint8 i = 0; i <= maxWinningTierIndex; i++) {
prizeDistributionFractions[i] = _calculatePrizeTierFraction(
_prizeDistribution,
i
);
Expand All @@ -460,22 +457,22 @@ contract DrawCalculator is IDrawCalculator, Ownable {
}

/**
* @notice Calculates the number of prizes at a distributionIndex
* @param _bitRangeSize bitRangeSize for Draw
* @param _distributionIndex Index of the prize distribution
* @return Returns the number of prizes at a distributionIndex
* @notice Calculates the number of prizes for a given prizeDistributionIndex
* @param _bitRangeSize Bit range size for Draw
* @param _prizeTierIndex Index of the prize tier array to calculate
* @return returns the fraction of the total prize (base 1e18)
*/
function _numberOfPrizesForIndex(uint8 _bitRangeSize, uint256 _distributionIndex)
function _numberOfPrizesForIndex(uint8 _bitRangeSize, uint256 _prizeTierIndex)
internal
pure
returns (uint256)
{
uint256 bitRangeDecimal = 2**uint256(_bitRangeSize);
uint256 numberOfPrizesForIndex = bitRangeDecimal**_distributionIndex;
uint256 numberOfPrizesForIndex = bitRangeDecimal**_prizeTierIndex;

while (_distributionIndex > 0) {
numberOfPrizesForIndex -= bitRangeDecimal**(_distributionIndex - 1);
_distributionIndex--;
while (_prizeTierIndex > 0) {
numberOfPrizesForIndex -= bitRangeDecimal**(_prizeTierIndex - 1);
_prizeTierIndex--;
}

return numberOfPrizesForIndex;
Expand Down
26 changes: 13 additions & 13 deletions contracts/PrizeDistributionHistory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ contract PrizeDistributionHistory is IPrizeDistributionHistory, Manageable {

uint256 internal constant MAX_CARDINALITY = 256;

uint256 internal constant DISTRIBUTION_CEILING = 1e9;
uint256 internal constant TIERS_CEILING = 1e9;

event Deployed(uint8 cardinality);

Expand Down Expand Up @@ -189,25 +189,25 @@ contract PrizeDistributionHistory is IPrizeDistributionHistory, Manageable {
require(_prizeDistribution.bitRangeSize > 0, "DrawCalc/bitRangeSize-gt-0");
require(_prizeDistribution.maxPicksPerUser > 0, "DrawCalc/maxPicksPerUser-gt-0");

// ensure that the sum of the distributions are not gt 100% and record number of non-zero distributions entries
uint256 sumTotalDistributions = 0;
uint256 nonZeroDistributions = 0;
uint256 distributionsLength = _prizeDistribution.distributions.length;
// ensure that the sum of the tiers are not gt 100% and record number of non-zero tiers entries
uint256 sumTotalTiers = 0;
uint256 nonZeroTiers = 0;
uint256 tiersLength = _prizeDistribution.tiers.length;

for (uint256 index = 0; index < distributionsLength; index++) {
sumTotalDistributions += _prizeDistribution.distributions[index];
for (uint256 index = 0; index < tiersLength; index++) {
sumTotalTiers += _prizeDistribution.tiers[index];

if (_prizeDistribution.distributions[index] > 0) {
nonZeroDistributions++;
if (_prizeDistribution.tiers[index] > 0) {
nonZeroTiers++;
}
}

// Each distribution amount stored as uint32 - summed can't exceed 1e9
require(sumTotalDistributions <= DISTRIBUTION_CEILING, "DrawCalc/distributions-gt-100%");
// Each tier amount stored as uint32 - summed can't exceed 1e9
require(sumTotalTiers <= TIERS_CEILING, "DrawCalc/tiers-gt-100%");

require(
_prizeDistribution.matchCardinality >= nonZeroDistributions,
"DrawCalc/matchCardinality-gte-distributions"
_prizeDistribution.matchCardinality >= nonZeroTiers,
"DrawCalc/matchCardinality-gte-tiers"
);

DrawRingBufferLib.Buffer memory buffer = _prizeDistributionsRingBufferData;
Expand Down
10 changes: 5 additions & 5 deletions contracts/interfaces/IDrawCalculator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import "../libraries/DrawLib.sol";
interface IDrawCalculator {
struct PickPrize {
bool won;
uint8 distributionIndex;
uint8 tierIndex;
}

///@notice Emitted when the contract is initialized
Expand Down Expand Up @@ -65,12 +65,12 @@ interface IDrawCalculator {

/**
* @notice Returns a users balances expressed as a fraction of the total supply over time.
* @param _user The user for which to calculate the distribution indices
* @param _user The user for which to calculate the tiers indices
* @param _pickIndices The users pick indices for a draw
* @param _drawId The draw for which to calculate the distribution indices
* @return List of distributions for Draw.drawId
* @param _drawId The draw for which to calculate the tiers indices
* @return List of PrizePicks for Draw.drawId
*/
function checkPrizeDistributionIndicesForDrawId(
function checkPrizeTierIndexForDrawId(
address _user,
uint64[] calldata _pickIndices,
uint32 _drawId
Expand Down
8 changes: 4 additions & 4 deletions contracts/libraries/DrawLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ library DrawLib {
uint32 beaconPeriodSeconds;
}

/// @notice Fixed length of distributions within a PrizeDistribution.distributions
uint8 public constant DISTRIBUTIONS_LENGTH = 16;
/// @notice Fixed length of prize tiers within a PrizeDistribution.tiers
uint8 public constant TIERS_LENGTH = 16;

///@notice PrizeDistribution struct created every draw
///@param bitRangeSize Decimal representation of bitRangeSize
Expand All @@ -27,7 +27,7 @@ library DrawLib {
///@param endTimestampOffset The end time offset in seconds from which Ticket balances are calculated.
///@param maxPicksPerUser Maximum number of picks a user can make in this draw
///@param numberOfPicks Number of picks this draw has (may vary across networks according to how much the network has contributed to the Reserve)
///@param distributions Array of prize distributions percentages, expressed in fraction form with base 1e9. Ordering: index0: grandPrize, index1: runnerUp, etc.
///@param tiers Array of prize tiers percentages, expressed in fraction form with base 1e9. Ordering: index0: grandPrize, index1: runnerUp, etc.
///@param prize Total prize amount available in this draw calculator for this draw (may vary from across networks)
struct PrizeDistribution {
uint8 bitRangeSize;
Expand All @@ -36,7 +36,7 @@ library DrawLib {
uint32 endTimestampOffset;
uint32 maxPicksPerUser;
uint136 numberOfPicks;
uint32[DISTRIBUTIONS_LENGTH] distributions;
uint32[TIERS_LENGTH] tiers;
uint256 prize;
}
}
18 changes: 9 additions & 9 deletions contracts/test/DrawCalculatorHarness.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ contract DrawCalculatorHarness is DrawCalculator {
PrizeDistributionHistory _prizeDistributionHistory
) DrawCalculator(_owner, _ticket, _drawHistory, _prizeDistributionHistory) {}

function calculateDistributionIndex(
function calculateTierIndex(
uint256 _randomNumberThisPick,
uint256 _winningRandomNumber,
uint256[] memory _masks
) public pure returns (uint256) {
return _calculateDistributionIndex(_randomNumberThisPick, _winningRandomNumber, _masks);
return _calculateTierIndex(_randomNumberThisPick, _winningRandomNumber, _masks);
}

function createBitMasks(DrawLib.PrizeDistribution calldata _prizeDistribution)
Expand All @@ -29,23 +29,23 @@ contract DrawCalculatorHarness is DrawCalculator {
return _createBitMasks(_prizeDistribution);
}

///@notice Calculates the expected prize fraction per prizeDistribution and prizeDistributionIndex
///@notice Calculates the expected prize fraction per prizeDistribution and prizeTierIndex
///@param _prizeDistribution prizeDistribution struct for Draw
///@param _prizeDistributionIndex Index of the prize distributions array to calculate
///@param _prizeTierIndex Index of the prize tiers array to calculate
///@return returns the fraction of the total prize
function calculatePrizeDistributionFraction(
function calculatePrizeTierFraction(
DrawLib.PrizeDistribution calldata _prizeDistribution,
uint256 _prizeDistributionIndex
uint256 _prizeTierIndex
) external pure returns (uint256) {
return _calculatePrizeDistributionFraction(_prizeDistribution, _prizeDistributionIndex);
return _calculatePrizeTierFraction(_prizeDistribution, _prizeTierIndex);
}

function numberOfPrizesForIndex(uint8 _bitRangeSize, uint256 _prizeDistributionIndex)
function numberOfPrizesForIndex(uint8 _bitRangeSize, uint256 _prizeTierIndex)
external
pure
returns (uint256)
{
return _numberOfPrizesForIndex(_bitRangeSize, _prizeDistributionIndex);
return _numberOfPrizesForIndex(_bitRangeSize, _prizeTierIndex);
}

function calculateNumberOfUserPicks(
Expand Down
4 changes: 2 additions & 2 deletions hardhat/prizeDistributions.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const { utils } = require('ethers');

const distributions = [
const tiers = [
utils.parseEther('0.9'),
utils.parseEther('0.1'),
utils.parseEther('0.1'),
utils.parseEther('0.1'),
];

module.exports = distributions;
module.exports = tiers;

0 comments on commit 694ff0a

Please sign in to comment.