From aed4f4addf8f674584c41f30b38720d286becbc8 Mon Sep 17 00:00:00 2001 From: Josh Levine Date: Tue, 23 Apr 2024 16:38:38 -0400 Subject: [PATCH] feat: add function to go from rewards back to ptokens after redemption has been enabled --- contracts/PointTokenVault.sol | 15 +++++++++- contracts/test/PointTokenVault.t.sol | 41 ++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/contracts/PointTokenVault.sol b/contracts/PointTokenVault.sol index 6145d7f..d2b25e9 100644 --- a/contracts/PointTokenVault.sol +++ b/contracts/PointTokenVault.sol @@ -109,7 +109,7 @@ contract PointTokenVault is UUPSUpgradeable, AccessControlUpgradeable, Multicall /// @notice Claims point tokens after verifying the merkle proof /// @param _claim The claim details including the merkle proof /// @param _account The account to claim for - // Adapted from Morpho's RewardsDistributor.sol (https://github.com/morpho-org/morpho-optimizers/blob/main/src/common/rewards-distribution/RewardsDistributor.sol) + // Adapted from Morpho's RewardsDistributor.sol (https://github.com/morpho-org/morpho-optimizers/blob/ffd702f045d24b911d6c8c6c2194dd15cf9387ff/src/common/rewards-distribution/RewardsDistributor.sol) function claimPTokens(Claim calldata _claim, address _account) public { bytes32 pointsId = _claim.pointsId; @@ -153,6 +153,19 @@ contract PointTokenVault is UUPSUpgradeable, AccessControlUpgradeable, Multicall emit RewardsClaimed(msg.sender, _receiver, pointsId, amountToClaim); } + /// @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); + + if (address(rewardToken) == address(0)) { + revert RewardsNotReleased(); + } + + rewardToken.safeTransferFrom(msg.sender, address(this), _amountToConvert); + pTokens[_pointsId].mint(_receiver, FixedPointMathLib.divWadDown(_amountToConvert, rewardsPerPToken)); // Round down for mint. + } + function deployPToken(bytes32 _pointsId) public { if (address(pTokens[_pointsId]) != address(0)) { revert PTokenAlreadyDeployed(); diff --git a/contracts/test/PointTokenVault.t.sol b/contracts/test/PointTokenVault.t.sol index 1c9e1fa..c25a3fd 100644 --- a/contracts/test/PointTokenVault.t.sol +++ b/contracts/test/PointTokenVault.t.sol @@ -456,6 +456,47 @@ contract PointTokenVaultTest is Test { assertEq(pointTokenVault.pTokens(eigenPointsId).balanceOf(vitalik), 1e18); } + + function test_MintPTokensForRewards() public { + bytes32 root = 0x4e40a10ce33f33a4786960a8bb843fe0e170b651acd83da27abc97176c4bed3c; + + bytes32[] memory proof = new bytes32[](1); + proof[0] = 0x6d0fcb8de12b1f57f81e49fa18b641487b932cdba4f064409fde3b05d3824ca2; + + 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, false); + + bytes32[] memory empty = new bytes32[](0); + vm.prank(vitalik); + pointTokenVault.redeemRewards(PointTokenVault.Claim(eigenPointsId, 2e18, 2e18, empty), vitalik); + + assertEq(rewardToken.balanceOf(vitalik), 2e18); + assertEq(pointTokenVault.pTokens(eigenPointsId).balanceOf(vitalik), 0); + + // Mint pTokens with reward tokens + vm.prank(vitalik); + rewardToken.approve(address(pointTokenVault), 1e18); + vm.prank(vitalik); + pointTokenVault.convertRewardsToPTokens(vitalik, eigenPointsId, 1e18); + + assertEq(rewardToken.balanceOf(vitalik), 1e18); + assertEq(pointTokenVault.pTokens(eigenPointsId).balanceOf(vitalik), 0.5e18); + + // Can go the other way again + vm.prank(vitalik); + pointTokenVault.redeemRewards(PointTokenVault.Claim(eigenPointsId, 1e18, 1e18, empty), vitalik); + + assertEq(rewardToken.balanceOf(vitalik), 2e18); + assertEq(pointTokenVault.pTokens(eigenPointsId).balanceOf(vitalik), 0); + } } contract Echo {