-
Notifications
You must be signed in to change notification settings - Fork 95
Upgrade Morpho strategies for MORPHO rewards and deploy new OUSD v2 strategy #2716
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
base: master
Are you sure you want to change the base?
Changes from all commits
4a6b9ef
644f6e5
2dbd89f
91c6feb
f755491
309f2ef
87533ef
a2fdecb
c614298
1f9f018
4d1ccec
47dbea9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| // SPDX-License-Identifier: BUSL-1.1 | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| interface IDistributor { | ||
| event Claimed(address indexed user, address indexed token, uint256 amount); | ||
|
|
||
| function claim( | ||
| address[] calldata users, | ||
| address[] calldata tokens, | ||
| uint256[] calldata amounts, | ||
| bytes32[][] calldata proofs | ||
| ) external; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| const addresses = require("../../utils/addresses"); | ||
| const { deploymentWithGovernanceProposal } = require("../../utils/deploy"); | ||
|
|
||
| module.exports = deploymentWithGovernanceProposal( | ||
| { | ||
| deployName: "160_upgrade_morpho_strategies", | ||
| forceDeploy: false, | ||
| // forceSkip: true, | ||
| // reduceQueueTime: true, | ||
| deployerIsProposer: false, | ||
| // proposalId: "", | ||
| }, | ||
| async ({ deployWithConfirmation, getTxOpts, withConfirmation }) => { | ||
| // Current OUSD Vault contracts | ||
| const cVaultProxy = await ethers.getContract("VaultProxy"); | ||
| const cVaultAdmin = await ethers.getContractAt( | ||
| "VaultAdmin", | ||
| cVaultProxy.address | ||
| ); | ||
| const dMorphoSteakhouseUSDCStrategyProxy = await ethers.getContract( | ||
| "MetaMorphoStrategyProxy" | ||
| ); | ||
| const dMorphoGauntletPrimeUSDCStrategyProxy = await ethers.getContract( | ||
| "MorphoGauntletPrimeUSDCStrategyProxy" | ||
| ); | ||
| const dMorphoGauntletPrimeUSDTStrategyProxy = await ethers.getContract( | ||
| "MorphoGauntletPrimeUSDTStrategyProxy" | ||
| ); | ||
|
|
||
| // Deployer Actions | ||
| // ---------------- | ||
|
|
||
| // Fix the signer to the deployer of the Morpho OUSD v2 strategy proxy | ||
| const sDeployer = await ethers.provider.getSigner( | ||
| "0x58890A9cB27586E83Cb51d2d26bbE18a1a647245" | ||
| ); | ||
|
|
||
| // 1. Deploy new contract for Morpho Steakhouse USDC | ||
| const dMorphoSteakhouseUSDCStrategyImpl = await deployWithConfirmation( | ||
| "Generalized4626Strategy", | ||
| [ | ||
| [addresses.mainnet.MorphoSteakhouseUSDCVault, cVaultProxy.address], | ||
| addresses.mainnet.USDC, | ||
| ] | ||
| ); | ||
|
|
||
| // 2. Deploy new contract for Morpho Gauntlet Prime USDC | ||
| const dMorphoGauntletPrimeUSDCStrategyImpl = await deployWithConfirmation( | ||
| "Generalized4626Strategy", | ||
| [ | ||
| [addresses.mainnet.MorphoGauntletPrimeUSDCVault, cVaultProxy.address], | ||
| addresses.mainnet.USDC, | ||
| ] | ||
| ); | ||
|
|
||
| // 3. Deploy new contract for Morpho Gauntlet Prime USDT | ||
| const dMorphoGauntletPrimeUSDTStrategyImpl = await deployWithConfirmation( | ||
| "Generalized4626USDTStrategy", | ||
| [ | ||
| [addresses.mainnet.MorphoGauntletPrimeUSDTVault, cVaultProxy.address], | ||
| addresses.mainnet.USDT, | ||
| ] | ||
| ); | ||
|
|
||
| // 4. Get previously deployed proxy to Morpho OUSD v2 strategy | ||
| const cOUSDMorphoV2StrategyProxy = await ethers.getContract( | ||
| "OUSDMorphoV2StrategyProxy" | ||
| ); | ||
|
|
||
| // 5. Deploy new strategy for the Morpho Yearn OUSD V2 Vault | ||
| const dOUSDMorphoV2StrategyImpl = await deployWithConfirmation( | ||
| "Generalized4626Strategy", | ||
| [ | ||
| [addresses.mainnet.MorphoOUSDv2Vault, cVaultProxy.address], | ||
| addresses.mainnet.USDC, | ||
| ] | ||
| ); | ||
| const cOUSDMorphoV2Strategy = await ethers.getContractAt( | ||
| "Generalized4626Strategy", | ||
| cOUSDMorphoV2StrategyProxy.address | ||
| ); | ||
|
|
||
| // 6. Construct initialize call data to initialize and configure the new strategy | ||
| const initData = cOUSDMorphoV2Strategy.interface.encodeFunctionData( | ||
| "initialize()", | ||
| [] | ||
| ); | ||
|
|
||
| // 7. Init the proxy to point at the implementation, set the governor, and call initialize | ||
| const initFunction = "initialize(address,address,bytes)"; | ||
| await withConfirmation( | ||
| cOUSDMorphoV2StrategyProxy.connect(sDeployer)[initFunction]( | ||
| dOUSDMorphoV2StrategyImpl.address, | ||
| addresses.mainnet.Timelock, // governor | ||
| initData, // data for delegate call to the initialize function on the strategy | ||
| await getTxOpts() | ||
| ) | ||
| ); | ||
|
|
||
| // Governance Actions | ||
| // ---------------- | ||
| return { | ||
| name: `Upgrade Morpho Steakhouse and Gauntlet Prime Strategies to claim MORPHO rewards from Merkl. | ||
| Remove the Morpho Steakhouse Strategy. | ||
| Set the Morpho Gauntlet Prime USDC strategy as the default for USDC | ||
| Add new Morpho OUSD v2 Strategy and deposit 10k USDC. | ||
| `, | ||
| actions: [ | ||
| { | ||
| // 1. Upgrade Morpho Steakhouse USDC Strategy | ||
| contract: dMorphoSteakhouseUSDCStrategyProxy, | ||
| signature: "upgradeTo(address)", | ||
| args: [dMorphoSteakhouseUSDCStrategyImpl.address], | ||
| }, | ||
| { | ||
| // 2. Upgrade Morpho Gauntlet Prime USDC Strategy | ||
| contract: dMorphoGauntletPrimeUSDCStrategyProxy, | ||
| signature: "upgradeTo(address)", | ||
| args: [dMorphoGauntletPrimeUSDCStrategyImpl.address], | ||
| }, | ||
| { | ||
| // 3. Upgrade Morpho Gauntlet Prime USDT Strategy | ||
| contract: dMorphoGauntletPrimeUSDTStrategyProxy, | ||
| signature: "upgradeTo(address)", | ||
| args: [dMorphoGauntletPrimeUSDTStrategyImpl.address], | ||
| }, | ||
| { | ||
| // 4. Add the new Morpho OUSD v2 strategy to the vault | ||
| contract: cVaultAdmin, | ||
| signature: "approveStrategy(address)", | ||
| args: [cOUSDMorphoV2Strategy.address], | ||
| }, | ||
|
Comment on lines
+128
to
+132
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Should we also remove the steakhouse strategy here so that we don't need two governance proposals? The guardian can do a withdrawAll before executing the governance proposal (to ensure the proposal doesn't revert for any reason)
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Action 8 is Remove the Morpho Steakhouse strategy Note the Morpho Gauntlet Prime USDC strategy is left while the funds in the new Morpho OUSD v2 strategy is ramped up. Once all the USDC has been reallocated from the Morpho Gauntlet Prime USDC strategy to the new Morpho OUSD v2 strategy, the Morpho Gauntlet Prime USDC strategy can then be removed in a new gov proposal |
||
| { | ||
| // 5. Set the Harvester of the Morpho OUSD v2 strategy to the BuyBack Operator | ||
| contract: cOUSDMorphoV2Strategy, | ||
| signature: "setHarvesterAddress(address)", | ||
| args: [addresses.multichainBuybackOperator], | ||
| }, | ||
| { | ||
| // 6. Set the Morpho Gauntlet Prime USDC strategy as the default for USDC | ||
| contract: cVaultAdmin, | ||
| signature: "setAssetDefaultStrategy(address,address)", | ||
| args: [ | ||
| addresses.mainnet.USDC, | ||
| dMorphoGauntletPrimeUSDCStrategyProxy.address, | ||
| ], | ||
| }, | ||
| { | ||
| // 7. Remove the Morpho Steakhouse strategy | ||
| contract: cVaultAdmin, | ||
| signature: "removeStrategy(address)", | ||
| args: [dMorphoSteakhouseUSDCStrategyProxy.address], | ||
| }, | ||
| ], | ||
| }; | ||
| } | ||
| ); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| const axios = require("axios"); | ||
| const { formatUnits } = require("ethers/lib/utils"); | ||
|
|
||
| const { logTxDetails } = require("../utils/txLogger"); | ||
|
|
||
| const log = require("../utils/logger")("task:merkl"); | ||
|
|
||
| const MERKL_API_ENDPOINT = "https://api.merkl.xyz/v4"; | ||
|
|
||
| const getMerklRewards = async ({ userAddress, chainId = 1 }) => { | ||
| const url = `${MERKL_API_ENDPOINT}/users/${userAddress}/rewards?chainId=${chainId}`; | ||
| try { | ||
| log(`Getting Merkl rewards data from ${url}`); | ||
|
|
||
| const response = await axios.get(url); | ||
|
|
||
| if (response.data.length === 0 || response.data[0].rewards.length === 0) { | ||
| return { | ||
| amount: 0n, | ||
| token: null, | ||
| proofs: [], | ||
| }; | ||
| } | ||
|
|
||
| return { | ||
| amount: response.data[0].rewards[0].amount, | ||
| token: response.data[0].rewards[0].token.address, | ||
| proofs: response.data[0].rewards[0].proofs, | ||
| }; | ||
| } catch (err) { | ||
| if (err.response) { | ||
| console.error("Response data : ", err.response.data); | ||
| console.error("Response status: ", err.response.status); | ||
| console.error("Response status: ", err.response.statusText); | ||
| } | ||
| throw Error(`Call to Merkl API failed: ${err.message}`); | ||
| } | ||
| }; | ||
|
|
||
| async function claimMerklRewards(strategyAddress, signer) { | ||
| const result = await getMerklRewards({ | ||
| userAddress: strategyAddress, | ||
| chainId: 1, | ||
| }); | ||
|
|
||
| log( | ||
| `${formatUnits(result.amount, 18)} ${ | ||
| result.token | ||
| } rewards available to claim.` | ||
| ); | ||
|
|
||
| const strategy = await ethers.getContractAt( | ||
| "Generalized4626Strategy", | ||
| strategyAddress, | ||
| signer | ||
| ); | ||
|
|
||
| const tx = await strategy.merkleClaim( | ||
| result.token, | ||
| result.amount, | ||
| result.proofs | ||
| ); | ||
| await logTxDetails(tx, "merkleClaim"); | ||
| } | ||
|
|
||
| module.exports = { claimMerklRewards, getMerklRewards }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels to me that all these changes should fit into a new contract smth like 4626MerklStrategy.sol. There might be strategies where Generalized4626 would be used (e.g. for Yearn 3 vaults) that won't be using the Merkl rewards distribution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I originally did but then realised
Generalized4626USDTStrategywould also have to change it's inheritance. Given the addition was small, I decided to just addmerkleClaimtoGeneralized4626StrategyThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
got it 👍