/
SupplyVault.sol
123 lines (98 loc) · 4.24 KB
/
SupplyVault.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;
import "@solmate/utils/FixedPointMathLib.sol";
import "./SupplyVaultUpgradeable.sol";
/// @title SupplyVault.
/// @author Morpho Labs.
/// @custom:contact security@morpho.xyz
/// @notice ERC4626-upgradeable Tokenized Vault implementation for Morpho-Compound, which tracks rewards from Compound's pool accrued by its users.
contract SupplyVault is SupplyVaultUpgradeable {
using FixedPointMathLib for uint256;
using SafeTransferLib for ERC20;
/// EVENTS ///
/// @notice Emitted when a user accrues its rewards.
/// @param user The address of the user.
/// @param index The new index of the user (also the global at the moment of the update).
/// @param unclaimed The new unclaimed amount of the user.
event Accrued(address indexed user, uint256 index, uint256 unclaimed);
/// @notice Emitted when a user claims its rewards.
/// @param user The address of the user.
/// @param claimed The amount of rewards claimed.
event Claimed(address indexed user, uint256 claimed);
/// STORAGE ///
struct UserRewards {
uint128 index; // User index for the reward token.
uint128 unclaimed; // User's unclaimed rewards.
}
uint256 public rewardsIndex; // The vault's rewards index.
mapping(address => UserRewards) public userRewards; // The rewards index of a user, used to track rewards accrued.
/// UPGRADE ///
/// @notice Initializes the vault.
/// @param _morpho The address of the main Morpho contract.
/// @param _poolToken The address of the pool token corresponding to the market to supply through this vault.
/// @param _name The name of the ERC20 token associated to this tokenized vault.
/// @param _symbol The symbol of the ERC20 token associated to this tokenized vault.
/// @param _initialDeposit The amount of the initial deposit used to prevent pricePerShare manipulation.
function initialize(
address _morpho,
address _poolToken,
string calldata _name,
string calldata _symbol,
uint256 _initialDeposit
) external initializer {
__SupplyVaultUpgradeable_init(_morpho, _poolToken, _name, _symbol, _initialDeposit);
}
/// EXTERNAL ///
/// @notice Claims rewards on behalf of `_user`.
/// @param _user The address of the user to claim rewards for.
/// @return rewardsAmount The amount of rewards claimed.
function claimRewards(address _user) external returns (uint256 rewardsAmount) {
_accrueUnclaimedRewards(_user);
rewardsAmount = userRewards[_user].unclaimed;
if (rewardsAmount > 0) {
userRewards[_user].unclaimed = 0;
comp.safeTransfer(_user, rewardsAmount);
}
emit Claimed(_user, rewardsAmount);
}
/// INTERNAL ///
function _deposit(
address _caller,
address _receiver,
uint256 _assets,
uint256 _shares
) internal virtual override {
_accrueUnclaimedRewards(_receiver);
super._deposit(_caller, _receiver, _assets, _shares);
}
function _withdraw(
address _caller,
address _receiver,
address _owner,
uint256 _assets,
uint256 _shares
) internal virtual override {
_accrueUnclaimedRewards(_receiver);
super._withdraw(_caller, _receiver, _owner, _assets, _shares);
}
function _accrueUnclaimedRewards(address _user) internal {
uint256 supply = totalSupply();
uint256 rewardsIndexMem = rewardsIndex;
if (supply > 0) {
address[] memory poolTokens = new address[](1);
poolTokens[0] = poolToken;
rewardsIndexMem += morpho.claimRewards(poolTokens, false).divWadDown(supply);
}
rewardsIndex = rewardsIndexMem;
uint256 rewardsIndexDiff = rewardsIndexMem - userRewards[_user].index;
uint256 unclaimed;
if (rewardsIndexDiff > 0) {
unclaimed =
userRewards[_user].unclaimed +
uint128(balanceOf(_user).mulWadDown(rewardsIndexDiff));
userRewards[_user].unclaimed = uint128(unclaimed);
}
userRewards[_user].index = uint128(rewardsIndexMem);
emit Accrued(_user, rewardsIndexMem, unclaimed);
}
}