Skip to content

Commit

Permalink
Formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
Dylan DesRosier committed Jun 27, 2023
1 parent cb094fb commit 2b4cd6b
Show file tree
Hide file tree
Showing 24 changed files with 3,913 additions and 3,352 deletions.
1,430 changes: 799 additions & 631 deletions src/PrizePool.sol

Large diffs are not rendered by default.

1,164 changes: 686 additions & 478 deletions src/abstract/TieredLiquidityDistributor.sol

Large diffs are not rendered by default.

39 changes: 19 additions & 20 deletions src/libraries/BitLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,23 @@ pragma solidity 0.8.17;

/// @title Helper functions to retrieve on bit from a word of bits.
library BitLib {
/// @notice Flips one bit in a packed array of bits.
/// @param packedBits The bit storage. There are 256 bits in a uint256.
/// @param bit The bit to flip
/// @return The passed bit storage that has the desired bit flipped.
function flipBit(uint256 packedBits, uint8 bit) internal pure returns (uint256) {
// create mask
uint256 mask = 0x1 << bit;
return packedBits ^ mask;
}

/// @notice Flips one bit in a packed array of bits.
/// @param packedBits The bit storage. There are 256 bits in a uint256.
/// @param bit The bit to flip
/// @return The passed bit storage that has the desired bit flipped.
function flipBit(uint256 packedBits, uint8 bit) internal pure returns (uint256) {
// create mask
uint256 mask = 0x1 << bit;
return packedBits ^ mask;
}

/// @notice Retrieves the value of one bit from a packed array of bits.
/// @param packedBits The bit storage. There are 256 bits in a uint256.
/// @param bit The bit to retrieve
/// @return The value of the desired bit
function getBit(uint256 packedBits, uint8 bit) internal pure returns (bool) {
uint256 mask = (0x1 << bit);// ^ type(uint256).max;
// 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
return (packedBits & mask) >> bit == 1;
}
}
/// @notice Retrieves the value of one bit from a packed array of bits.
/// @param packedBits The bit storage. There are 256 bits in a uint256.
/// @param bit The bit to retrieve
/// @return The value of the desired bit
function getBit(uint256 packedBits, uint8 bit) internal pure returns (bool) {
uint256 mask = (0x1 << bit); // ^ type(uint256).max;
// 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
return (packedBits & mask) >> bit == 1;
}
}
683 changes: 372 additions & 311 deletions src/libraries/DrawAccumulatorLib.sol

Large diffs are not rendered by default.

