diff --git a/contracts/PointTokenVault.sol b/contracts/PointTokenVault.sol index fd6f5f6..60f0954 100644 --- a/contracts/PointTokenVault.sol +++ b/contracts/PointTokenVault.sol @@ -26,7 +26,7 @@ contract PointTokenVault is UUPSUpgradeable, AccessControlUpgradeable, Multicall bytes32 public constant MERKLE_UPDATER_ROLE = keccak256("MERKLE_UPDATER_ROLE"); bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - // Deposit asset balancess. + // Deposit asset balances. mapping(address => mapping(ERC20 => uint256)) public balances; // user => point-earning token => balance // Merkle root distribution. @@ -69,6 +69,7 @@ contract PointTokenVault is UUPSUpgradeable, AccessControlUpgradeable, Multicall error ProofInvalidOrExpired(); error ClaimTooLarge(); error RewardsNotReleased(); + error CantConvertMerkleRedemption(); error PTokenAlreadyDeployed(); error DepositExceedsCap(); error PTokenNotDeployed(); @@ -156,12 +157,17 @@ contract PointTokenVault is UUPSUpgradeable, AccessControlUpgradeable, Multicall /// @notice Mints point tokens for rewards after redemption has been enabled function convertRewardsToPTokens(address _receiver, bytes32 _pointsId, uint256 _amountToConvert) public { RedemptionParams memory params = redemptions[_pointsId]; - (ERC20 rewardToken, uint256 rewardsPerPToken) = (params.rewardToken, params.rewardsPerPToken); + (ERC20 rewardToken, uint256 rewardsPerPToken, bool isMerkleBased) = + (params.rewardToken, params.rewardsPerPToken, params.isMerkleBased); if (address(rewardToken) == address(0)) { revert RewardsNotReleased(); } + if (isMerkleBased) { + revert CantConvertMerkleRedemption(); + } + rewardToken.safeTransferFrom(msg.sender, address(this), _amountToConvert); pTokens[_pointsId].mint(_receiver, FixedPointMathLib.divWadDown(_amountToConvert, rewardsPerPToken)); // Round down for mint. } diff --git a/contracts/test/PointTokenVault.t.sol b/contracts/test/PointTokenVault.t.sol index 05a4ee8..579f43f 100644 --- a/contracts/test/PointTokenVault.t.sol +++ b/contracts/test/PointTokenVault.t.sol @@ -498,6 +498,42 @@ contract PointTokenVaultTest is Test { assertEq(pointTokenVault.pTokens(eigenPointsId).balanceOf(vitalik), 0); } + function test_CantMintPTokensForRewardsMerkleBased() public { + bool IS_MERKLE_BASED = true; + + bytes32 root = 0x409fd0e46d8453765fb513ae35a1899d667478c40233b67360023c86927eb802; + + bytes32[] memory proof = new bytes32[](2); + proof[0] = 0x6d0fcb8de12b1f57f81e49fa18b641487b932cdba4f064409fde3b05d3824ca2; + proof[1] = 0xae126f1299213c869259b52ab24f7270f3cce1de54c187271c52373d8947c2fe; + + vm.prank(merkleUpdater); + pointTokenVault.updateRoot(root); + + vm.prank(vitalik); + pointTokenVault.claimPTokens(PointTokenVault.Claim(eigenPointsId, 1e18, 1e18, proof), vitalik); + + rewardToken.mint(address(pointTokenVault), 3e18); + + vm.prank(operator); + pointTokenVault.setRedemption(eigenPointsId, rewardToken, 2e18, IS_MERKLE_BASED); + + bytes32[] memory redemptionProof = new bytes32[](1); + redemptionProof[0] = 0x4e40a10ce33f33a4786960a8bb843fe0e170b651acd83da27abc97176c4bed3c; + vm.prank(vitalik); + pointTokenVault.redeemRewards(PointTokenVault.Claim(eigenPointsId, 2e18, 2e18, redemptionProof), vitalik); + + assertEq(rewardToken.balanceOf(vitalik), 2e18); + assertEq(pointTokenVault.pTokens(eigenPointsId).balanceOf(vitalik), 0); + + // Can't mint ptokens if it's a merkle-based redemption + vm.prank(vitalik); + rewardToken.approve(address(pointTokenVault), 1e18); + vm.prank(vitalik); + vm.expectRevert(PointTokenVault.CantConvertMerkleRedemption.selector); + pointTokenVault.convertRewardsToPTokens(vitalik, eigenPointsId, 1e18); + } + function test_ReceiveETH() public payable { // Amount of ETH to send uint256 amountToSend = 1 ether;