Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/solmate
37 changes: 14 additions & 23 deletions src/utils/MultiRewardStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,7 @@ contract MultiRewardStaking is ERC4626Upgradeable, OwnedUpgradeable {
* @param _escrow An optional escrow contract which can be used to lock rewards on claim.
* @param _owner Owner of the contract. Controls management functions.
*/
function initialize(
IERC20 _stakingToken,
IMultiRewardEscrow _escrow,
address _owner
) external initializer {
function initialize(IERC20 _stakingToken, IMultiRewardEscrow _escrow, address _owner) external initializer {
__ERC4626_init(IERC20Metadata(address(_stakingToken)));
__Owned_init(_owner);

Expand Down Expand Up @@ -134,11 +130,7 @@ contract MultiRewardStaking is ERC4626Upgradeable, OwnedUpgradeable {
}

/// @notice Internal transfer function used by `transfer()` and `transferFrom()`. Accrues rewards for `from` and `to`.
function _transfer(
address from,
address to,
uint256 amount
) internal override accrueRewards(from, to) {
function _transfer(address from, address to, uint256 amount) internal override accrueRewards(from, to) {
if (from == address(0) || to == address(0)) revert ZeroAddressTransfer(from, to);

uint256 fromBalance = balanceOf(from);
Expand Down Expand Up @@ -188,12 +180,7 @@ contract MultiRewardStaking is ERC4626Upgradeable, OwnedUpgradeable {
}

/// @notice Locks a percentage of a reward in an escrow contract. Pays out the rest to the user.
function _lockToken(
address user,
IERC20 rewardToken,
uint256 rewardAmount,
EscrowInfo memory escrowInfo
) internal {
function _lockToken(address user, IERC20 rewardToken, uint256 rewardAmount, EscrowInfo memory escrowInfo) internal {
uint256 escrowed = rewardAmount.mulDiv(uint256(escrowInfo.escrowPercentage), 1e18, Math.Rounding.Down);
uint256 payout = rewardAmount - escrowed;

Expand Down Expand Up @@ -258,7 +245,7 @@ contract MultiRewardStaking is ERC4626Upgradeable, OwnedUpgradeable {
if (rewards.lastUpdatedTimestamp > 0) revert RewardTokenAlreadyExist(rewardToken);

if (amount > 0) {
if (rewardsPerSecond == 0) revert ZeroRewardsSpeed();
if (rewardsPerSecond == 0 && totalSupply() == 0) revert InvalidConfig();
rewardToken.safeTransferFrom(msg.sender, address(this), amount);
}

Expand All @@ -275,7 +262,7 @@ contract MultiRewardStaking is ERC4626Upgradeable, OwnedUpgradeable {
rewardToken.safeApprove(address(escrow), type(uint256).max);
}

uint64 ONE = (10**IERC20Metadata(address(rewardToken)).decimals()).safeCastTo64();
uint64 ONE = (10 ** IERC20Metadata(address(rewardToken)).decimals()).safeCastTo64();
uint32 rewardsEndTimestamp = rewardsPerSecond == 0
? block.timestamp.safeCastTo32()
: _calcRewardsEnd(0, rewardsPerSecond, amount);
Expand Down Expand Up @@ -328,6 +315,8 @@ contract MultiRewardStaking is ERC4626Upgradeable, OwnedUpgradeable {
// Cache RewardInfo
RewardInfo memory rewards = rewardInfos[rewardToken];

if (rewards.rewardsPerSecond == 0 && totalSupply() == 0) revert InvalidConfig();

// Make sure that the reward exists
if (rewards.lastUpdatedTimestamp == 0) revert RewardTokenDoesntExist(rewardToken);

Expand All @@ -344,8 +333,6 @@ contract MultiRewardStaking is ERC4626Upgradeable, OwnedUpgradeable {
rewardInfos[rewardToken].rewardsEndTimestamp = rewardsEndTimestamp;
}

rewardInfos[rewardToken].lastUpdatedTimestamp = block.timestamp.safeCastTo32();

emit RewardInfoUpdate(rewardToken, rewards.rewardsPerSecond, rewardsEndTimestamp);
}

Expand Down Expand Up @@ -402,9 +389,11 @@ contract MultiRewardStaking is ERC4626Upgradeable, OwnedUpgradeable {
/// @notice Accrue global rewards for a rewardToken
function _accrueRewards(IERC20 _rewardToken, uint256 accrued) internal {
uint256 supplyTokens = totalSupply();
uint224 deltaIndex;
uint224 deltaIndex; // DeltaIndex is the amount of rewardsToken paid out per stakeToken
if (supplyTokens != 0)
deltaIndex = accrued.mulDiv(uint256(10**decimals()), supplyTokens, Math.Rounding.Down).safeCastTo224();
deltaIndex = accrued.mulDiv(uint256(10 ** decimals()), supplyTokens, Math.Rounding.Down).safeCastTo224();
// rewardDecimals * stakeDecimals / stakeDecimals = rewardDecimals
// 1e18 * 1e6 / 10e6 = 0.1e18 | 1e6 * 1e18 / 10e18 = 0.1e6

rewardInfos[_rewardToken].index += deltaIndex;
rewardInfos[_rewardToken].lastUpdatedTimestamp = block.timestamp.safeCastTo32();
Expand All @@ -425,7 +414,9 @@ contract MultiRewardStaking is ERC4626Upgradeable, OwnedUpgradeable {
uint256 deltaIndex = rewards.index - oldIndex;

// Accumulate rewards by multiplying user tokens by rewardsPerToken index and adding on unclaimed
uint256 supplierDelta = balanceOf(_user).mulDiv(deltaIndex, uint256(rewards.ONE), Math.Rounding.Down);
uint256 supplierDelta = balanceOf(_user).mulDiv(deltaIndex, uint256(10 ** decimals()), Math.Rounding.Down);
// stakeDecimals * rewardDecimals / stakeDecimals = rewardDecimals
// 1e18 * 1e6 / 10e18 = 0.1e18 | 1e6 * 1e18 / 10e18 = 0.1e6

userIndex[_user][_rewardToken] = rewards.index;

Expand Down
16 changes: 16 additions & 0 deletions src/vault/FeeRecipientProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,42 @@ contract FeeRecipientProxy is Owned {

uint256 public approvals;

event TokenApproved(uint8 len);
event TokenApprovalVoided(uint8 len);

error TokenAlreadyApproved(IERC20 token);
error TokenApprovalAlreadyVoided(IERC20 token);

function approveToken(IERC20[] calldata tokens) external onlyOwner {
uint8 len = uint8(tokens.length);
for (uint8 i = 0; i < len; i++) {
if (tokens[i].allowance(address(this), owner) > 0) revert TokenAlreadyApproved(tokens[i]);

tokens[i].approve(owner, type(uint256).max);
approvals++;
}

emit TokenApproved(len);
}

function voidTokenApproval(IERC20[] calldata tokens) external onlyOwner {
uint8 len = uint8(tokens.length);
for (uint8 i = 0; i < len; i++) {
if (tokens[i].allowance(address(this), owner) == 0) revert TokenApprovalAlreadyVoided(tokens[i]);

tokens[i].approve(owner, 0);
approvals--;
}

emit TokenApprovalVoided(len);
}

function acceptOwnership() external override {
require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
require(approvals == 0, "Must void all approvals first");

emit OwnerChanged(owner, nominatedOwner);

owner = nominatedOwner;
nominatedOwner = address(0);
}
Expand Down
9 changes: 3 additions & 6 deletions src/vault/TemplateRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,12 @@ contract TemplateRegistry is Owned {
}

/**
* @notice Adds a new template to the registry. Caller must be owner. (`DeploymentController`)
* @notice Adds a new template to the registry.
* @param templateCategory TemplateCategory of the new template.
* @param templateId Unique TemplateId of the new template.
* @param template Contains the implementation address and necessary informations to clone the implementation.
*/
function addTemplate(
bytes32 templateCategory,
bytes32 templateId,
Template memory template
) external onlyOwner {
function addTemplate(bytes32 templateCategory, bytes32 templateId, Template memory template) external onlyOwner {
if (!templateCategoryExists[templateCategory]) revert KeyNotFound(templateCategory);
if (templateExists[templateId]) revert TemplateExists(templateId);

Expand Down Expand Up @@ -100,6 +96,7 @@ contract TemplateRegistry is Owned {
* @dev Only the DAO can endorse templates via `VaultController`.
*/
function toggleTemplateEndorsement(bytes32 templateCategory, bytes32 templateId) external onlyOwner {
if (!templateCategoryExists[templateCategory]) revert KeyNotFound(templateCategory);
if (!templateExists[templateId]) revert KeyNotFound(templateId);

bool oldEndorsement = templates[templateCategory][templateId].endorsed;
Expand Down
Loading