1+ // SPDX-License-Identifier: MIT
2+ pragma solidity ^ 0.8.24 ;
3+
4+ import { IPrizeHooks } from "../lib/pt-v5-vault/src/interfaces/IPrizeHooks.sol " ;
5+ import { PrizePool } from "../lib/pt-v5-vault/lib/pt-v5-prize-pool/src/PrizePool.sol " ;
6+ import { IERC20 } from "../lib/pt-v5-vault/lib/pt-v5-prize-pool/lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol " ;
7+ import { Ownable2Step, Ownable } from "../lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol " ;
8+
9+ /// @title PoolTogether V5 - World Verified Prize Boost Hook
10+ /// @notice This contract is a prize hook for PoolTogether V5 that sends an additional multiplier of prize tokens to the winner
11+ /// if the winning address is verified with a world ID.
12+ /// @author G9 Software Inc.
13+ contract WorldVerifiedPrizeBoostHook is Ownable2Step , IPrizeHooks {
14+ /// @notice Emitted when a prize win is boosted
15+ /// @param recipient The recipient of the prize and boost
16+ /// @param vault The vault the prize was won through
17+ /// @param prizeAmount The original prize tokens won
18+ /// @param boostAmount The amount of boosted prize tokens sent to the recipient
19+ /// @param tier The prize tier won
20+ event VerifiedPrizeBoosted (address indexed recipient , address indexed vault , uint256 prizeAmount , uint256 boostAmount , uint8 tier );
21+
22+ /// @notice Emitted when the boost multiplier is set
23+ /// @param boostMultiplier The new boost multiplier
24+ event SetBoostMultiplier (uint256 boostMultiplier );
25+
26+ /// @notice Emitted when the per winner boost limit is set
27+ /// @param perWinnerBoostLimit The new boost limit in prize tokens
28+ event SetPerWinnerBoostLimit (uint256 perWinnerBoostLimit );
29+
30+ /// @notice The prize pool that is eligible for boosting
31+ PrizePool public immutable PRIZE_POOL;
32+
33+ /// @notice The prize token used for boosting
34+ IERC20 public immutable PRIZE_TOKEN;
35+
36+ /// @notice Mapping to keep track of boosted prizes and prevent replay attacks
37+ mapping (
38+ address vault = > mapping (
39+ address account = > mapping (
40+ uint24 drawId = > mapping (
41+ uint8 tier = > mapping (
42+ uint32 prizeIndex = > bool hooked
43+ )
44+ )
45+ )
46+ )
47+ ) public isPrizeBoosted;
48+
49+ /// @notice The prize boost multiplier
50+ uint256 public boostMultiplier;
51+
52+ /// @notice The maximum total boost tokens that can be won by each verified address
53+ uint256 public perWinnerBoostLimit;
54+
55+ /// @notice The total boost tokens that have been received by an address
56+ mapping (address => uint256 ) public boostTokensReceived;
57+
58+ /// @notice Constructor to set parameters for the vault hook
59+ /// @param _prizePool The prize pool to boost wins from
60+ /// @param _initialOwner The initial owner of the vault hook
61+ /// @param _boostMultiplier The integer multipler that determines the boost amount from the prize won
62+ /// (ex. **1x** boost Multiplier will send **1x** the prize amount as a boost to the winner, effectively creating a **2x** prize multiplier)
63+ /// @param _perWinnerBoostLimit The initial max amount of total boosted prize tokens that will be sent to any verified address over any amount of time
64+ constructor (PrizePool _prizePool , address _initialOwner , uint256 _boostMultiplier , uint256 _perWinnerBoostLimit ) Ownable (_initialOwner) {
65+ PRIZE_POOL = _prizePool;
66+ PRIZE_TOKEN = _prizePool.prizeToken ();
67+ _setBoostMultiplier (_boostMultiplier);
68+ _setPerWinnerBoostLimit (_perWinnerBoostLimit);
69+ }
70+
71+ /// @inheritdoc IPrizeHooks
72+ /// @dev This prize hook does not implement the `beforeClaimPrize` call, but it is still required in the
73+ /// IPrizeHooks interface.
74+ function beforeClaimPrize (address , uint8 , uint32 , uint96 , address ) external pure returns (address , bytes memory ) {}
75+
76+ /// @inheritdoc IPrizeHooks
77+ /// @notice Sends `boostMultiplier` times the prize amount in *extra* prize tokens to the winner if the winner's address
78+ /// is verified and the prize hasn't already been boosted.
79+ /// @dev Ensures the winner is the recipient (this will always be the case on standard vaults using this hook)
80+ /// @dev Caps the boost amount to prevent the total amount of boost tokens the winner receives from exceeding the
81+ /// `perWinnerBoostLimit`.
82+ /// @dev Fails silently as to not interrupt a prize claim if the prize is not eligible for a boost or if
83+ /// this contract runs out of boost funds.
84+ function afterClaimPrize (address winner , uint8 tier , uint32 prizeIndex , uint256 prizeAmount , address recipient , bytes memory ) external {
85+ uint24 awardedDrawId = PRIZE_POOL.getLastAwardedDrawId ();
86+ uint256 winnerPreviousBoostReceived = boostTokensReceived[winner];
87+ if (
88+ winner == recipient &&
89+ PRIZE_POOL.isWinner (msg .sender , winner, tier, prizeIndex) &&
90+ ! isPrizeBoosted[msg .sender ][winner][awardedDrawId][tier][prizeIndex] &&
91+ winnerPreviousBoostReceived < perWinnerBoostLimit
92+ ) {
93+ uint256 boostAmount = prizeAmount * boostMultiplier;
94+ if (winnerPreviousBoostReceived + boostAmount > perWinnerBoostLimit) {
95+ boostAmount = perWinnerBoostLimit - winnerPreviousBoostReceived;
96+ }
97+ if (
98+ boostAmount > 0 &&
99+ PRIZE_TOKEN.balanceOf (address (this )) >= boostAmount
100+ ) {
101+ isPrizeBoosted[msg .sender ][winner][awardedDrawId][tier][prizeIndex] = true ;
102+ PRIZE_TOKEN.transfer (winner, boostAmount);
103+ emit VerifiedPrizeBoosted (winner, msg .sender , prizeAmount, boostAmount, tier);
104+ }
105+ }
106+ }
107+
108+ /// @notice ONLY OWNER function to change the boost multiplier
109+ /// @param _boostMultiplier The new integer boost multiplier
110+ function setBoostMultiplier (uint256 _boostMultiplier ) external onlyOwner {
111+ _setBoostMultiplier (_boostMultiplier);
112+ }
113+
114+ /// @notice ONLY OWNER function to change the per winner boost limit
115+ /// @param _perWinnerBoostLimit The new per winner boost limit
116+ function setPerWinnerBoostLimit (uint256 _perWinnerBoostLimit ) external onlyOwner {
117+ _setPerWinnerBoostLimit (_perWinnerBoostLimit);
118+ }
119+
120+ /// @notice Sets the boost multiplier and emits an event
121+ function _setBoostMultiplier (uint256 _boostMultiplier ) internal {
122+ boostMultiplier = _boostMultiplier;
123+ emit SetBoostMultiplier (_boostMultiplier);
124+ }
125+
126+ /// @notice Sets the per winner boost limit and emits an event
127+ function _setPerWinnerBoostLimit (uint256 _perWinnerBoostLimit ) internal {
128+ perWinnerBoostLimit = _perWinnerBoostLimit;
129+ emit SetPerWinnerBoostLimit (_perWinnerBoostLimit);
130+ }
131+ }
0 commit comments