Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VIP-14 Morpho PCV Deposit #128

Merged
merged 88 commits into from
Oct 27, 2022
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
5ba0def
add mock maple contract
ElliotFriedman Oct 2, 2022
344cbc0
Add harvest event to the PCV Deposit Interface
ElliotFriedman Oct 3, 2022
95368fe
Morpho PCV Deposit and integration tests
ElliotFriedman Oct 3, 2022
85e18d2
Maple PCV Deposit, harvest functionality, COMP Token
ElliotFriedman Oct 3, 2022
fc92e6b
Update comment in Compound PCV router to correctly label PCV deposit …
ElliotFriedman Oct 4, 2022
86ea05e
VIP-14 Integration Test Skeleton
ElliotFriedman Oct 4, 2022
43ac3fb
Update TODO PCV Guard Verification
ElliotFriedman Oct 4, 2022
c9ff1b1
VIP-14 Proposal, Deploys Router, Deploys Deposits, Deploys Oracle, Di…
ElliotFriedman Oct 4, 2022
38f7cdb
Add skeleton of VIP-14 integration tests
ElliotFriedman Oct 4, 2022
cf6c21a
VIP-14 typescript implementation
ElliotFriedman Oct 4, 2022
c0ddda9
Morpho router swap integration tests
ElliotFriedman Oct 4, 2022
96c3b93
add arbitrum proposal to set volt price increases to 0
ElliotFriedman Oct 4, 2022
09a11e5
Disable mint tests on arbitrum, pause minting on arbitrum
ElliotFriedman Oct 4, 2022
1b11bda
Pause minting on Arbitrum PSM's
ElliotFriedman Oct 4, 2022
3a927a5
remove newline in mintredeemverification
ElliotFriedman Oct 4, 2022
288fb42
VIP-14 Arbitrum deployment and governance scripts in typescript
ElliotFriedman Oct 4, 2022
6323a8e
remove vm.warp from morpho integration test
ElliotFriedman Oct 5, 2022
f86be20
Morpho and Maple PCV Deposit refactor and integration tests
ElliotFriedman Oct 5, 2022
d72ddea
assertEq
ElliotFriedman Oct 5, 2022
175b45c
add deposit call on maple deposit to vip 14
ElliotFriedman Oct 5, 2022
02453fa
remove arbitrum typescript deployment, arbitrary execution as pcv con…
ElliotFriedman Oct 6, 2022
64177bb
Add pcv deposit pausing
ElliotFriedman Oct 6, 2022
41b1496
update description with deployment steps
ElliotFriedman Oct 6, 2022
513d872
deployment script gov description fix
ElliotFriedman Oct 6, 2022
c231877
Clarifying comments Maple Deposit
ElliotFriedman Oct 6, 2022
72101fa
update maple deposit to accurately track interest and losses
ElliotFriedman Oct 7, 2022
979ce31
update comment
ElliotFriedman Oct 7, 2022
376e7d4
update comment pcv guardian ts
ElliotFriedman Oct 7, 2022
b3a1843
warn comment on maple deposit
ElliotFriedman Oct 7, 2022
101415e
add assertion
ElliotFriedman Oct 7, 2022
729fc01
pause old pcv deposits assertions
ElliotFriedman Oct 7, 2022
64a50dd
Remove new oracle deployment and depositing of pcv into maple
ElliotFriedman Oct 7, 2022
4c0e890
VIP-14 Integration tests
ElliotFriedman Oct 7, 2022
6619e94
typescript proposal update
ElliotFriedman Oct 7, 2022
21d0bf7
Add dev comments to Morpho and Maple PCV Deposits
ElliotFriedman Oct 11, 2022
1ce3129
license
ElliotFriedman Oct 11, 2022
248a693
import visual cleanup
ElliotFriedman Oct 11, 2022
97b9255
PCV Deposit harvest emits amount harvested
ElliotFriedman Oct 13, 2022
3358ab0
Add amount emitted from harvest in deposits
ElliotFriedman Oct 13, 2022
c56434f
attribution to MakerDAO multicall
ElliotFriedman Oct 13, 2022
649ec46
If rewards contract paused, do not stake
ElliotFriedman Oct 17, 2022
6de5056
Morpho 🦋 & Maple 🍁 Slither Run
ElliotFriedman Oct 18, 2022
e83c195
Update contracts/pcv/morpho/ILens.sol
ElliotFriedman Oct 18, 2022
6ca9cc5
Update contracts/pcv/morpho/ILens.sol
ElliotFriedman Oct 18, 2022
4999583
Update comment and constructor params to allow PCV deposit to hold ceth
ElliotFriedman Oct 18, 2022
d2a78e0
Merge branch 'feat/morpho-maple-rate-upgrade' of github.com:volt-prot…
ElliotFriedman Oct 18, 2022
cb8ccd0
fix integration tests and governance proposal
ElliotFriedman Oct 18, 2022
1c06df3
typescript deployment script upgraded to pass 3rd parameter into morp…
ElliotFriedman Oct 18, 2022
79a6981
remove pausing logic from maple smart contracts
ElliotFriedman Oct 18, 2022
91a817b
scalingFactor snakecase, emit withdrawal event in sad path api, add a…
ElliotFriedman Oct 18, 2022
8f6b3d6
remove withdrawAll function from maple pcv deposit
ElliotFriedman Oct 18, 2022
397642b
remove unnecessary cast
ElliotFriedman Oct 18, 2022
28a501a
merge develop and fix merge conflicts
ElliotFriedman Oct 19, 2022
a2fcf0a
remove Maple from PR
ElliotFriedman Oct 19, 2022
49e18cd
typescript vip-14 updates to only handling Morpho deposits
ElliotFriedman Oct 19, 2022
5a03f63
Merge branch 'develop' of github.com:volt-protocol/volt-protocol-core…
ElliotFriedman Oct 19, 2022
1a6b664
Update contracts/pcv/morpho/MorphoCompoundPCVDeposit.sol
ElliotFriedman Oct 19, 2022
a71e3b3
Update contracts/pcv/morpho/MorphoCompoundPCVDeposit.sol
ElliotFriedman Oct 19, 2022
bc52fa4
Update contracts/pcv/morpho/MorphoCompoundPCVDeposit.sol
ElliotFriedman Oct 19, 2022
a96025a
remove revert in mint redeem verification
ElliotFriedman Oct 19, 2022
2073f25
Merge branch 'feat/morpho-maple-rate-upgrade' of github.com:volt-prot…
ElliotFriedman Oct 19, 2022
024edd3
Add profit tracking to Morpho PCV Deposit, fuzz tests, security tests…
ElliotFriedman Oct 20, 2022
3056222
update ts deployment script
ElliotFriedman Oct 20, 2022
457613d
remove console.log
ElliotFriedman Oct 20, 2022
42d92da
update comments on how fast interest accrues at current rates, fix ex…
ElliotFriedman Oct 20, 2022
26be8a2
invariant tests Morpho
ElliotFriedman Oct 20, 2022
81cba10
add additional comments around depositedAmount
ElliotFriedman Oct 20, 2022
0a90a64
invariant tests for morpho compound pcv deposit
ElliotFriedman Oct 20, 2022
ab9c93e
fix unit tests assertions around events being emitted
ElliotFriedman Oct 20, 2022
1fc86aa
add underlying mismatch test
ElliotFriedman Oct 20, 2022
72a4567
slither run 10-21
ElliotFriedman Oct 21, 2022
c8ca5d7
add depositedAmount check equal 0 in recordPNL function
ElliotFriedman Oct 21, 2022
414b443
address Erwan feedback
ElliotFriedman Oct 21, 2022
4d59814
add vip14 to runner, remove vip14a
ElliotFriedman Oct 21, 2022
52f3ef6
change name of depositedAmount to lastRecordedBalance, update tests t…
ElliotFriedman Oct 21, 2022
d1e2632
update comments in MorphoPCVDeposit
ElliotFriedman Oct 21, 2022
513e3a9
add unit test that checks implcit balance check in _withdraw function
ElliotFriedman Oct 21, 2022
9c14d09
organize functions based on type and permission, add large block comm…
ElliotFriedman Oct 21, 2022
072b88d
slither 22 run
ElliotFriedman Oct 22, 2022
a1e9b2f
Add PCV Oracle callback to morpho PCV Deposit
ElliotFriedman Oct 24, 2022
8519129
update deposit endingRecordedBalance to be exact amount pcv deposit o…
ElliotFriedman Oct 24, 2022
d7c9f6c
remove amount == 0 check in _withdraw, setPCVOracle records pnl and u…
ElliotFriedman Oct 24, 2022
ab72cd2
add unit/fuzz tests around calling setPCVOracle
ElliotFriedman Oct 24, 2022
6cd4317
add additional invariant tests for updateLiquidBalance hook
ElliotFriedman Oct 24, 2022
d2c285e
slither run 10/24
ElliotFriedman Oct 25, 2022
7fa0d40
Deploy MorphoCompound PCV Depsosits, add addresses to mainnet address…
ElliotFriedman Oct 25, 2022
0101e8d
remove compound pcv deposits from pcv guard verification
ElliotFriedman Oct 25, 2022
f867262
remove todo comment on adding morpho deposits in pcv guard verification
ElliotFriedman Oct 25, 2022
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
9 changes: 9 additions & 0 deletions contracts/mock/MockMaplePool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pragma solidity =0.8.13;