238 changes: 129 additions & 109 deletions src/libraries/TierCalculationLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,132 +9,152 @@ import { UD60x18, toUD60x18, fromUD60x18 } from "prb-math/UD60x18.sol";
/// @author PoolTogether Inc. Team
/// @notice Provides helper functions to assist in calculating tier prize counts, frequency, and odds.
library TierCalculationLib {
/// @notice Calculates the odds of a tier occurring
/// @param _tier The tier to calculate odds for
/// @param _numberOfTiers The total number of tiers
/// @param _grandPrizePeriod The number of draws between grand prizes
/// @return The odds that a tier should occur for a single draw.
function getTierOdds(
uint8 _tier,
uint8 _numberOfTiers,
uint16 _grandPrizePeriod
) internal pure returns (SD59x18) {
SD59x18 _k = sd(1).div(sd(int16(_grandPrizePeriod))).ln().div(
sd((-1 * int8(_numberOfTiers) + 1))
);

/// @notice Calculates the odds of a tier occurring
/// @param _tier The tier to calculate odds for
/// @param _numberOfTiers The total number of tiers
/// @param _grandPrizePeriod The number of draws between grand prizes
/// @return The odds that a tier should occur for a single draw.
function getTierOdds(uint8 _tier, uint8 _numberOfTiers, uint16 _grandPrizePeriod) internal pure returns (SD59x18) {
SD59x18 _k = sd(1).div(
sd(int16(_grandPrizePeriod))
).ln().div(
sd((-1 * int8(_numberOfTiers) + 1))
);
return E.pow(_k.mul(sd(int8(_tier) - (int8(_numberOfTiers) - 1))));
}

return E.pow(_k.mul(sd(int8(_tier) - (int8(_numberOfTiers) - 1))));
}
/// @notice Estimates the number of draws between a tier occurring
/// @param _tier The tier to calculate the frequency of
/// @param _numberOfTiers The total number of tiers
/// @param _grandPrizePeriod The number of draws between grand prizes
/// @return The estimated number of draws between the tier occurring
function estimatePrizeFrequencyInDraws(
uint8 _tier,
uint8 _numberOfTiers,
uint16 _grandPrizePeriod
) internal pure returns (uint256) {
return
uint256(
fromSD59x18(
sd(1e18)
.div(TierCalculationLib.getTierOdds(_tier, _numberOfTiers, _grandPrizePeriod))
.ceil()
)
);
}

/// @notice Estimates the number of draws between a tier occurring
/// @param _tier The tier to calculate the frequency of
/// @param _numberOfTiers The total number of tiers
/// @param _grandPrizePeriod The number of draws between grand prizes
/// @return The estimated number of draws between the tier occurring
function estimatePrizeFrequencyInDraws(uint8 _tier, uint8 _numberOfTiers, uint16 _grandPrizePeriod) internal pure returns (uint256) {
return uint256(fromSD59x18(
sd(1e18).div(TierCalculationLib.getTierOdds(_tier, _numberOfTiers, _grandPrizePeriod)).ceil()
));
}
/// @notice Computes the number of prizes for a given tier
/// @param _tier The tier to compute for
/// @return The number of prizes
function prizeCount(uint8 _tier) internal pure returns (uint256) {
uint256 _numberOfPrizes = 4 ** _tier;

/// @notice Computes the number of prizes for a given tier
/// @param _tier The tier to compute for
/// @return The number of prizes
function prizeCount(uint8 _tier) internal pure returns (uint256) {
uint256 _numberOfPrizes = 4 ** _tier;
return _numberOfPrizes;
}

return _numberOfPrizes;
}
/// @notice Computes the number of canary prizes as a fraction, based on the share distribution. This is important because the canary prizes should be indicative of the smallest prizes if
/// the number of prize tiers was to increase by 1.
/// @param _numberOfTiers The number of tiers
/// @param _canaryShares The number of shares allocated to canary prizes
/// @param _reserveShares The number of shares allocated to the reserve
/// @param _tierShares The number of shares allocated to prize tiers
/// @return The number of canary prizes, including fractional prizes.
function canaryPrizeCount(
uint8 _numberOfTiers,
uint8 _canaryShares,
uint8 _reserveShares,
uint8 _tierShares
) internal pure returns (UD60x18) {
uint256 numerator = uint256(_canaryShares) *
((_numberOfTiers + 1) * uint256(_tierShares) + _canaryShares + _reserveShares);
uint256 denominator = uint256(_tierShares) *
((_numberOfTiers) * uint256(_tierShares) + _canaryShares + _reserveShares);
UD60x18 multiplier = toUD60x18(numerator).div(toUD60x18(denominator));
return multiplier.mul(toUD60x18(prizeCount(_numberOfTiers)));
}

/// @notice Computes the number of canary prizes as a fraction, based on the share distribution. This is important because the canary prizes should be indicative of the smallest prizes if
/// the number of prize tiers was to increase by 1.
/// @param _numberOfTiers The number of tiers
/// @param _canaryShares The number of shares allocated to canary prizes
/// @param _reserveShares The number of shares allocated to the reserve
/// @param _tierShares The number of shares allocated to prize tiers
/// @return The number of canary prizes, including fractional prizes.
function canaryPrizeCount(
uint8 _numberOfTiers,
uint8 _canaryShares,
uint8 _reserveShares,
uint8 _tierShares
) internal pure returns (UD60x18) {
uint256 numerator = uint256(_canaryShares) * ((_numberOfTiers+1) * uint256(_tierShares) + _canaryShares + _reserveShares);
uint256 denominator = uint256(_tierShares) * ((_numberOfTiers) * uint256(_tierShares) + _canaryShares + _reserveShares);
UD60x18 multiplier = toUD60x18(numerator).div(toUD60x18(denominator));
return multiplier.mul(toUD60x18(prizeCount(_numberOfTiers)));
/// @notice Determines if a user won a prize tier
/// @param _userSpecificRandomNumber The random number to use as entropy
/// @param _userTwab The user's time weighted average balance
/// @param _vaultTwabTotalSupply The vault's time weighted average total supply
/// @param _vaultContributionFraction The portion of the prize that was contributed by the vault
/// @param _tierOdds The odds of the tier occurring
/// @return True if the user won the tier, false otherwise
function isWinner(
uint256 _userSpecificRandomNumber,
uint128 _userTwab,
uint128 _vaultTwabTotalSupply,
SD59x18 _vaultContributionFraction,
SD59x18 _tierOdds
) internal pure returns (bool) {
if (_vaultTwabTotalSupply == 0) {
return false;
}

/// @notice Determines if a user won a prize tier
/// @param _userSpecificRandomNumber The random number to use as entropy
/// @param _userTwab The user's time weighted average balance
/// @param _vaultTwabTotalSupply The vault's time weighted average total supply
/// @param _vaultContributionFraction The portion of the prize that was contributed by the vault
/// @param _tierOdds The odds of the tier occurring
/// @return True if the user won the tier, false otherwise
function isWinner(
uint256 _userSpecificRandomNumber,
uint128 _userTwab,
uint128 _vaultTwabTotalSupply,
SD59x18 _vaultContributionFraction,
SD59x18 _tierOdds
) internal pure returns (bool) {
if (_vaultTwabTotalSupply == 0) {
return false;
}
/*
/*
The user-held portion of the total supply is the "winning zone". If the above pseudo-random number falls within the winning zone, the user has won this tier
However, we scale the size of the zone based on:
- Odds of the tier occuring
- Number of prizes
- Portion of prize that was contributed by the vault
*/
// first constrain the random number to be within the vault total supply
uint256 constrainedRandomNumber = _userSpecificRandomNumber % (_vaultTwabTotalSupply);
uint256 winningZone = calculateWinningZone(_userTwab, _vaultContributionFraction, _tierOdds);
// first constrain the random number to be within the vault total supply
uint256 constrainedRandomNumber = _userSpecificRandomNumber % (_vaultTwabTotalSupply);
uint256 winningZone = calculateWinningZone(_userTwab, _vaultContributionFraction, _tierOdds);

return constrainedRandomNumber < winningZone;
}
return constrainedRandomNumber < winningZone;
}

/// @notice Calculates a pseudo-random number that is unique to the user, tier, and winning random number
/// @param _user The user
/// @param _tier The tier
/// @param _prizeIndex The particular prize index they are checking
/// @param _winningRandomNumber The winning random number
/// @return A pseudo-random number
function calculatePseudoRandomNumber(
address _user,
uint8 _tier,
uint32 _prizeIndex,
uint256 _winningRandomNumber
) internal pure returns (uint256) {
return uint256(keccak256(abi.encode(_user, _tier, _prizeIndex, _winningRandomNumber)));
}
/// @notice Calculates a pseudo-random number that is unique to the user, tier, and winning random number
/// @param _user The user
/// @param _tier The tier
/// @param _prizeIndex The particular prize index they are checking
/// @param _winningRandomNumber The winning random number
/// @return A pseudo-random number
function calculatePseudoRandomNumber(
address _user,
uint8 _tier,
uint32 _prizeIndex,
uint256 _winningRandomNumber
) internal pure returns (uint256) {
return uint256(keccak256(abi.encode(_user, _tier, _prizeIndex, _winningRandomNumber)));
}

/// @notice Calculates the winning zone for a user. If their pseudo-random number falls within this zone, they win the tier.
/// @param _userTwab The user's time weighted average balance
/// @param _vaultContributionFraction The portion of the prize that was contributed by the vault
/// @param _tierOdds The odds of the tier occurring
/// @return The winning zone for the user.
function calculateWinningZone(
uint256 _userTwab,
SD59x18 _vaultContributionFraction,
SD59x18 _tierOdds
) internal pure returns (uint256) {
return uint256(fromSD59x18(
toSD59x18(int256(_userTwab)).mul(_tierOdds).mul(_vaultContributionFraction)
));
}
/// @notice Calculates the winning zone for a user. If their pseudo-random number falls within this zone, they win the tier.
/// @param _userTwab The user's time weighted average balance
/// @param _vaultContributionFraction The portion of the prize that was contributed by the vault
/// @param _tierOdds The odds of the tier occurring
/// @return The winning zone for the user.
function calculateWinningZone(
uint256 _userTwab,
SD59x18 _vaultContributionFraction,
SD59x18 _tierOdds
) internal pure returns (uint256) {
return
uint256(
fromSD59x18(toSD59x18(int256(_userTwab)).mul(_tierOdds).mul(_vaultContributionFraction))
);
}

/// @notice Computes the estimated number of prizes per draw given the number of tiers and the grand prize period.
/// @param _numberOfTiers The number of tiers
/// @param _grandPrizePeriod The grand prize period
/// @return The estimated number of prizes per draw
function estimatedClaimCount(uint8 _numberOfTiers, uint16 _grandPrizePeriod) internal pure returns (uint32) {
uint32 count = 0;
for (uint8 i = 0; i < _numberOfTiers; i++) {
count += uint32(uint256(unwrap(sd(int256(prizeCount(i))).mul(getTierOdds(i, _numberOfTiers, _grandPrizePeriod)))));
}
return count;
/// @notice Computes the estimated number of prizes per draw given the number of tiers and the grand prize period.
/// @param _numberOfTiers The number of tiers
/// @param _grandPrizePeriod The grand prize period
/// @return The estimated number of prizes per draw
function estimatedClaimCount(
uint8 _numberOfTiers,
uint16 _grandPrizePeriod
) internal pure returns (uint32) {
uint32 count = 0;
for (uint8 i = 0; i < _numberOfTiers; i++) {
count += uint32(
uint256(
unwrap(sd(int256(prizeCount(i))).mul(getTierOdds(i, _numberOfTiers, _grandPrizePeriod)))
)
);
}
return count;
}
}
32 changes: 16 additions & 16 deletions src/libraries/UD34x4.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,27 @@ uint128 constant uUNIT = 1e4;
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_UD2x18`.
function intoUD60x18(UD34x4 x) pure returns (UD60x18 result) {
uint256 xUint = uint256(UD34x4.unwrap(x)) * uint256(1e14);
result = UD60x18.wrap(xUint);
uint256 xUint = uint256(UD34x4.unwrap(x)) * uint256(1e14);
result = UD60x18.wrap(xUint);
}

/// @notice Casts an UD34x4 number into UD60x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_UD2x18`.
function fromUD60x18(UD60x18 x) pure returns (UD34x4 result) {
uint256 xUint = UD60x18.unwrap(x) / 1e14;
if (xUint > uMAX_UD34x4) {
revert PRBMath_UD34x4_fromUD60x18_Convert_Overflow(x.unwrap());
}
result = UD34x4.wrap(uint128(xUint));
uint256 xUint = UD60x18.unwrap(x) / 1e14;
if (xUint > uMAX_UD34x4) {
revert PRBMath_UD34x4_fromUD60x18_Convert_Overflow(x.unwrap());
}
result = UD34x4.wrap(uint128(xUint));
}

/// @notice Converts an UD34x4 number to a simple integer by dividing it by `UNIT`. Rounds towards zero in the process.
/// @dev Rounds down in the process.
/// @param x The UD34x4 number to convert.
/// @return result The same number in basic integer form.
function convert(UD34x4 x) pure returns (uint128 result) {
result = UD34x4.unwrap(x) / uUNIT;
result = UD34x4.unwrap(x) / uUNIT;
}

/// @notice Converts a simple integer to UD34x4 by multiplying it by `UNIT`.
Expand All @@ -50,22 +50,22 @@ function convert(UD34x4 x) pure returns (uint128 result) {
/// @param x The basic integer to convert.
/// @param result The same number converted to UD34x4.
function convert(uint128 x) pure returns (UD34x4 result) {
if (x > uMAX_UD34x4 / uUNIT) {
revert PRBMath_UD34x4_Convert_Overflow(x);
}
unchecked {
result = UD34x4.wrap(x * uUNIT);
}
if (x > uMAX_UD34x4 / uUNIT) {
revert PRBMath_UD34x4_Convert_Overflow(x);
}
unchecked {
result = UD34x4.wrap(x * uUNIT);
}
}

/// @notice Alias for the `convert` function defined above.
/// @dev Here for backward compatibility. Will be removed in V4.
function fromUD34x4(UD34x4 x) pure returns (uint128 result) {
result = convert(x);
result = convert(x);
}

/// @notice Alias for the `convert` function defined above.
/// @dev Here for backward compatibility. Will be removed in V4.
function toUD34x4(uint128 x) pure returns (UD34x4 result) {
result = convert(x);
result = convert(x);
}
Loading

0 comments on commit 2b4cd6b

Please sign in to comment.