| Overview | Design | Implementation | Setting the Hook |
In this example we will demonstrate how to award prizes to random holders of an NFT using a prize hook. This can be used for NFT promotions or to integrate prize savings directly with tokenized collectibles.
The core principle of awarding prizes to a random NFT holder is simple: we use the entropy from the daily draw to select a random NFT holder and redirect the prize to them using the beforePrizeClaim
hook. To accomplish this we need to ensure two things:
- The entropy can not be manipulated by NFT holders.
- The NFT holders are enumerable (we know how many there are and can get the owner's address of the
nth
token)
import { IPrizeHooks } from "pt-v5-vault/interfaces/IPrizeHooks.sol";
contract PrizeToEnumerableNFTHolderHook is IPrizeHooks {
// hook code goes here...
}
import { IERC721Enumerable } from "openzeppelin-v5/token/ERC721/extensions/IERC721Enumerable.sol";
import { PrizePool } from "pt-v5-prize-pool/PrizePool.sol";
error TokenNotERC721Enumerable();
error PrizePoolAddressZero();
contract PrizeToEnumerableNFTHolderHook is IPrizeHooks {
IERC721Enumerable public enumerableToken;
PrizePool public prizePool;
constructor(IERC721Enumerable enumerableToken_, PrizePool prizePool_) {
if (address(0) == address(prizePool_)) {
revert PrizePoolAddressZero();
}
if (
!enumerableToken_.supportsInterface(type(IERC721Enumerable).interfaceId)
) {
revert TokenNotERC721Enumerable();
}
enumerableToken = enumerableToken_;
prizePool = prizePool_;
}
}
Note that we check to ensure that the NFT implements the OpenZeppelin
IERC721Enumerable
interface to ensure that the token contract provides the functions needed to pick a random token holder.
Implement the beforeClaimPrize
hook and use the random number from the awarded draw as entropy to select a random NFT holder:
import { UniformRandomNumber } from "uniform-random-number/UniformRandomNumber.sol";
// ...
function beforeClaimPrize(
address,
uint8 tier,
uint32 prizeIndex,
uint96,
address
) external view returns (address prizeRecipient, bytes memory data) {
uint256 _entropy = uint256(keccak256(abi.encode(prizePool.getWinningRandomNumber(), tier, prizeIndex)));
uint256 _randomTokenIndex = UniformRandomNumber.uniform(_entropy, enumerableToken.totalSupply());
prizeRecipient = enumerableToken.ownerOf(enumerableToken.tokenByIndex(_randomTokenIndex));
}
Note that we include the tier and prize index in the entropy so that each individual prize won in a draw can have a different winner.
We also use the
UniformRandomNumber
library to ensure we don't introduce any modulo bias to the random selection.
function afterClaimPrize(
address,
uint8,
uint32,
uint256,
address,
bytes memory
) external pure {}
We now have a hook that can award prizes to random NFT holders! See the full implementation here.
If a you want to set this hook on a prize vault, you will need to:
- Deploy the hook contract
- Call
setHooks
on the prize vault contract with the following information:
VaultHooks({
useBeforeClaimPrize: true;
useAfterClaimPrize: false;
implementation: 0x... // replace with the hook deployment contract
});