contract MockMaplePool {
address public liquidityAsset;

constructor(address _liquidityAsset) {
liquidityAsset = _liquidityAsset;
}
}
3 changes: 3 additions & 0 deletions contracts/pcv/IPCVDeposit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "./IPCVDepositBalances.sol";
/// @author Fei Protocol
interface IPCVDeposit is IPCVDepositBalances {
// ----------- Events -----------

event Deposit(address indexed _from, uint256 _amount);

event Withdrawal(
Expand All @@ -28,6 +29,8 @@ interface IPCVDeposit is IPCVDepositBalances {
uint256 _amount
);

event Harvest();

// ----------- State changing api -----------

function deposit() external;
Expand Down
4 changes: 2 additions & 2 deletions contracts/pcv/compound/CompoundPCVRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ import {IPegStabilityModule} from "../../peg/IPegStabilityModule.sol";
contract CompoundPCVRouter is CoreRef {
using SafeERC20 for IERC20;

/// @notice reference to the Compound PCV deposit for USDC
/// @notice reference to the Compound PCV deposit for DAI
PCVDeposit public immutable daiPcvDeposit;

/// @notice reference to the Compound PCV deposit for DAI
/// @notice reference to the Compound PCV deposit for USDC
PCVDeposit public immutable usdcPcvDeposit;

/// @notice reference to the Maker DAI-USDC PSM that this router interacts with
Expand Down
58 changes: 58 additions & 0 deletions contracts/pcv/maple/IMplRewards.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity =0.8.13;

interface IMplRewards {
// Views
function rewardsToken() external view returns (address);

function stakingToken() external view returns (address);

function periodFinish() external view returns (uint256);

function rewardRate() external view returns (uint256);

function rewardsDuration() external view returns (uint256);

function lastUpdateTime() external view returns (uint256);

function rewardPerTokenStored() external view returns (uint256);

function lastPauseTime() external view returns (uint256);

function paused() external view returns (bool);

function userRewardPerTokenPaid(address) external view returns (uint256);

function rewards(address) external view returns (uint256);

function totalSupply() external view returns (uint256);

function balanceOf(address) external view returns (uint256);

function lastTimeRewardApplicable() external view returns (uint256);

function rewardPerToken() external view returns (uint256);

function earned(address) external view returns (uint256);

function getRewardForDuration() external view returns (uint256);

// Mutative
function stake(uint256) external;

function withdraw(uint256) external;

function getReward() external;

function exit() external;

function notifyRewardAmount(uint256) external;

function updatePeriodFinish(uint256) external;

function recoverERC20(address, uint256) external;

function setRewardsDuration(uint256) external;

function setPaused(bool) external;
}
93 changes: 93 additions & 0 deletions contracts/pcv/maple/IPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity =0.8.13;

/// @notice maple pool interface
interface IPool {
function balanceOf(address) external view returns (uint256);

/**
@dev Returns the amount of funds that an account has earned in total.
@dev accumulativeFundsOf(_owner) = withdrawableFundsOf(_owner) + withdrawnFundsOf(_owner)
= (pointsPerShare * balanceOf(_owner) + pointsCorrection[_owner]) / pointsMultiplier
@param _owner The address of a token holder.
@return The amount of funds that `_owner` has earned in total.
*/
function accumulativeFundsOf(address _owner)
external
view
returns (uint256);

/**
@dev Returns the total amount of funds a given address is able to withdraw currently.
@param owner Address of FDT holder.
@return A uint256 representing the available funds for a given account.
*/
function withdrawableFundsOf(address owner) external view returns (uint256);

/**
@dev Handles Liquidity Providers depositing of Liquidity Asset into the LiquidityLocker, minting PoolFDTs.
@dev It emits a `DepositDateUpdated` event.
@dev It emits a `BalanceUpdated` event.
@dev It emits a `Cooldown` event.
@param amt Amount of Liquidity Asset to deposit.
*/
function deposit(uint256 amt) external;

function increaseCustodyAllowance(address, uint256) external;

function poolState() external view returns (uint256);

function claim(address, address) external returns (uint256[7] memory);

function fundLoan(
address,
address,
uint256
) external;

/**
@dev Activates the cooldown period to withdraw. It can't be called if the account is not providing liquidity.
@dev It emits a `Cooldown` event.
*/
function intendToWithdraw() external;

/**
@dev Handles Liquidity Providers withdrawing of Liquidity Asset from the LiquidityLocker, burning PoolFDTs.
@dev It emits two `BalanceUpdated` event.
@param amt Amount of Liquidity Asset to withdraw.
*/
function withdraw(uint256 amt) external;

/**
@dev Withdraws all available funds for a FDT holder.
*/
function withdrawFunds() external;

function liquidityAsset() external view returns (address);

function liquidityLocker() external view returns (address);

function stakeAsset() external view returns (address);

function stakeLocker() external view returns (address);

function stakingFee() external view returns (uint256);

function principalOut() external view returns (uint256);

function liquidityCap() external view returns (uint256);

function lockupPeriod() external view returns (uint256);

function depositDate(address) external view returns (uint256);

function debtLockers(address, address) external view returns (address);

function withdrawCooldown(address) external view returns (uint256);

function setLiquidityCap(uint256) external;

function cancelWithdraw() external;

function isDepositAllowed(uint256) external view returns (bool);
}
151 changes: 151 additions & 0 deletions contracts/pcv/maple/MaplePCVDeposit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
pragma solidity =0.8.13;

import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import {IPool} from "./IPool.sol";
import {IMplRewards} from "./IMplRewards.sol";
import {CoreRef} from "../../refs/CoreRef.sol";
import {PCVDeposit} from "../PCVDeposit.sol";

/// @notice PCV Deposit for Maple
/// Allows depositing only by privileged role to prevent lockup period being extended by griefers

/// Can only deposit USDC in this MAPLE PCV deposit
ElliotFriedman marked this conversation as resolved.
Show resolved Hide resolved
contract MaplePCVDeposit is PCVDeposit {
using SafeERC20 for IERC20;

/// @notice reference to the Maple Pool where deposits and withdraws will originate
IPool public immutable pool;

/// @notice reference to the Maple Staking Rewards Contract
IMplRewards public immutable mplRewards;

/// @notice reference to the underlying token
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// @notice reference to the underlying token
/// @notice reference to the underlying token (USDC, Mainnet)

IERC20 public immutable token;

/// @notice scaling factor for USDC
/// @dev hardcoded to use USDC decimals as this is the only
/// supplied asset Volt Protocol will support
uint256 public constant scalingFactor = 1e12;

/// @notice fetch underlying asset by calling pool and getting liquidity asset
/// @param _core reference to the Core contract
/// @param _pool reference to the Maple Pool contract
ElliotFriedman marked this conversation as resolved.
Show resolved Hide resolved
constructor(
address _core,
address _pool,
address _mplRewards
) CoreRef(_core) {
token = IERC20(IPool(_pool).liquidityAsset());
/// enforce underlying token is USDC
require(
address(token) == 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48,
"MaplePCVDeposit: Underlying not USDC"
);
pool = IPool(_pool);
mplRewards = IMplRewards(_mplRewards);
}

/// @notice return the amount of funds this contract owns in Maple FDT's
/// without accounting for interest earned
/// does not account for unrealized losses in the venue
function balance() public view override returns (uint256) {
return pool.balanceOf(address(this)) / scalingFactor;
ElliotFriedman marked this conversation as resolved.
Show resolved Hide resolved
}

/// @notice return the underlying token denomination for this deposit
function balanceReportedIn() external view returns (address) {
return address(token);
}

/// @notice deposit PCV into Maple.
/// all deposits are subject to a minimum 90 day lockup,
/// no op if 0 token balance
/// deposits are then immediately staked to accrue MPL rewards
/// only pcv controller can deposit, as this contract would be vulnerable
/// to donation / griefing attacks if anyone could call deposit and extend lockup time
function deposit() external onlyPCVController {
ElliotFriedman marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would gate this to a new role, for instance keccak256(MAPLE_DEPOSIT_ROLE), to avoid unintended interactions from the ERC20Allocator / PCVGuardian that are PCV Controllers

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ERC20Allocator is not going to be connected to this deposit, and PCVGuardian cannot call deposit, only withdraw.

uint256 amount = IERC20(token).balanceOf(address(this));
if (amount == 0) {
/// no op to prevent wasted gas
return;
}

/// pool deposit
token.approve(address(pool), amount);
pool.deposit(amount);

/// stake pool FDT for MPL rewards
uint256 scaledDepositAmount = amount * scalingFactor;
pool.increaseCustodyAllowance(address(mplRewards), scaledDepositAmount);
mplRewards.stake(scaledDepositAmount);
ElliotFriedman marked this conversation as resolved.
Show resolved Hide resolved
ElliotFriedman marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any idea why they have this custody locking mechanism ? Seems to me that it would be simpler to just transfer the erc20 tokens, it works but it's a weird pattern?

Copy link
Collaborator Author

@ElliotFriedman ElliotFriedman Oct 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's likely due to the token lockup period where you can't transfer during lockup.


emit Deposit(msg.sender, amount);
}

/// @notice function to start the cooldown process to withdraw
/// 1. lp lockup on deposit --> 90 days locked up and can't withdraw
/// 2. cool down period, call intend to withdraw -->
/// must wait 10 days before withdraw after calling intend to withdraw function
/// 3. after cool down and past the lockup period,
/// have 2 days to withdraw before cool down period restarts.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's interestingly similar to AAVE/stkAAVE

function signalIntentToWithdraw() external onlyPCVController {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd gate this to a specific keccak256(MAPLE_WITHDRAW_ROLE) as starting the cooldown is low severity, this could be granted to the team multisig for instance, and then a full governance timelock is needed to call the actual withdraw because it would be gated to PCV Controller (or go through guardian)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a more mature system state, I would agree that adding additional roles and separating concerns makes sense.

pool.intendToWithdraw();
}

/// @notice function to cancel a withdraw
/// should only be used to allow a transfer when doing a withdrawERC20 call
function cancelWithdraw() external onlyPCVController {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also gate this to keccak256(MAPLE_WITHDRAW_ROLE)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not like the idea of adding a new role here. It adds complexity and additional cognitive overhead when trying to reason about which addresses are allowed to act on protocol components. Adding roles that are this contract specific creates a multiplicative explosion of new roles.

pool.cancelWithdraw();
}

/// @notice withdraw PCV from Maple, only callable by PCV controller
/// @param to destination after funds are withdrawn from venue
/// @param amount of PCV to withdraw from the venue
ElliotFriedman marked this conversation as resolved.
Show resolved Hide resolved
function withdraw(address to, uint256 amount)
external
override
onlyPCVController
{
uint256 scaledWithdrawAmount = amount * scalingFactor;

mplRewards.getReward(); /// get MPL rewards
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

either remove this from withdraw() or emit the Harvest event here too, or it could break accounting from events

/// this call will withdraw amount of principal requested, and then send
/// over any accrued interest.
/// expected behavior is that this contract
/// receives either amount of USDC, or amount of USDC + interest accrued
/// if lending losses were taken, receive less than amount
mplRewards.withdraw(scaledWithdrawAmount); /// decreases allowance
ElliotFriedman marked this conversation as resolved.
Show resolved Hide resolved

/// withdraw from the pool
pool.withdraw(amount);
token.safeTransfer(to, amount);

emit Withdrawal(msg.sender, to, amount);
}

ElliotFriedman marked this conversation as resolved.
Show resolved Hide resolved
/// @notice withdraw all PCV from Maple
ElliotFriedman marked this conversation as resolved.
Show resolved Hide resolved
function withdrawAll(address to) external onlyPCVController {
uint256 amount = balance();
mplRewards.exit(); /// unstakes from Maple reward contract and claims rewards
/// this call will withdraw all principal,
/// then send over any accrued interest.
/// expected behavior is that this contract
/// receives balance amount of USDC, or amount of USDC + interest accrued
/// if lending losses were taken, receive less than amount
pool.withdraw(amount); /// call pool and withdraw entire balance

uint256 tokenBalance = IERC20(token).balanceOf(address(this));
token.safeTransfer(to, tokenBalance);

emit Withdrawal(msg.sender, to, tokenBalance);
}

/// permissionless function to harvest rewards before withdraw
function harvest() external {
mplRewards.getReward();
ElliotFriedman marked this conversation as resolved.
Show resolved Hide resolved

emit Harvest();
}
ElliotFriedman marked this conversation as resolved.
Show resolved Hide resolved
}
Loading