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

Auctions allocate fees instead of withdraw reserve #69

Merged
Merged
Show file tree
Hide file tree
Changes from 3 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
53 changes: 27 additions & 26 deletions src/PrizePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,10 @@ contract PrizePool is TieredLiquidityDistributor, Ownable {
uint48 drawStartedAt
);

/// @notice Emitted when any amount of the reserve is withdrawn.
/// @param to The address the assets are transferred to
/// @param amount The amount of assets transferred
event WithdrawReserve(address indexed to, uint256 amount);
/// @notice Emitted when any amount of the reserve is rewarded to a recipient.
/// @param to The recipient of the reward
/// @param amount The amount of assets rewarded
event AllocateRewardFromReserve(address indexed to, uint256 amount);

/// @notice Emitted when the reserve is manually increased.
/// @param user The user who increased the reserve
Expand All @@ -182,10 +182,11 @@ contract PrizePool is TieredLiquidityDistributor, Ownable {
event ContributePrizeTokens(address indexed vault, uint24 indexed drawId, uint256 amount);

/// @notice Emitted when an address withdraws their prize claim rewards.
/// @param account The account that is withdrawing rewards
/// @param to The address the rewards are sent to
/// @param amount The amount withdrawn
/// @param available The total amount that was available to withdraw before the transfer
event WithdrawClaimRewards(address indexed to, uint256 amount, uint256 available);
event WithdrawRewards(address indexed account, address indexed to, uint256 amount, uint256 available);

/// @notice Emitted when an address receives new prize claim rewards.
/// @param to The address the rewards are given to
Expand All @@ -205,8 +206,8 @@ contract PrizePool is TieredLiquidityDistributor, Ownable {
mapping(address vault => mapping(address account => mapping(uint24 drawId => mapping(uint8 tier => mapping(uint32 prizeIndex => bool claimed)))))
internal _claimedPrizes;

/// @notice Tracks the total fees accrued to each claimer.
mapping(address claimer => uint256 rewards) internal _claimerRewards;
/// @notice Tracks the total rewards accrued for a claimer or draw completer.
mapping(address recipient => uint256 rewards) internal _rewards;

/// @notice The degree of POOL contribution smoothing. 0 = no smoothing, ~1 = max smoothing.
/// @dev Smoothing spreads out vault contribution over multiple draws; the higher the smoothing the more draws.
Expand Down Expand Up @@ -335,10 +336,10 @@ contract PrizePool is TieredLiquidityDistributor, Ownable {
return _deltaBalance;
}

/// @notice Allows the Manager to withdraw tokens from the reserve.
/// @param _to The address to send the tokens to
/// @param _amount The amount of tokens to withdraw
function withdrawReserve(address _to, uint96 _amount) external onlyDrawManager {
/// @notice Allows the Manager to allocate a reward from the reserve to a recipient.
/// @param _to The address to allocate the rewards to
/// @param _amount The amount of tokens for the reward
function allocateRewardFromReserve(address _to, uint96 _amount) external onlyDrawManager {
if (_amount > _reserve) {
revert InsufficientReserve(_amount, _reserve);
}
Expand All @@ -347,8 +348,8 @@ contract PrizePool is TieredLiquidityDistributor, Ownable {
_reserve -= _amount;
}

_transfer(_to, _amount);
emit WithdrawReserve(_to, _amount);
_rewards[_to] += _amount;
emit AllocateRewardFromReserve(_to, _amount);
}

/// @notice Allows the Manager to close the current open draw and open the next one.
Expand Down Expand Up @@ -480,7 +481,7 @@ contract PrizePool is TieredLiquidityDistributor, Ownable {
uint256 amount;
if (_fee != 0) {
emit IncreaseClaimRewards(_feeRecipient, _fee);
_claimerRewards[_feeRecipient] += _fee;
_rewards[_feeRecipient] += _fee;

unchecked {
amount = tierLiquidity.prizeSize - _fee;
Expand Down Expand Up @@ -511,23 +512,23 @@ contract PrizePool is TieredLiquidityDistributor, Ownable {
}

/**
* @notice Withdraws the claim fees for the caller.
* @param _to The address to transfer the claim fees to.
* @param _amount The amount of claim fees to withdraw
* @notice Withdraws earned rewards for the caller.
* @param _to The address to transfer the rewards to
* @param _amount The amount of rewards to withdraw
*/
function withdrawClaimRewards(address _to, uint256 _amount) external {
uint256 _available = _claimerRewards[msg.sender];
function withdrawRewards(address _to, uint256 _amount) external {
uint256 _available = _rewards[msg.sender];

if (_amount > _available) {
revert InsufficientRewardsError(_amount, _available);
}

unchecked {
_claimerRewards[msg.sender] = _available - _amount;
_rewards[msg.sender] = _available - _amount;
}

_transfer(_to, _amount);
emit WithdrawClaimRewards(_to, _amount, _available);
emit WithdrawRewards(msg.sender, _to, _amount, _available);
}

/// @notice Allows anyone to deposit directly into the Prize Pool reserve.
Expand Down Expand Up @@ -686,12 +687,12 @@ contract PrizePool is TieredLiquidityDistributor, Ownable {
}

/**
* @notice Returns the balance of fees for a given claimer
* @param _claimer The claimer to retrieve the fee balance for
* @return The balance of fees for the given claimer
* @notice Returns the balance of rewards earned for the given address.
* @param _recipient The recipient to retrieve the reward balance for
* @return The balance of rewards for the given recipient
*/
function balanceOfClaimRewards(address _claimer) external view returns (uint256) {
return _claimerRewards[_claimer];
function rewardBalance(address _recipient) external view returns (uint256) {
return _rewards[_recipient];
}

/**
Expand Down
109 changes: 79 additions & 30 deletions test/PrizePool.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,37 +65,38 @@ contract PrizePoolTest is Test {
uint48 drawStartedAt
);

/// @notice Emitted when any amount of the reserve is withdrawn.
/// @param to The address the assets are transferred to
/// @notice Emitted when any amount of the reserve is rewarded to a recipient.
/// @param to The recipient of the reward
/// @param amount The amount of assets rewarded
event AllocateRewardFromReserve(address indexed to, uint256 amount);

/// @notice Emitted when the reserve is manually increased.
/// @param user The user who increased the reserve
/// @param amount The amount of assets transferred
event WithdrawReserve(address indexed to, uint256 amount);
event ContributedReserve(address indexed user, uint256 amount);

/// @notice Emitted when a vault contributes prize tokens to the pool.
/// @param vault The address of the vault that is contributing tokens
/// @param drawId The ID of the first draw that the tokens will be applied to
/// @param amount The amount of tokens contributed
event ContributePrizeTokens(address indexed vault, uint24 indexed drawId, uint256 amount);

/// @notice Emitted when an address withdraws their claim rewards
/// @notice Emitted when an address withdraws their prize claim rewards.
/// @param account The account that is withdrawing rewards
/// @param to The address the rewards are sent to
/// @param amount The amount withdrawn
/// @param available The total amount that was available to withdraw before the transfer
event WithdrawClaimRewards(address indexed to, uint256 amount, uint256 available);
event WithdrawRewards(address indexed account, address indexed to, uint256 amount, uint256 available);

/// @notice Emitted when an address receives new claim rewards
/// @notice Emitted when an address receives new prize claim rewards.
/// @param to The address the rewards are given to
/// @param amount The amount increased
event IncreaseClaimRewards(address indexed to, uint256 amount);

/// @notice Emitted when the drawManager is set
/// @notice Emitted when the drawManager is set.
/// @param drawManager The draw manager
event DrawManagerSet(address indexed drawManager);

/// @notice Emitted when the reserve is manually increased.
/// @param user The user who increased the reserve
/// @param amount The amount of assets transferred
event ContributedReserve(address indexed user, uint256 amount);

/**********************************************************************************/

ConstructorParams params;
Expand Down Expand Up @@ -282,28 +283,35 @@ contract PrizePoolTest is Test {
assertEq(prizePool.reserveForOpenDraw(), newReserve);
}

function testWithdrawReserve_notManager() public {
function testAllocateRewardFromReserve_notManager() public {
vm.prank(address(0));
vm.expectRevert(
abi.encodeWithSelector(CallerNotDrawManager.selector, address(0), address(this))
);
prizePool.withdrawReserve(address(0), 1);
prizePool.allocateRewardFromReserve(address(0), 1);
}

function testWithdrawReserve_insuff() public {
function testAllocateRewardFromReserve_insuff() public {
vm.expectRevert(abi.encodeWithSelector(InsufficientReserve.selector, 1, 0));
prizePool.withdrawReserve(address(this), 1);
prizePool.allocateRewardFromReserve(address(this), 1);
}

function testWithdrawReserve() public {
function testAllocateRewardFromReserve() public {
contribute(310e18);
closeDraw(winningRandomNumber);
assertEq(prizeToken.balanceOf(address(this)), 0);
vm.expectEmit();
emit WithdrawReserve(address(this), 1e18);
prizePool.withdrawReserve(address(this), 1e18);
assertEq(prizeToken.balanceOf(address(this)), 1e18);
assertEq(prizePool.accountedBalance(), 309e18);
emit AllocateRewardFromReserve(address(this), 1e18);
prizePool.allocateRewardFromReserve(address(this), 1e18);
assertEq(prizePool.rewardBalance(address(this)), 1e18);
assertEq(prizeToken.balanceOf(address(this)), 0); // still 0 since there shouldn't be a transfer
assertEq(prizePool.accountedBalance(), 310e18); // still 310e18 since there were no tokens transferred out yet

// withdraw rewards:
prizePool.withdrawRewards(address(this), 1e17);
assertEq(prizePool.rewardBalance(address(this)), 9e17);
assertEq(prizeToken.balanceOf(address(this)), 1e17);
assertEq(prizePool.accountedBalance(), 3099e17);
}

function testGetTotalContributedBetween() public {
Expand Down Expand Up @@ -346,7 +354,7 @@ contract PrizePoolTest is Test {
(10e18 * RESERVE_SHARES) / prizePool.getTotalShares(),
100
);
prizePool.withdrawReserve(address(this), prizePool.reserve());
prizePool.allocateRewardFromReserve(address(this), prizePool.reserve());
assertEq(prizePool.accountedBalance(), prizeToken.balanceOf(address(prizePool)));
assertEq(prizePool.reserve(), 0);
}
Expand Down Expand Up @@ -878,7 +886,7 @@ contract PrizePoolTest is Test {
// grand prize is (100/220) * 0.1 * 100e18 = 4.5454...e18
assertEq(prizeToken.balanceOf(msg.sender), prize - 1e18, "balance is prize less fee");
assertEq(prizePool.claimCount(), 1);
assertEq(prizePool.balanceOfClaimRewards(address(this)), 1e18);
assertEq(prizePool.rewardBalance(address(this)), 1e18);
}

function testClaimPrize_notWinner() public {
Expand Down Expand Up @@ -986,6 +994,47 @@ contract PrizePoolTest is Test {
assertEq(prizePool.claimCount(), 1);
}

function testClaimPrize_claimFeesAccountedFor() public {
contribute(100e18);
closeDraw(winningRandomNumber);

address winner = makeAddr("winner");
address recipient = makeAddr("recipient");
mockTwab(address(this), winner, 1);

uint96 fee = 0xfee;
uint prizeAmount = 806451612903225800;
uint prize = prizeAmount - fee;
assertApproxEqAbs(prizeAmount, (10e18 * TIER_SHARES) / (4 * prizePool.getTotalShares()), 100);

vm.expectEmit();
emit ClaimedPrize(address(this), winner, recipient, 1, 1, 0, uint152(prize), fee, address(this));
assertEq(prizePool.claimPrize(winner, 1, 0, recipient, fee, address(this)), prizeAmount);
assertEq(prizeToken.balanceOf(recipient), prize, "recipient balance is good");
assertEq(prizePool.claimCount(), 1);

// Check if claim fees are accounted for
// (if they aren't anyone can call contributePrizeTokens with the unaccounted fee amount and basically take it as their own)
uint accountedBalance = prizePool.accountedBalance();
uint actualBalance = prizeToken.balanceOf(address(prizePool));
console2.log("accounted balance: ", accountedBalance);
console2.log("actual balance: ", actualBalance);
console2.log("diff: ", actualBalance - accountedBalance);

// show that the claimer can still withdraw their fees:
assertEq(prizeToken.balanceOf(address(this)), 0);
vm.expectEmit();
emit WithdrawRewards(address(this), address(this), fee, fee);
prizePool.withdrawRewards(address(this), fee);
assertEq(prizeToken.balanceOf(address(this)), fee);

accountedBalance = prizePool.accountedBalance();
actualBalance = prizeToken.balanceOf(address(prizePool));
console2.log("accounted balance: ", accountedBalance);
console2.log("actual balance: ", actualBalance);
console2.log("diff: ", actualBalance - accountedBalance);
}

function testTotalWithdrawn() public {
assertEq(prizePool.totalWithdrawn(), 0);
contribute(100e18);
Expand Down Expand Up @@ -1047,30 +1096,30 @@ contract PrizePoolTest is Test {
assertEq(prizePool.hasOpenDrawFinished(), true);
}

function testWithdrawClaimRewards_sufficient() public {
function testWithdrawRewards_sufficient() public {
contribute(100e18);
closeDraw(winningRandomNumber);
mockTwab(address(this), msg.sender, 0);
claimPrize(msg.sender, 0, 0, 1e18, address(this));
prizePool.withdrawClaimRewards(address(this), 1e18);
prizePool.withdrawRewards(address(this), 1e18);
assertEq(prizeToken.balanceOf(address(this)), 1e18);
}

function testWithdrawClaimRewards_insufficient() public {
function testWithdrawRewards_insufficient() public {
vm.expectRevert(abi.encodeWithSelector(InsufficientRewardsError.selector, 1e18, 0));
prizePool.withdrawClaimRewards(address(this), 1e18);
prizePool.withdrawRewards(address(this), 1e18);
}

function testWithdrawClaimRewards_emitsEvent() public {
function testWithdrawRewards_emitsEvent() public {
contribute(100e18);
closeDraw(winningRandomNumber);
mockTwab(address(this), msg.sender, 0);

prizePool.claimPrize(msg.sender, 0, 0, msg.sender, 1e18, address(this));

vm.expectEmit();
emit WithdrawClaimRewards(address(this), 5e17, 1e18);
prizePool.withdrawClaimRewards(address(this), 5e17);
emit WithdrawRewards(address(this), address(1), 5e17, 1e18);
prizePool.withdrawRewards(address(1), 5e17);
}

function testOpenDrawStartsAt_zeroDraw() public {
Expand Down
2 changes: 1 addition & 1 deletion test/invariants/PrizePoolInvariants.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ contract PrizePoolInvariants is Test {
bytes4[] memory selectors = new bytes4[](6);
selectors[0] = prizePoolHarness.contributePrizeTokens.selector;
selectors[1] = prizePoolHarness.contributeReserve.selector;
selectors[2] = prizePoolHarness.withdrawReserve.selector;
selectors[2] = prizePoolHarness.allocateRewardFromReserve.selector;
selectors[3] = prizePoolHarness.withdrawClaimReward.selector;
selectors[4] = prizePoolHarness.claimPrizes.selector;
selectors[5] = prizePoolHarness.closeDraw.selector;
Expand Down
6 changes: 3 additions & 3 deletions test/invariants/helpers/PrizePoolFuzzHarness.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@ contract PrizePoolFuzzHarness is CommonBase, StdCheats {
prizePool.contributeReserve(_amount);
}

function withdrawReserve() public warp {
function allocateRewardFromReserve() public warp {
uint96 amount = prizePool.reserve();
withdrawn += amount;
prizePool.withdrawReserve(address(msg.sender), amount);
prizePool.allocateRewardFromReserve(address(msg.sender), amount);
}

function withdrawClaimReward() public warp {
vm.startPrank(claimer);
prizePool.withdrawClaimRewards(address(claimer), prizePool.balanceOfClaimRewards(claimer));
prizePool.withdrawRewards(address(claimer), prizePool.rewardBalance(claimer));
vm.stopPrank();
}

Expand Down