Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limited tier changes to one per draw #83

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/PrizePool.sol
Original file line number Diff line number Diff line change
@@ -838,7 +838,15 @@ contract PrizePool is TieredLiquidityDistributor, Ownable {
/// @return The estimated number of tiers + the canary tier
function _computeNextNumberOfTiers(uint32 _claimCount) internal view returns (uint8) {
// claimCount is expected to be the estimated number of claims for the current prize tier.
return _estimateNumberOfTiersUsingPrizeCountPerDraw(_claimCount) + 1;
uint8 nextNumberOfTiers = _estimateNumberOfTiersUsingPrizeCountPerDraw(_claimCount) + 1;
// limit change to 1 tier
uint8 _numTiers = numberOfTiers;
if (nextNumberOfTiers > _numTiers) {
nextNumberOfTiers = _numTiers + 1;
} else if (nextNumberOfTiers < _numTiers) {
nextNumberOfTiers = _numTiers - 1;
}
return nextNumberOfTiers;
}

/// @notice Calculates the number of tiers given the number of prize claims
26 changes: 15 additions & 11 deletions src/abstract/TieredLiquidityDistributor.sol
Original file line number Diff line number Diff line change
@@ -250,7 +250,6 @@ contract TieredLiquidityDistributor {
// need to redistribute to the canary tier and any new tiers (if expanding)
uint8 start;
uint8 end;
uint8 shares = tierShares;
// if we are shrinking, we need to reclaim including the new canary tier
if (_nextNumberOfTiers < _numberOfTiers) {
start = _nextNumberOfTiers - 1;
@@ -263,7 +262,6 @@ contract TieredLiquidityDistributor {
for (uint8 i = start; i < end; i++) {
reclaimedLiquidity = reclaimedLiquidity.add(
_getTierRemainingLiquidity(
shares,
fromUD34x4toUD60x18(_tiers[i].prizeTokenPerShare),
_currentPrizeTokenPerShare
)
@@ -350,7 +348,6 @@ contract TieredLiquidityDistributor {
uint104 remainingLiquidity = SafeCast.toUint104(
convert(
_getTierRemainingLiquidity(
_shares,
fromUD34x4toUD60x18(_tierStruct.prizeTokenPerShare),
fromUD34x4toUD60x18(prizeTokenPerShare)
)
@@ -443,35 +440,42 @@ contract TieredLiquidityDistributor {
/// @notice Computes the remaining liquidity available to a tier.
/// @param _tier The tier to compute the liquidity for
/// @return The remaining liquidity
function getTierRemainingLiquidity(uint8 _tier) external view returns (uint256) {
uint8 _numTiers = numberOfTiers;
function getTierRemainingLiquidity(uint8 _tier) public view returns (uint256) {
return _getTierRemainingLiquidity(_tier, fromUD34x4toUD60x18(prizeTokenPerShare));
}

/// @notice Computes the remaining liquidity available to a tier.
/// @param _tier The tier to compute the liquidity for
/// @param _prizeTokenPerShare The global prizeTokenPerShare
/// @return The remaining liquidity
function _getTierRemainingLiquidity(
uint8 _tier,
UD60x18 _prizeTokenPerShare
) internal view returns (uint256) {
uint8 _numTiers = numberOfTiers;
return
!TierCalculationLib.isValidTier(_tier, _numTiers)
? 0
: convert(
_getTierRemainingLiquidity(
tierShares,
fromUD34x4toUD60x18(_getTier(_tier, _numTiers).prizeTokenPerShare),
fromUD34x4toUD60x18(prizeTokenPerShare)
_prizeTokenPerShare
)
);
}

/// @notice Computes the remaining tier liquidity.
/// @param _shares The number of shares that the tier has (can be tierShares or canaryShares)
/// @param _tierPrizeTokenPerShare The prizeTokenPerShare of the Tier struct
/// @param _prizeTokenPerShare The global prizeTokenPerShare
/// @return The remaining available liquidity
function _getTierRemainingLiquidity(
uint256 _shares,
UD60x18 _tierPrizeTokenPerShare,
UD60x18 _prizeTokenPerShare
) internal pure returns (UD60x18) {
) internal view returns (UD60x18) {
return
_tierPrizeTokenPerShare.gte(_prizeTokenPerShare)
? ud(0)
: _prizeTokenPerShare.sub(_tierPrizeTokenPerShare).mul(convert(_shares));
: _prizeTokenPerShare.sub(_tierPrizeTokenPerShare).mul(convert(tierShares));
}

/// @notice Estimates the number of prizes for the current number of tiers, including the canary tier
40 changes: 28 additions & 12 deletions test/PrizePool.t.sol
Original file line number Diff line number Diff line change
@@ -625,8 +625,13 @@ contract PrizePoolTest is Test {

contribute(510e18);

assertEq(prizePool.estimateNextNumberOfTiers(), 3, "will reduce to 3");
assertEq(prizePool.estimateNextNumberOfTiers(), 4, "will reduce to 4");

// first draw (same num tiers)
// total for first draw = 0.1 * 510e18 = 51e18
// 5th tier (canary tier) = (100/510)*51e18 = 10e18
// 4th tier (daily tier) = (100/510)*51.18 = 10e18
// reserve = 51e18 * (10 / 510) = 1e18
awardDraw(1234);

assertEq(prizePool.reserve(), 1e18, "reserve after first draw");
@@ -637,24 +642,28 @@ contract PrizePoolTest is Test {
2,
4567,
startingTiers,
3,
3448387096774193470 /*reserve from output*/,
UD34x4.wrap(3448387096774193330000) /*prize tokens per share from output*/,
4, // change is limited to 1 tier
2607317073170731770 /*reserve from output*/,
UD34x4.wrap(2607317073170731540000) /*prize tokens per share from output*/,
firstDrawOpensAt + drawPeriodSeconds
);

// award second draw
// reclaimed tiers: 10e18 + 10e18 = 20e18
// liquidity for second draw = 0.1 * (510e18 - 51e18) + 20e18 = 65.9e18
// reserve for second draw = 65.9e18 * (10 / 410) = 1607317073170731800
// total reserve = 1e18 + 1607317073170731800 = 2607317073170732000
awardDraw(4567);

assertEq(prizePool.numberOfTiers(), 3, "number of tiers");
assertEq(prizePool.numberOfTiers(), 4, "number of tiers");

// two tiers + canary tier = 30e18
// total liquidity for second draw is 45.9e18
// new liquidity for second draw = 75.9e18
// reserve for second draw = (10/310)*75.9e18 = 2.445e18
// total reserve = 3.445e18

assertEq(prizePool.reserve(), 3.44838709677419347e18, "size of reserve");
assertEq(prizePool.reserve(), 2607317073170731770, "size of reserve");
}

function testAwardDraw_expandingTiers() public {
@@ -1003,19 +1012,26 @@ contract PrizePoolTest is Test {
assertEq(prizePool.computeNextNumberOfTiers(24), 4);
}

function testComputeNextNumberOfTiers_beyondMinimum() public {
function testComputeNextNumberOfTiers_beyondMinimum_maxIncreaseBy1() public {
// canary prizes were taken for tier 4!
assertEq(prizePool.computeNextNumberOfTiers(80), 5);
// should crank up to 5, but limit increasee to 1
assertEq(prizePool.computeNextNumberOfTiers(80), 4);
}

function testComputeNextNumberOfTiers_beyondMinimum_bigDeviation() public {
function testComputeNextNumberOfTiers_beyondMinimum_bigDeviation_maxIncreaseBy1() public {
// half the canary prizes were taken
assertEq(prizePool.computeNextNumberOfTiers(150), 5);
assertEq(prizePool.computeNextNumberOfTiers(150), 4);
}

function testComputeNextNumberOfTiers_beyondMinimum_nextLevelUp() public {
function testComputeNextNumberOfTiers_beyondMinimum_nextLevelUp_maxIncreaseBy1() public {
// half the canary prizes were taken
assertEq(prizePool.computeNextNumberOfTiers(200), 6);
assertEq(prizePool.computeNextNumberOfTiers(200), 4);
}

function testComputeNextNumberOfTiers_drop_maxDecreaseBy1() public {
params.numberOfTiers = 5;
prizePool = new PrizePool(params);
assertEq(prizePool.computeNextNumberOfTiers(0), 4);
}

function testClaimPrize_secondTier_claimTwice() public {
44 changes: 44 additions & 0 deletions test/abstract/TieredLiquidityDistributor.t.sol
Original file line number Diff line number Diff line change
@@ -34,6 +34,36 @@ contract TieredLiquidityDistributorTest is Test {
distributor.awardDraw(1, 100);
}

function testAwardDraw_liquidity_shrinkTiers1() public {
distributor.awardDraw(5, 100e18);
distributor.awardDraw(4, 100e18);
assertEq(_computeLiquidity(), 200e18);
}

function testAwardDraw_liquidity_shrinkTiers2() public {
distributor.awardDraw(5, 100e18);
distributor.awardDraw(3, 100e18);
assertEq(_computeLiquidity(), 200e18);
}

function testAwardDraw_liquidity_sameTiers() public {
distributor.awardDraw(5, 100e18);
distributor.awardDraw(5, 100e18);
assertEq(_computeLiquidity(), 200e18);
}

function testAwardDraw_liquidity_growTiers1() public {
distributor.awardDraw(5, 100e18);
distributor.awardDraw(6, 100e18);
assertEq(_computeLiquidity(), 200e18);
}

function testAwardDraw_liquidity_growTiers2() public {
distributor.awardDraw(5, 100e18);
distributor.awardDraw(7, 100e18);
assertEq(_computeLiquidity(), 200e18);
}

function testConstructor_numberOfTiersTooLarge() public {
vm.expectRevert(abi.encodeWithSelector(NumberOfTiersGreaterThanMaximum.selector, 16));
new TieredLiquidityDistributorWrapper(16, tierShares, reserveShares, 365);
@@ -295,4 +325,18 @@ contract TieredLiquidityDistributorTest is Test {
summed += distributor.reserve();
return summed;
}

function _computeLiquidity() internal view returns (uint256) {
uint256 liquidity = _getTierLiquidity(distributor.numberOfTiers(), fromUD34x4toUD60x18(distributor.prizeTokenPerShare()));
liquidity += distributor.reserve();
return liquidity;
}

function _getTierLiquidity(uint8 _numberOfTiers, UD60x18 _prizeTokenPerShare) internal view returns (uint256) {
uint256 liquidity = 0;
for (uint8 i = 0; i < _numberOfTiers; i++) {
liquidity += distributor.getTierRemainingLiquidity(i, _prizeTokenPerShare);
}
return liquidity;
}
}
45 changes: 33 additions & 12 deletions test/abstract/helper/TieredLiquidityDistributorWrapper.sol
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ pragma solidity ^0.8.19;
import "forge-std/console2.sol";

import { TieredLiquidityDistributor, Tier, fromUD34x4toUD60x18, convert } from "../../../src/abstract/TieredLiquidityDistributor.sol";
import { UD60x18 } from "prb-math/UD60x18.sol";

contract TieredLiquidityDistributorWrapper is TieredLiquidityDistributor {
constructor(
@@ -25,18 +26,33 @@ contract TieredLiquidityDistributorWrapper is TieredLiquidityDistributor {
}

function remainingTierLiquidity(uint8 _tier) external view returns (uint112) {
uint8 shares = tierShares;
Tier memory tier = _getTier(_tier, numberOfTiers);
return
uint112(
convert(
_getTierRemainingLiquidity(
shares,
fromUD34x4toUD60x18(tier.prizeTokenPerShare),
fromUD34x4toUD60x18(prizeTokenPerShare)
)
)
);
return uint112(getTierRemainingLiquidity(_tier));
// uint8 shares = tierShares;
// Tier memory tier = _getTier(_tier, numberOfTiers);
// return
// uint112(
// convert(
// _getTierRemainingLiquidity(
// fromUD34x4toUD60x18(tier.prizeTokenPerShare),
// fromUD34x4toUD60x18(prizeTokenPerShare)
// )
// )
// );
}

function computeNewDistributions(
uint8 _numberOfTiers,
uint8 _nextNumberOfTiers,
UD60x18 _currentPrizeTokenPerShare,
uint256 _prizeTokenLiquidity
) external view returns (uint96, UD60x18) {
(uint96 newReserve, UD60x18 newPrizeTokenPerShare) = _computeNewDistributions(
_numberOfTiers,
_nextNumberOfTiers,
_currentPrizeTokenPerShare,
_prizeTokenLiquidity
);
return (newReserve, newPrizeTokenPerShare);
}

function estimateNumberOfTiersUsingPrizeCountPerDraw(
@@ -50,4 +66,9 @@ contract TieredLiquidityDistributorWrapper is TieredLiquidityDistributor {
uint32 result = _sumTierPrizeCounts(_numTiers);
return result;
}

function getTierRemainingLiquidity(uint8 _tier, UD60x18 _prizeTokenPerShare) external view returns (uint256) {
uint256 result = _getTierRemainingLiquidity(_tier, _prizeTokenPerShare);
return result;
}
}
Original file line number Diff line number Diff line change
@@ -27,7 +27,6 @@ contract TieredLiquidityDistributorFuzzHarness is TieredLiquidityDistributor {
Tier memory tier = _getTier(i, numberOfTiers);
availableLiquidity += convert(
_getTierRemainingLiquidity(
tierShares,
fromUD34x4toUD60x18(tier.prizeTokenPerShare),
fromUD34x4toUD60x18(prizeTokenPerShare)
)
@@ -43,11 +42,9 @@ contract TieredLiquidityDistributorFuzzHarness is TieredLiquidityDistributor {
uint8 tier = _tier % numberOfTiers;

Tier memory tier_ = _getTier(tier, numberOfTiers);
uint8 shares = tierShares;
uint104 liq = uint104(
convert(
_getTierRemainingLiquidity(
shares,
fromUD34x4toUD60x18(tier_.prizeTokenPerShare),
fromUD34x4toUD60x18(prizeTokenPerShare)
)