Skip to content

Commit

Permalink
Merge pull request #34 from GenerationSoftware/gen-1834-handle-dust-s…
Browse files Browse the repository at this point in the history
…cenario-in-claimer-target-price

Handle dust scenario in claimer target price
  • Loading branch information
trmid authored Jul 12, 2024
2 parents 52299ab + 21a9ed6 commit 20f52a6
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 16 deletions.
9 changes: 3 additions & 6 deletions src/Claimer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -227,20 +227,17 @@ contract Claimer is ReentrancyGuard {
// scope to avoid stack too deep error
SD59x18 decayConstant;
{
// the fee should never need to go beyond the full daily prize size under normal operating conditions
uint256 highFee = prizePool.getTierPrizeSize(numberOfTiers - 3);

// handle the case where the target fee is zero, high fee is zero, or high fee is less than the target fee
if (targetFee == 0 || highFee < targetFee) {
// handle the case where the target fee is zero, or max fee is less than the target fee
if (targetFee == 0 || maxFee < targetFee) {

// we fall back to a tier-specific ramp up from 1% of the max fee to 100% of the max fee
targetFee = maxFee / 100;
highFee = maxFee;
}
decayConstant = LinearVRGDALib.getDecayConstant(
LinearVRGDALib.getMaximumPriceDeltaScale(
targetFee,
highFee,
maxFee,
timeToReachMaxFee
)
);
Expand Down
54 changes: 44 additions & 10 deletions test/Claimer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ contract ClaimerTest is Test {
uint256 public PRIZE_SIZE_C2 = 0.0001e18;
uint256 public TIME_TO_REACH_MAX = 86400;
uint256 public ESTIMATED_PRIZES = 1000;
uint256 public NO_SALES_100_SECONDS_BEHIND_SCHEDULE_FEE = 100243095112994;
uint256 public SOLD_ONE_100_SECONDS_IN_FEE = 98708714827462;
uint256 public NO_SALES_100_SECONDS_BEHIND_SCHEDULE_FEE = 100232158536309;
uint256 public SOLD_ONE_100_SECONDS_IN_FEE = 98766381570607;
uint64 public MAX_FEE_PERCENTAGE_OF_PRIZE = 0.5e18;

Claimer public claimer;
Expand Down Expand Up @@ -100,7 +100,7 @@ contract ClaimerTest is Test {

vm.mockCallRevert(
address(vault),
abi.encodeCall(vault.claimPrize, (winner1, 1, 0, 100243095112994, address(this))),
abi.encodeCall(vault.claimPrize, (winner1, 1, 0, 100232158536309, address(this))),
"errrooooor"
);

Expand Down Expand Up @@ -236,7 +236,7 @@ contract ClaimerTest is Test {
function testClaimPrizes_maxFee() public {
address[] memory winners = newWinners(winner1);
uint32[][] memory prizeIndices = newPrizeIndices(1, 1);
mockPrizePool(1, -1 * int256((99 * TIME_TO_REACH_MAX) / 100), 0); // much time has passed, meaning the fee is large
mockPrizePool(1, -1 * int256((101 * TIME_TO_REACH_MAX) / 100), 0); // much time has passed, meaning the fee has reached the max
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, PRIZE_SIZE_DAILY / 2, "Total fees");
Expand Down Expand Up @@ -279,15 +279,16 @@ contract ClaimerTest is Test {

function testComputeTotalFees_one() public {
mockPrizePool(1, -100, 0);
assertEq(claimer.computeTotalFees(1, 1), NO_SALES_100_SECONDS_BEHIND_SCHEDULE_FEE);
assertApproxEqRel(claimer.computeTotalFees(1, 1), NO_SALES_100_SECONDS_BEHIND_SCHEDULE_FEE, 0.01e18); // 1% margin for error
}

function testComputeTotalFees_two() public {
mockPrizePool(1, -100, 0);
uint totalFees = claimer.computeTotalFees(1, 2);
assertEq(
assertApproxEqRel(
totalFees,
NO_SALES_100_SECONDS_BEHIND_SCHEDULE_FEE + SOLD_ONE_100_SECONDS_IN_FEE
NO_SALES_100_SECONDS_BEHIND_SCHEDULE_FEE + SOLD_ONE_100_SECONDS_IN_FEE,
0.01e18 // 1% margin for error
);
}

Expand All @@ -298,12 +299,12 @@ contract ClaimerTest is Test {

function testComputeTotalFeesAlreadyClaimed_one() public {
mockPrizePool(1, -100, 0);
assertEq(claimer.computeTotalFees(1, 1, 10), 85914163796254);
assertApproxEqRel(claimer.computeTotalFees(1, 1, 10), 85914163796254, 0.01e18); // 1% margin for error
}

function testComputeTotalFeesAlreadyClaimed_two() public {
mockPrizePool(1, -100, 0);
assertEq(claimer.computeTotalFees(1, 2, 10), 170513274430708);
assertApproxEqRel(claimer.computeTotalFees(1, 2, 10), 170513274430708, 0.01e18); // 1% margin for error
}

function testComputeTotalFees_canary() public {
Expand Down Expand Up @@ -365,7 +366,40 @@ contract ClaimerTest is Test {
uint firstSaleTime = TIME_TO_REACH_MAX / ESTIMATED_PRIZES;

vm.warp(startTime + firstSaleTime + TIME_TO_REACH_MAX + 1);
assertApproxEqRel(claimer.computeFeePerClaim(0, 1), PRIZE_SIZE_DAILY, 0.02e18);
assertApproxEqRel(claimer.computeFeePerClaim(0, 1), (PRIZE_SIZE_GP * MAX_FEE_PERCENTAGE_OF_PRIZE) / 1e18, 0.02e18);
}

function testComputeFeePerClaim_reasonableFeeRampForGp() public {
uint startTime = block.timestamp;

mockPrizePool(1, -600, 0); // 10 min have passed
vm.warp(startTime + 600);
assertLt(claimer.computeFeePerClaim(0, 1), PRIZE_SIZE_C2 * 2); // less than twice the second canary

mockPrizePool(1, -int256(TIME_TO_REACH_MAX / 6), 0); // 1/6th of the time to reach max has passed
vm.warp(startTime + TIME_TO_REACH_MAX / 6);
assertLt(claimer.computeFeePerClaim(0, 1), PRIZE_SIZE_GP / 100); // less than 1% of GP
}

function testComputeFeePerClaim_reasonableFeeRampForGp_whenCanaryIsDust() public {
uint startTime = block.timestamp;

// mock 1 wei for all other prizes
mockGetTierPrizeSize(1, 1);
mockGetTierPrizeSize(2, 1);
mockGetTierPrizeSize(3, 1);

mockPrizePool(1, -600, 0); // 10 min have passed
vm.warp(startTime + 600);
assertLt(claimer.computeFeePerClaim(0, 1), PRIZE_SIZE_GP / 1000); // less than 0.1% of GP

mockPrizePool(1, -int256(TIME_TO_REACH_MAX / 6), 0); // 1/6th of the time to reach max has passed
vm.warp(startTime + TIME_TO_REACH_MAX / 6);
assertLt(claimer.computeFeePerClaim(0, 1), PRIZE_SIZE_GP / 100); // less than 1% of GP

mockPrizePool(1, -int256(TIME_TO_REACH_MAX / 2), 0); // 1/2 of the time to reach max has passed
vm.warp(startTime + TIME_TO_REACH_MAX / 2);
assertGt(claimer.computeFeePerClaim(0, 1), 1e14); // grater than some expected L2 gas cost
}

function mockPrizePool(uint256 drawId, int256 drawEndedRelativeToNow, uint256 claimCount) public {
Expand Down

0 comments on commit 20f52a6

Please sign in to comment.