Skip to content

Commit

Permalink
Merge pull request #31 from GenerationSoftware/gen-1289-update-claime…
Browse files Browse the repository at this point in the history
…r-to-use-daily-prize-tier-as-max-fee-param

Tighten dynamic claim fee range
  • Loading branch information
trmid committed Apr 10, 2024
2 parents 1238ec3 + 1d77755 commit 1d277ab
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 42 deletions.
2 changes: 1 addition & 1 deletion lib/prb-math
Submodule prb-math updated 42 files
+0 −9 .github/ISSUE_TEMPLATE/1-bug-report.yml
+27 −3 .github/workflows/ci.yml
+0 −8 .gitmodules
+22 −0 CHANGELOG.md
+16 −20 README.md
+0 −1 foundry.toml
+0 −1 lib/forge-std
+0 −1 lib/prb-test
+6 −6 package.json
+314 −37 pnpm-lock.yaml
+2 −2 remappings.txt
+1 −1 src/Common.sol
+1 −1 src/sd59x18/Errors.sol
+0 −5 src/test/Assertions.sol
+0 −5 src/test/Utils.sol
+0 −1 src/ud2x18/Casting.sol
+1 −1 src/ud60x18/Math.sol
+2 −9 test/Base.t.sol
+0 −1 test/fuzz/sd1x18/casting/Casting.t.sol
+0 −2 test/fuzz/sd59x18/casting/Casting.t.sol
+0 −3 test/fuzz/sd59x18/helpers/Helpers.t.sol
+1 −2 test/fuzz/ud60x18/helpers/Helpers.t.sol
+2 −3 test/unit/sd59x18/SD59x18.t.sol
+0 −1 test/unit/sd59x18/conversion/convert-from/convertFrom.t.sol
+1 −1 test/unit/sd59x18/math/div/div.t.sol
+0 −1 test/unit/sd59x18/math/frac/frac.t.sol
+2 −2 test/unit/sd59x18/math/inv/inv.t.sol
+0 −2 test/unit/sd59x18/math/mul/mul.t.sol
+0 −1 test/unit/sd59x18/math/pow/pow.t.sol
+1 −1 test/unit/sd59x18/math/sqrt/sqrt.t.sol
+2 −3 test/unit/ud60x18/UD60x18.t.sol
+0 −1 test/unit/ud60x18/conversion/convert-from/convertFrom.t.sol
+1 −1 test/unit/ud60x18/math/div/div.t.sol
+1 −1 test/unit/ud60x18/math/gm/gm.t.sol
+1 −1 test/unit/ud60x18/math/inv/inv.t.sol
+0 −2 test/unit/ud60x18/math/ln/ln.t.sol
+0 −2 test/unit/ud60x18/math/log10/log10.t.sol
+0 −2 test/unit/ud60x18/math/log2/log2.t.sol
+0 −2 test/unit/ud60x18/math/mul/mul.t.sol
+0 −1 test/unit/ud60x18/math/pow/pow.t.sol
+1 −1 test/utils/Assertions.sol
+65 −65 test/utils/Utils.sol
2 changes: 1 addition & 1 deletion lib/pt-v5-claimable-interface
23 changes: 14 additions & 9 deletions src/Claimer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,9 @@ contract Claimer {
if (prizePool.isCanaryTier(_tier)) {
return prizePool.getTierPrizeSize(_tier);
}
uint256 targetFee = _computeFeeTarget();
SD59x18 decayConstant = _computeDecayConstant(targetFee);
uint8 numberOfTiers = prizePool.numberOfTiers();
uint256 targetFee = _computeFeeTarget(numberOfTiers);
SD59x18 decayConstant = _computeDecayConstant(targetFee, numberOfTiers);
uint256 _maxFee = _computeMaxFee(_tier);
SD59x18 perTimeUnit = LinearVRGDALib.getPerTimeUnit(
prizePool.estimatedPrizeCountWithBothCanaries(),
Expand Down Expand Up @@ -252,19 +253,23 @@ contract Claimer {
}

/// @notice Compute the target fee for prize claims
/// @param _numberOfTiers The current number of tiers for the prize pool
/// @return The target fee for prize claims
function _computeFeeTarget() internal view returns (uint256) {
uint8 numberOfTiers = prizePool.numberOfTiers();
// we expect the fee to match the second canary tier
return prizePool.getTierPrizeSize(numberOfTiers - 2);
function _computeFeeTarget(uint8 _numberOfTiers) internal view returns (uint256) {
// we expect the fee to be somewhere between the first and second canary tier prize sizes,
// so we set it to the lower of the two.
return prizePool.getTierPrizeSize(_numberOfTiers - 1);
}

/// @notice Computes the decay constant for the VRGDA.
/// @dev This is a decay constant that ensures the fee will grow from the target to the max fee within the time frame
/// @param _targetFee The target fee
/// @param _targetFee The target fee
/// @param _numberOfTiers The current number of tiers for the prize pool
/// @return The decay constant
function _computeDecayConstant(uint256 _targetFee) internal view returns (SD59x18) {
uint maximumFee = _computeMaxFee(0);
function _computeDecayConstant(uint256 _targetFee, uint8 _numberOfTiers) internal view returns (SD59x18) {
// the max fee should never need to go beyond the full daily prize size under normal operating
// conditions.
uint maximumFee = prizePool.getTierPrizeSize(_numberOfTiers - 3);
return LinearVRGDALib.getDecayConstant(
LinearVRGDALib.getMaximumPriceDeltaScale(
_targetFee,
Expand Down
47 changes: 20 additions & 27 deletions test/Claimer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ contract ClaimerTest is Test {
bytes reason
);

uint256 public MINIMUM_FEE = 0.0001e18;
uint256 public MAXIMUM_FEE = 1000e18;
uint256 public PRIZE_SIZE_GP = 1000000e18;
uint256 public PRIZE_SIZE_DAILY = 500e18;
uint256 public PRIZE_SIZE_C1 = 0.01e18;
uint256 public PRIZE_SIZE_C2 = 0.0001e18;
uint256 public TIME_TO_REACH_MAX = 86400;
uint256 public ESTIMATED_PRIZES = 1000;
uint256 public SMALLEST_PRIZE_SIZE = 1e18;
uint256 public CANARY_PRIZE_SIZE = 0.4e18;
uint256 public NO_SALES_100_SECONDS_BEHIND_SCHEDULE_FEE = 100243095112994;
uint256 public SOLD_ONE_100_SECONDS_IN_FEE = 98708714827462;
uint64 public MAX_FEE_PERCENTAGE_OF_PRIZE = 0.5e18;
Expand Down Expand Up @@ -63,6 +63,11 @@ contract ClaimerTest is Test {
mockIsCanaryTier(1, false);
mockIsCanaryTier(2, true);
mockIsCanaryTier(3, true);

mockGetTierPrizeSize(0, PRIZE_SIZE_GP);
mockGetTierPrizeSize(1, PRIZE_SIZE_DAILY);
mockGetTierPrizeSize(2, PRIZE_SIZE_C1);
mockGetTierPrizeSize(3, PRIZE_SIZE_C2);
}

function testConstructor() public {
Expand Down Expand Up @@ -204,21 +209,19 @@ contract ClaimerTest is Test {
function testClaimPrizes_maxFee() public {
address[] memory winners = newWinners(winner1);
uint32[][] memory prizeIndices = newPrizeIndices(1, 1);
mockPrizePool(1, -1, 0);
mockLastClosedDrawAwardedAt(-80000); // much time has passed, meaning the fee is large
mockClaimPrize(1, winner1, 0, uint96(0.5e18), address(this), 100);
mockPrizePool(1, -1 * int256((99 * TIME_TO_REACH_MAX) / 100), 0); // much time has passed, meaning the fee is large
mockClaimPrize(1, winner1, 0, uint96(PRIZE_SIZE_DAILY / 2), address(this), PRIZE_SIZE_DAILY);
uint256 totalFees = claimer.claimPrizes(vault, 1, winners, prizeIndices, address(this), 0);
assertEq(totalFees, 0.5e18, "Total fees");
assertEq(totalFees, PRIZE_SIZE_DAILY / 2, "Total fees");
}

function testClaimPrizes_veryLongElapsedTime() public {
address[] memory winners = newWinners(winner1);
uint32[][] memory prizeIndices = newPrizeIndices(1, 1);
mockPrizePool(1, -1, 0);
mockLastClosedDrawAwardedAt(-1_000_000); // a long time has passed, meaning the fee should be capped (and there should be no EXP_OVERFLOW!)
mockClaimPrize(1, winner1, 0, uint96(0.5e18), address(this), 100);
mockPrizePool(1, -1_000_000, 0);// a long time has passed, meaning the fee should be capped (and there should be no EXP_OVERFLOW!)
mockClaimPrize(1, winner1, 0, uint96(PRIZE_SIZE_DAILY / 2), address(this), PRIZE_SIZE_DAILY);
uint256 totalFees = claimer.claimPrizes(vault, 1, winners, prizeIndices, address(this), 0);
assertEq(totalFees, 0.5e18, "Total fees");
assertEq(totalFees, PRIZE_SIZE_DAILY / 2, "Total fees");
}

function testClaimPrizes_arrayMismatchGt() public {
Expand Down Expand Up @@ -277,26 +280,20 @@ contract ClaimerTest is Test {
}

function testComputeTotalFees_canary() public {
mockIsCanaryTier(1, true);
mockGetTierPrizeSize(1, 100e18);
vm.mockCall(
address(prizePool),
abi.encodeWithSelector(prizePool.claimCount.selector),
abi.encode(0)
);
assertEq(claimer.computeTotalFees(1, 1), 100e18);
assertEq(claimer.computeTotalFees(2, 1), PRIZE_SIZE_C1);
}

function testComputeMaxFee_normalPrizes() public {
mockGetTierPrizeSize(0, 10e18);
mockGetTierPrizeSize(1, SMALLEST_PRIZE_SIZE);
assertEq(claimer.computeMaxFee(0), 5e18);
assertEq(claimer.computeMaxFee(1), 0.5e18);
assertEq(claimer.computeMaxFee(0), PRIZE_SIZE_GP / 2);
assertEq(claimer.computeMaxFee(1), PRIZE_SIZE_DAILY / 2);
}

function testComputeMaxFee_canaryPrizes() public {
mockGetTierPrizeSize(2, 0.5e18);
mockGetTierPrizeSize(3, 1e18);
assertEq(claimer.computeMaxFee(2), type(uint256).max); // full size
assertEq(claimer.computeMaxFee(3), type(uint256).max); // full size
}
Expand All @@ -312,7 +309,7 @@ contract ClaimerTest is Test {
ud2x18(MAX_FEE_PERCENTAGE_OF_PRIZE)
);
mockPrizePool(1, -int(firstSaleTime), 0);
assertApproxEqAbs(claimer.computeFeePerClaim(0, 1), MINIMUM_FEE, 4);
assertApproxEqAbs(claimer.computeFeePerClaim(0, 1), PRIZE_SIZE_C2, 4);
}

function testComputeFeePerClaim_maxFee() public {
Expand All @@ -322,11 +319,10 @@ contract ClaimerTest is Test {
uint firstSaleTime = TIME_TO_REACH_MAX / ESTIMATED_PRIZES;

vm.warp(startTime + firstSaleTime + TIME_TO_REACH_MAX + 1);
assertApproxEqAbs(claimer.computeFeePerClaim(0, 1), MAXIMUM_FEE/2 /* 50% */, 4);
assertApproxEqRel(claimer.computeFeePerClaim(0, 1), PRIZE_SIZE_DAILY, 0.02e18);
}

function mockPrizePool(uint256 drawId, int256 drawEndedRelativeToNow, uint256 claimCount) public {
uint numberOfTiers = 3;
vm.mockCall(
address(prizePool),
abi.encodeWithSignature("getLastClosedDrawId()"),
Expand All @@ -342,9 +338,6 @@ contract ClaimerTest is Test {
abi.encodeWithSignature("estimatedPrizeCountWithBothCanaries()"),
abi.encodePacked(ESTIMATED_PRIZES)
);
mockGetTierPrizeSize(1, SMALLEST_PRIZE_SIZE);
mockGetTierPrizeSize(2, MINIMUM_FEE);
mockGetTierPrizeSize(0, MAXIMUM_FEE);
mockLastClosedDrawAwardedAt(drawEndedRelativeToNow);
vm.mockCall(
address(prizePool),
Expand Down

0 comments on commit 1d277ab

Please sign in to comment.