Skip to content

Commit

Permalink
Merge pull request #278 from morpho-org/refactor/rewards-skim
Browse files Browse the repository at this point in the history
refactor(metamorpho): rename rewards to skim
  • Loading branch information
Rubilmax authored Nov 14, 2023
2 parents fe0f1e1 + 25151f8 commit 77fbb34
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 45 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/foundry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ jobs:
type: ["slow", "fast"]
include:
- type: "slow"
fuzz-runs: 32768
fuzz-runs: 8192
max-test-rejects: 1048576
invariant-runs: 64
invariant-depth: 1024
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The vault owner can set a performance fee, cutting up to 50% of the generated in
The `feeRecipient` can then withdraw the accumulated fee at any time.

The vault may be entitled to some rewards emitted on Morpho Blue markets the vault has supplied to.
Those rewards can be transferred to the `rewardsRecipient`.
Those rewards can be transferred to the `skimRecipient`.
The vault's owner has the choice to distribute back these rewards to vault depositors however they want.
For more information about this use case, see the [Rewards](#rewards) section.

Expand Down Expand Up @@ -100,14 +100,14 @@ Below is a typical example of how this use case would take place:
- If not already done:

- Create a rewards distributor using the [UrdFactory](https://github.com/morpho-org/universal-rewards-distributor/blob/main/src/UrdFactory.sol) (can be done by anyone).
- Set the vault’s rewards recipient address to the created URD using `setRewardsRecipient`.
- Set the vault’s rewards recipient address to the created URD using `setSkimRecipient`.

- Claim tokens from the Morpho Blue distribution to the vault.

NB: Anyone can claim tokens on behalf of the vault and automatically transfer them to the vault.
Thus, this step might be already performed by some third-party.

- Transfer rewards from the vault to the rewards distributor using the `transferRewards` function.
- Transfer rewards from the vault to the rewards distributor using the `skim` function.

NB: Anyone can transfer rewards from the vault to the rewards distributor unless it is unset.
Thus, this step might be already performed by some third-party.
Expand Down
25 changes: 12 additions & 13 deletions src/MetaMorpho.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph
/// @notice The fee recipient.
address public feeRecipient;

/// @notice The rewards recipient.
address public rewardsRecipient;
/// @notice The skim recipient.
address public skimRecipient;

/// @dev Stores the order of markets on which liquidity is supplied upon deposit.
/// @dev Can contain any market. A market is skipped as soon as its supply cap is reached.
Expand Down Expand Up @@ -197,13 +197,13 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph
emit EventsLib.SetIsAllocator(newAllocator, newIsAllocator);
}

/// @notice Sets `rewardsRecipient` to `newRewardsRecipient`.
function setRewardsRecipient(address newRewardsRecipient) external onlyOwner {
if (newRewardsRecipient == rewardsRecipient) revert ErrorsLib.AlreadySet();
/// @notice Sets `skimRecipient` to `newSkimRecipient`.
function setSkimRecipient(address newSkimRecipient) external onlyOwner {
if (newSkimRecipient == skimRecipient) revert ErrorsLib.AlreadySet();

rewardsRecipient = newRewardsRecipient;
skimRecipient = newSkimRecipient;

emit EventsLib.SetRewardsRecipient(newRewardsRecipient);
emit EventsLib.SetSkimRecipient(newSkimRecipient);
}

/// @notice Submits a `newTimelock`.
Expand Down Expand Up @@ -515,17 +515,16 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph
_setCap(id, uint184(pendingCap[id].value));
}

/// @notice Transfers `token` rewards collected by the vault to the `rewardsRecipient`.
/// @dev Can be used to extract any token that would be stuck on the contract as well.
function transferRewards(address token) external {
if (rewardsRecipient == address(0)) revert ErrorsLib.ZeroAddress();
/// @notice Skims the vault `token` balance to `skimRecipient`.
function skim(address token) external {
if (skimRecipient == address(0)) revert ErrorsLib.ZeroAddress();

uint256 amount = IERC20(token).balanceOf(address(this));
if (token == asset()) amount -= idle;

IERC20(token).safeTransfer(rewardsRecipient, amount);
IERC20(token).safeTransfer(skimRecipient, amount);

emit EventsLib.TransferRewards(_msgSender(), token, amount);
emit EventsLib.Skim(_msgSender(), token, amount);
}

/* ERC4626 (PUBLIC) */
Expand Down
6 changes: 3 additions & 3 deletions src/interfaces/IMetaMorpho.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ interface IMetaMorphoBase {

function fee() external view returns (uint96);
function feeRecipient() external view returns (address);
function rewardsRecipient() external view returns (address);
function skimRecipient() external view returns (address);
function timelock() external view returns (uint256);
function supplyQueue(uint256) external view returns (Id);
function supplyQueueLength() external view returns (uint256);
Expand All @@ -63,13 +63,13 @@ interface IMetaMorphoBase {
function acceptGuardian() external;
function revokePendingGuardian() external;

function transferRewards(address) external;
function skim(address) external;

function setIsAllocator(address newAllocator, bool newIsAllocator) external;
function setCurator(address newCurator) external;
function setFee(uint256 newFee) external;
function setFeeRecipient(address newFeeRecipient) external;
function setRewardsRecipient(address) external;
function setSkimRecipient(address) external;

function setSupplyQueue(Id[] calldata newSupplyQueue) external;
function updateWithdrawQueue(uint256[] calldata indexes) external;
Expand Down
8 changes: 4 additions & 4 deletions src/libraries/EventsLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ library EventsLib {
/// @notice Emitted `timelock` is set to `newTimelock`.
event SetTimelock(address indexed caller, uint256 newTimelock);

/// @notice Emitted `rewardsDistibutor` is set to `newRewardsRecipient`.
event SetRewardsRecipient(address indexed newRewardsRecipient);
/// @notice Emitted when `skimRecipient` is set to `newSkimRecipient`.
event SetSkimRecipient(address indexed newSkimRecipient);

/// @notice Emitted `fee` is set to `newFee`.
event SetFee(address indexed caller, uint256 newFee);
Expand Down Expand Up @@ -85,8 +85,8 @@ library EventsLib {
/// @notice Emitted when fees are accrued.
event AccrueFee(uint256 feeShares);

/// @notice Emitted when an `amount` of `token` is transferred to the `rewardsRecipient` by `caller`.
event TransferRewards(address indexed caller, address indexed token, uint256 amount);
/// @notice Emitted when an `amount` of `token` is transferred to the skim recipient by `caller`.
event Skim(address indexed caller, address indexed token, uint256 amount);

/// @notice Emitted when a new MetaMorpho vault is created.
/// @param metaMorpho The address of the MetaMorpho vault.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "erc4626-tests/ERC4626.test.sol";

import {IntegrationTest} from "./helpers/IntegrationTest.sol";

contract ERC4626A16zTest is IntegrationTest, ERC4626Test {
contract ERC4626ComplianceTest is IntegrationTest, ERC4626Test {
function setUp() public override(IntegrationTest, ERC4626Test) {
super.setUp();

Expand All @@ -14,5 +14,7 @@ contract ERC4626A16zTest is IntegrationTest, ERC4626Test {
_delta_ = 0;
_vaultMayBeEmpty = true;
_unlimitedAmount = true;

_setCap(allMarkets[0], 1e28);
}
}
40 changes: 20 additions & 20 deletions test/forge/UrdTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,41 +21,41 @@ contract UrdTest is IntegrationTest {
rewardsDistributor = urdFactory.createUrd(OWNER, 0, bytes32(0), bytes32(0), bytes32(0));
}

function testSetRewardsRecipient(address newRewardsRecipient) public {
vm.assume(newRewardsRecipient != vault.rewardsRecipient());
function testSetSkimRecipient(address newSkimRecipient) public {
vm.assume(newSkimRecipient != vault.skimRecipient());

vm.expectEmit();
emit EventsLib.SetRewardsRecipient(newRewardsRecipient);
emit EventsLib.SetSkimRecipient(newSkimRecipient);
vm.prank(OWNER);
vault.setRewardsRecipient(newRewardsRecipient);
vault.setSkimRecipient(newSkimRecipient);

assertEq(vault.rewardsRecipient(), newRewardsRecipient);
assertEq(vault.skimRecipient(), newSkimRecipient);
}

function testAlreadySetRewardsRecipient() public {
address currentRewardsRecipient = vault.rewardsRecipient();
function testAlreadySetSkimRecipient() public {
address currentSkimRecipient = vault.skimRecipient();

vm.prank(OWNER);
vm.expectRevert(ErrorsLib.AlreadySet.selector);
vault.setRewardsRecipient(currentRewardsRecipient);
vault.setSkimRecipient(currentSkimRecipient);
}

function testSetRewardsRecipientNotOwner() public {
function testSetSkimRecipientNotOwner() public {
vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, address(this)));
vault.setRewardsRecipient(address(0));
vault.setSkimRecipient(address(0));
}

function testTransferRewardsNotLoanToken(uint256 amount) public {
function testSkimNotLoanToken(uint256 amount) public {
vm.prank(OWNER);
vault.setRewardsRecipient(address(rewardsDistributor));
vault.setSkimRecipient(address(rewardsDistributor));

collateralToken.setBalance(address(vault), amount);
uint256 vaultBalanceBefore = collateralToken.balanceOf(address(vault));
assertEq(vaultBalanceBefore, amount, "vaultBalanceBefore");

vm.expectEmit(address(vault));
emit EventsLib.TransferRewards(address(this), address(collateralToken), amount);
vault.transferRewards(address(collateralToken));
emit EventsLib.Skim(address(this), address(collateralToken), amount);
vault.skim(address(collateralToken));
uint256 vaultBalanceAfter = collateralToken.balanceOf(address(vault));

assertEq(vaultBalanceAfter, 0, "vaultBalanceAfter");
Expand All @@ -66,12 +66,12 @@ contract UrdTest is IntegrationTest {
);
}

function testTransferRewardsLoanToken(uint256 rewards, uint256 idle) public {
function testSkimLoanToken(uint256 rewards, uint256 idle) public {
idle = bound(idle, 0, MAX_TEST_ASSETS);
rewards = bound(rewards, 0, MAX_TEST_ASSETS);

vm.prank(OWNER);
vault.setRewardsRecipient(address(rewardsDistributor));
vault.setSkimRecipient(address(rewardsDistributor));

loanToken.setBalance(address(vault), rewards);

Expand All @@ -84,8 +84,8 @@ contract UrdTest is IntegrationTest {
assertEq(vaultBalanceBefore, idle + rewards, "vaultBalanceBefore");

vm.expectEmit(address(vault));
emit EventsLib.TransferRewards(address(this), address(loanToken), rewards);
vault.transferRewards(address(loanToken));
emit EventsLib.Skim(address(this), address(loanToken), rewards);
vault.skim(address(loanToken));
uint256 vaultBalanceAfter = loanToken.balanceOf(address(vault));

assertEq(vaultBalanceAfter, idle, "vaultBalanceAfter");
Expand All @@ -96,8 +96,8 @@ contract UrdTest is IntegrationTest {
);
}

function testTransferRewardsZeroAddress() public {
function testSkimZeroAddress() public {
vm.expectRevert(ErrorsLib.ZeroAddress.selector);
vault.transferRewards(address(loanToken));
vault.skim(address(loanToken));
}
}

0 comments on commit 77fbb34

Please sign in to comment.