From 4a6b9ef2ae85c41401653f1c2874559d5110448d Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Tue, 16 Dec 2025 16:13:47 +1100 Subject: [PATCH 01/11] Morpho strategies can now claim MORPHO rewards using Merkl --- contracts/contracts/interfaces/IMerkl.sol | 13 +++ .../strategies/Generalized4626Strategy.sol | 33 ++++++++ .../mainnet/159_upgrade_morpho_strategies.js | 82 +++++++++++++++++++ contracts/tasks/merkl.js | 56 +++++++++++++ contracts/tasks/tasks.js | 25 ++++++ ...o-guantlet-prime-usdc.mainnet.fork-test.js | 55 ++++++++++++- 6 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 contracts/contracts/interfaces/IMerkl.sol create mode 100644 contracts/deploy/mainnet/159_upgrade_morpho_strategies.js create mode 100644 contracts/tasks/merkl.js diff --git a/contracts/contracts/interfaces/IMerkl.sol b/contracts/contracts/interfaces/IMerkl.sol new file mode 100644 index 0000000000..adb4a66eda --- /dev/null +++ b/contracts/contracts/interfaces/IMerkl.sol @@ -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; +} diff --git a/contracts/contracts/strategies/Generalized4626Strategy.sol b/contracts/contracts/strategies/Generalized4626Strategy.sol index 1e5d850740..0695a6fc0c 100644 --- a/contracts/contracts/strategies/Generalized4626Strategy.sol +++ b/contracts/contracts/strategies/Generalized4626Strategy.sol @@ -8,8 +8,13 @@ pragma solidity ^0.8.0; */ import { IERC4626 } from "../../lib/openzeppelin/interfaces/IERC4626.sol"; import { IERC20, InitializableAbstractStrategy } from "../utils/InitializableAbstractStrategy.sol"; +import { IDistributor } from "../interfaces/IMerkl.sol"; contract Generalized4626Strategy is InitializableAbstractStrategy { + /// @notice The address of the Merkle Distributor contract. + IDistributor public constant merkleDistributor = + IDistributor(0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae); + /// @dev Replaced with an immutable variable // slither-disable-next-line constable-states address private _deprecate_shareToken; @@ -23,6 +28,8 @@ contract Generalized4626Strategy is InitializableAbstractStrategy { // For future use uint256[50] private __gap; + event ClaimedRewards(address indexed token, uint256 amount); + /** * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI, * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy @@ -210,4 +217,30 @@ contract Generalized4626Strategy is InitializableAbstractStrategy { function removePToken(uint256) external override onlyGovernor { revert("unsupported function"); } + + /// @notice Claim tokens from the Merkle Distributor + /// @param token The address of the token to claim. + /// @param amount The amount of tokens to claim. + /// @param proof The Merkle proof to validate the claim. + function merkleClaim( + address token, + uint256 amount, + bytes32[] calldata proof + ) external { + address[] memory users = new address[](1); + users[0] = address(this); + + address[] memory tokens = new address[](1); + tokens[0] = token; + + uint256[] memory amounts = new uint256[](1); + amounts[0] = amount; + + bytes32[][] memory proofs = new bytes32[][](1); + proofs[0] = proof; + + merkleDistributor.claim(users, tokens, amounts, proofs); + + emit ClaimedRewards(token, amount); + } } diff --git a/contracts/deploy/mainnet/159_upgrade_morpho_strategies.js b/contracts/deploy/mainnet/159_upgrade_morpho_strategies.js new file mode 100644 index 0000000000..dff1e33fc9 --- /dev/null +++ b/contracts/deploy/mainnet/159_upgrade_morpho_strategies.js @@ -0,0 +1,82 @@ +const addresses = require("../../utils/addresses"); +const { deploymentWithGovernanceProposal } = require("../../utils/deploy"); + +module.exports = deploymentWithGovernanceProposal( + { + deployName: "159_upgrade_morpho_strategies", + forceDeploy: false, + // forceSkip: true, + // reduceQueueTime: true, + deployerIsProposer: false, + // proposalId: "", + }, + async ({ deployWithConfirmation }) => { + // Current OUSD Vault contracts + const cVaultProxy = await ethers.getContract("VaultProxy"); + const dMorphoSteakhouseUSDCStrategyProxy = await ethers.getContract( + "MetaMorphoStrategyProxy" + ); + const dMorphoGauntletPrimeUSDCStrategyProxy = await ethers.getContract( + "MorphoGauntletPrimeUSDCStrategyProxy" + ); + const dMorphoGauntletPrimeUSDTStrategyProxy = await ethers.getContract( + "MorphoGauntletPrimeUSDTStrategyProxy" + ); + + // Deployer Actions + // ---------------- + + // 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, + ] + ); + + // Governance Actions + // ---------------- + return { + name: "Upgrade Morpho Steakhouse and Gauntlet Prime Strategies to claim MORPHO rewards from Merkl", + actions: [ + { + // 1. Upgrade Morpho Steakhouse USDC Strategy + contract: dMorphoSteakhouseUSDCStrategyProxy, + signature: "upgradeTo(address)", + args: [dMorphoSteakhouseUSDCStrategyImpl.address], + }, + { + // 1. Upgrade Morpho Steakhouse USDC Strategy + contract: dMorphoGauntletPrimeUSDCStrategyProxy, + signature: "upgradeTo(address)", + args: [dMorphoGauntletPrimeUSDCStrategyImpl.address], + }, + { + // 1. Upgrade Morpho Steakhouse USDC Strategy + contract: dMorphoGauntletPrimeUSDTStrategyProxy, + signature: "upgradeTo(address)", + args: [dMorphoGauntletPrimeUSDTStrategyImpl.address], + }, + ], + }; + } +); diff --git a/contracts/tasks/merkl.js b/contracts/tasks/merkl.js new file mode 100644 index 0000000000..e83dc53041 --- /dev/null +++ b/contracts/tasks/merkl.js @@ -0,0 +1,56 @@ +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 { + const response = await axios.get(url); + + 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 }; diff --git a/contracts/tasks/tasks.js b/contracts/tasks/tasks.js index 210de44de1..b4578c1eaa 100644 --- a/contracts/tasks/tasks.js +++ b/contracts/tasks/tasks.js @@ -141,6 +141,7 @@ const { mockBeaconRoot, copyBeaconRoot, } = require("./beaconTesting"); +const { claimMerklRewards } = require("./merkl"); const log = require("../utils/logger")("tasks"); @@ -2376,3 +2377,27 @@ subtask("tenderlyUpload", "Uploads a contract to Tenderly.") task("tenderlyUpload").setAction(async (_, __, runSuper) => { return runSuper(); }); + +subtask( + "claimMorphoRewards", + "Claim MORPHO rewards from the Morpho Vaults." +).setAction(async () => { + const signer = await getSigner(); + + const morphoVaultAddresses = [ + // Morpho Gauntlet Prime USDC + "0x2b8f37893ee713a4e9ff0ceb79f27539f20a32a1", + // Morpho Gauntlet Prime USDT + "0xe3ae7c80a1b02ccd3fb0227773553aeb14e32f26", + // Meta Morpho Vault + "0x603CDEAEC82A60E3C4A10dA6ab546459E5f64Fa0", + ]; + + for (const morphoVaultAddress of morphoVaultAddresses) { + log(`Claiming MORPHO rewards from Morpho Vault: ${morphoVaultAddress}`); + await claimMerklRewards(morphoVaultAddress, signer); + } +}); +task("claimMorphoRewards").setAction(async (_, __, runSuper) => { + return runSuper(); +}); diff --git a/contracts/test/strategies/ousd-morpho-guantlet-prime-usdc.mainnet.fork-test.js b/contracts/test/strategies/ousd-morpho-guantlet-prime-usdc.mainnet.fork-test.js index c85fb37567..16048298ff 100644 --- a/contracts/test/strategies/ousd-morpho-guantlet-prime-usdc.mainnet.fork-test.js +++ b/contracts/test/strategies/ousd-morpho-guantlet-prime-usdc.mainnet.fork-test.js @@ -1,6 +1,7 @@ const { expect } = require("chai"); const { formatUnits, parseUnits } = require("ethers/lib/utils"); +const { getMerklRewards } = require("../../tasks/merkl"); const addresses = require("../../utils/addresses"); const { units, isCI } = require("../helpers"); @@ -9,7 +10,9 @@ const { morphoGauntletPrimeUSDCFixture, } = require("../_fixture"); -const log = require("../../utils/logger"); +const log = require("../../utils/logger")( + "test:fork:ousd-morpho-gauntlet-usdc" +); describe("ForkTest: Morpho Gauntlet Prime USDC Strategy", function () { this.timeout(0); @@ -357,6 +360,56 @@ describe("ForkTest: Morpho Gauntlet Prime USDC Strategy", function () { }); }); + describe("claim and collect MORPHO rewards", () => { + const loadFixture = createFixtureLoader(morphoGauntletPrimeUSDCFixture); + beforeEach(async () => { + fixture = await loadFixture(); + }); + it("Should claim MORPHO rewards", async () => { + const { josh, morphoGauntletPrimeUSDCStrategy, morphoToken } = fixture; + + const { amount, proofs } = await getMerklRewards({ + userAddress: morphoGauntletPrimeUSDCStrategy.address, + chainId: 1, + }); + log(`MORPHO rewards available to claim: ${formatUnits(amount, 18)}`); + + const tx = await morphoGauntletPrimeUSDCStrategy + .connect(josh) + .merkleClaim(morphoToken.address, amount, proofs); + await expect(tx) + .to.emit(morphoGauntletPrimeUSDCStrategy, "ClaimedRewards") + .withArgs(morphoToken.address, amount); + }); + + it("Should be able to collect MORPHO rewards", async () => { + const { strategist, josh, morphoGauntletPrimeUSDCStrategy, morphoToken } = + fixture; + + const { amount, proofs } = await getMerklRewards({ + userAddress: morphoGauntletPrimeUSDCStrategy.address, + chainId: 1, + }); + log(`MORPHO rewards available to claim: ${formatUnits(amount, 18)}`); + + await morphoGauntletPrimeUSDCStrategy + .connect(josh) + .merkleClaim(morphoToken.address, amount, proofs); + + const tx = await morphoGauntletPrimeUSDCStrategy + .connect(strategist) + .collectRewardTokens(); + + await expect(tx) + .to.emit(morphoToken, "Transfer") + .withArgs( + morphoGauntletPrimeUSDCStrategy.address, + strategist.address, + amount + ); + }); + }); + describe("administration", () => { const loadFixture = createFixtureLoader(morphoGauntletPrimeUSDCFixture); beforeEach(async () => { From 2dbd89fa22fd367630b947c26a78681ec4ac16de Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Thu, 18 Dec 2025 09:57:21 +1100 Subject: [PATCH 02/11] bumped deploy script number --- ...de_morpho_strategies.js => 160_upgrade_morpho_strategies.js} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename contracts/deploy/mainnet/{159_upgrade_morpho_strategies.js => 160_upgrade_morpho_strategies.js} (98%) diff --git a/contracts/deploy/mainnet/159_upgrade_morpho_strategies.js b/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js similarity index 98% rename from contracts/deploy/mainnet/159_upgrade_morpho_strategies.js rename to contracts/deploy/mainnet/160_upgrade_morpho_strategies.js index dff1e33fc9..96fd0430bd 100644 --- a/contracts/deploy/mainnet/159_upgrade_morpho_strategies.js +++ b/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js @@ -3,7 +3,7 @@ const { deploymentWithGovernanceProposal } = require("../../utils/deploy"); module.exports = deploymentWithGovernanceProposal( { - deployName: "159_upgrade_morpho_strategies", + deployName: "160_upgrade_morpho_strategies", forceDeploy: false, // forceSkip: true, // reduceQueueTime: true, From 91c6feb96b1d9ab34f6415868fd66cac71099528 Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Thu, 18 Dec 2025 10:42:08 +1100 Subject: [PATCH 03/11] Add deploy of new Morpho OUSD v2 strategy --- .../mainnet/160_upgrade_morpho_strategies.js | 62 ++++++++++++++++++- contracts/utils/addresses.js | 2 + 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js b/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js index 96fd0430bd..aa08fe28ce 100644 --- a/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js +++ b/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js @@ -10,9 +10,13 @@ module.exports = deploymentWithGovernanceProposal( deployerIsProposer: false, // proposalId: "", }, - async ({ deployWithConfirmation }) => { + 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" ); @@ -26,6 +30,11 @@ module.exports = deploymentWithGovernanceProposal( // 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", @@ -53,6 +62,41 @@ module.exports = deploymentWithGovernanceProposal( ] ); + // 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.MorphoYearnOUSDv2Vault, 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 { @@ -65,17 +109,29 @@ module.exports = deploymentWithGovernanceProposal( args: [dMorphoSteakhouseUSDCStrategyImpl.address], }, { - // 1. Upgrade Morpho Steakhouse USDC Strategy + // 2. Upgrade Morpho Gauntlet Prime USDC Strategy contract: dMorphoGauntletPrimeUSDCStrategyProxy, signature: "upgradeTo(address)", args: [dMorphoGauntletPrimeUSDCStrategyImpl.address], }, { - // 1. Upgrade Morpho Steakhouse USDC Strategy + // 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], + }, + { + // 5. Set the Harvester of the Morpho OUSD v2 strategy to the BuyBack Operator + contract: cOUSDMorphoV2Strategy, + signature: "setHarvesterAddress(address)", + args: [addresses.multichainBuybackOperator], + }, ], }; } diff --git a/contracts/utils/addresses.js b/contracts/utils/addresses.js index d7f4d34b84..03209fb9ac 100644 --- a/contracts/utils/addresses.js +++ b/contracts/utils/addresses.js @@ -207,6 +207,8 @@ addresses.mainnet.MorphoGauntletPrimeUSDCVault = "0xdd0f28e19C1780eb6396170735D45153D261490d"; addresses.mainnet.MorphoGauntletPrimeUSDTVault = "0x8CB3649114051cA5119141a34C200D65dc0Faa73"; +addresses.mainnet.MorphoYearnOUSDv2Vault = + "0xFB154c729A16802c4ad1E8f7FF539a8b9f49c960"; addresses.mainnet.UniswapOracle = "0xc15169Bad17e676b3BaDb699DEe327423cE6178e"; addresses.mainnet.CompensationClaims = From f755491a2d725487e0c55e58b601fb113b00c10c Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Thu, 18 Dec 2025 12:44:43 +1100 Subject: [PATCH 04/11] Added OUSD v2 fork tests --- .../mainnet/160_upgrade_morpho_strategies.js | 2 +- contracts/test/_fixture.js | 82 +++++ .../ousd-v2-morpho.mainnet.fork-test.js | 343 ++++++++++++++++++ contracts/utils/addresses.js | 2 +- 4 files changed, 427 insertions(+), 2 deletions(-) create mode 100644 contracts/test/strategies/ousd-v2-morpho.mainnet.fork-test.js diff --git a/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js b/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js index aa08fe28ce..1ce1428d26 100644 --- a/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js +++ b/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js @@ -71,7 +71,7 @@ module.exports = deploymentWithGovernanceProposal( const dOUSDMorphoV2StrategyImpl = await deployWithConfirmation( "Generalized4626Strategy", [ - [addresses.mainnet.MorphoYearnOUSDv2Vault, cVaultProxy.address], + [addresses.mainnet.MorphoOUSDv2Vault, cVaultProxy.address], addresses.mainnet.USDC, ] ); diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index 86cd1ecc8e..dd086b1b2a 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -658,6 +658,16 @@ const defaultFixture = deployments.createFixture(async () => { morphoGauntletPrimeUSDTStrategyProxy.address ); + const morphoOUSDv2StrategyProxy = !isFork + ? undefined + : await ethers.getContract("OUSDMorphoV2StrategyProxy"); + const morphoOUSDv2Strategy = !isFork + ? undefined + : await ethers.getContractAt( + "Generalized4626Strategy", + morphoOUSDv2StrategyProxy.address + ); + const curvePoolBooster = isFork ? await ethers.getContractAt( "CurvePoolBooster", @@ -744,6 +754,7 @@ const defaultFixture = deployments.createFixture(async () => { morphoSteakHouseUSDCVault, morphoGauntletPrimeUSDCVault, morphoGauntletPrimeUSDTVault, + morphoOUSDv2Vault, ssv; let chainlinkOracleFeedDAI, @@ -824,6 +835,10 @@ const defaultFixture = deployments.createFixture(async () => { metamorphoAbi, addresses.mainnet.MorphoGauntletPrimeUSDTVault ); + morphoOUSDv2Vault = await ethers.getContractAt( + metamorphoAbi, + addresses.mainnet.MorphoOUSDv2Vault + ); morphoToken = await ethers.getContractAt( erc20Abi, addresses.mainnet.MorphoToken @@ -1143,6 +1158,8 @@ const defaultFixture = deployments.createFixture(async () => { morphoGauntletPrimeUSDCVault, morphoGauntletPrimeUSDTStrategy, morphoGauntletPrimeUSDTVault, + morphoOUSDv2Strategy, + morphoOUSDv2Vault, curvePoolBooster, simpleOETHHarvester, oethFixedRateDripper, @@ -1851,6 +1868,70 @@ async function morphoGauntletPrimeUSDTFixture( return fixture; } +/** + * Configure a Vault with default USDC strategy to Yearn's Morpho OUSD v2 Vault. + */ +async function morphoOUSDv2Fixture( + config = { + usdcMintAmount: 0, + depositToStrategy: false, + } +) { + const fixture = await defaultFixture(); + + if (isFork) { + const { usdc, josh, morphoOUSDv2Strategy, strategist, vault } = fixture; + + // TODO remove once Yearn has done this on mainnet + // Whitelist the strategy + const gateOwner = await impersonateAndFund( + "0x50B75d586929Ab2F75dC15f07E1B921b7C4Ba8fA" + ); + const gate = await ethers.getContractAt( + ["function setIsWhitelisted(address,bool) external"], + "0x6704aB7aF6787930c60DFa422104E899E823e657" + ); + await gate + .connect(gateOwner) + .setIsWhitelisted(morphoOUSDv2Strategy.address, true); + + // Impersonate the OUSD Vault + fixture.vaultSigner = await impersonateAndFund(vault.address); + + // mint some OUSD using USDC if configured + if (config?.usdcMintAmount > 0) { + const usdcMintAmount = parseUnits(config.usdcMintAmount.toString(), 6); + await vault.connect(josh).rebase(); + await vault.connect(josh).allocate(); + + // Approve the Vault to transfer USDC + await usdc.connect(josh).approve(vault.address, usdcMintAmount); + + // Mint OUSD with USDC + // This will sit in the vault, not the strategy + await vault.connect(josh).mint(usdc.address, usdcMintAmount, 0); + + // Add USDC to the strategy + if (config?.depositToStrategy) { + // The strategist deposits the USDC to the strategy + await vault + .connect(strategist) + .depositToStrategy( + morphoOUSDv2Strategy.address, + [usdc.address], + [usdcMintAmount] + ); + } + } + } else { + throw new Error( + "Yearn's Morpho OUSD v2 strategy only supported in forked test environment" + ); + } + + return fixture; +} + /** * Configure a Vault with only the Morpho strategy. */ @@ -2928,6 +3009,7 @@ module.exports = { morphoSteakhouseUSDCFixture, morphoGauntletPrimeUSDCFixture, morphoGauntletPrimeUSDTFixture, + morphoOUSDv2Fixture, morphoCompoundFixture, aaveFixture, morphoAaveFixture, diff --git a/contracts/test/strategies/ousd-v2-morpho.mainnet.fork-test.js b/contracts/test/strategies/ousd-v2-morpho.mainnet.fork-test.js new file mode 100644 index 0000000000..762fa22de5 --- /dev/null +++ b/contracts/test/strategies/ousd-v2-morpho.mainnet.fork-test.js @@ -0,0 +1,343 @@ +const { expect } = require("chai"); +const { formatUnits, parseUnits } = require("ethers/lib/utils"); + +const addresses = require("../../utils/addresses"); +const { units, isCI } = require("../helpers"); + +const { createFixtureLoader, morphoOUSDv2Fixture } = require("../_fixture"); + +const log = require("../../utils/logger")("test:fork:ousd-v2-morpho"); + +describe("ForkTest: Yearn's Morpho OUSD v2 Strategy", function () { + this.timeout(0); + + // Retry up to 3 times on CI + this.retries(isCI ? 3 : 0); + + let fixture; + + describe("post deployment", () => { + const loadFixture = createFixtureLoader(morphoOUSDv2Fixture); + beforeEach(async () => { + fixture = await loadFixture(); + }); + it("Should have constants and immutables set", async () => { + const { vault, morphoOUSDv2Strategy } = fixture; + + expect(await morphoOUSDv2Strategy.platformAddress()).to.equal( + addresses.mainnet.MorphoOUSDv2Vault + ); + expect(await morphoOUSDv2Strategy.vaultAddress()).to.equal(vault.address); + expect(await morphoOUSDv2Strategy.shareToken()).to.equal( + addresses.mainnet.MorphoOUSDv2Vault + ); + expect(await morphoOUSDv2Strategy.assetToken()).to.equal( + addresses.mainnet.USDC + ); + expect( + await morphoOUSDv2Strategy.supportsAsset(addresses.mainnet.USDC) + ).to.equal(true); + expect( + await morphoOUSDv2Strategy.assetToPToken(addresses.mainnet.USDC) + ).to.equal(addresses.mainnet.MorphoOUSDv2Vault); + expect(await morphoOUSDv2Strategy.governor()).to.equal( + addresses.mainnet.Timelock + ); + }); + it("Should be able to check balance", async () => { + const { usdc, josh, morphoOUSDv2Strategy } = fixture; + + // This uses a transaction to call a view function so the gas usage can be reported. + const tx = await morphoOUSDv2Strategy + .connect(josh) + .populateTransaction.checkBalance(usdc.address); + await josh.sendTransaction(tx); + }); + it("Only Governor can approve all tokens", async () => { + const { + timelock, + oldTimelock, + strategist, + josh, + daniel, + domen, + morphoOUSDv2Strategy, + usdc, + vaultSigner, + } = fixture; + + // Governor can approve all tokens + const tx = await morphoOUSDv2Strategy + .connect(timelock) + .safeApproveAllTokens(); + await expect(tx).to.emit(usdc, "Approval"); + + for (const signer of [ + daniel, + domen, + josh, + strategist, + oldTimelock, + vaultSigner, + ]) { + const tx = morphoOUSDv2Strategy.connect(signer).safeApproveAllTokens(); + await expect(tx).to.be.revertedWith("Caller is not the Governor"); + } + }); + }); + + describe("with some USDC in the vault", () => { + const loadFixture = createFixtureLoader(morphoOUSDv2Fixture, { + usdcMintAmount: 12000, + depositToStrategy: false, + }); + beforeEach(async () => { + fixture = await loadFixture(); + }); + + it("Vault should deposit some USDC to strategy", async function () { + const { + usdc, + ousd, + morphoOUSDv2Strategy, + vault, + strategist, + vaultSigner, + } = fixture; + + const checkBalanceBefore = await morphoOUSDv2Strategy.checkBalance( + usdc.address + ); + + const usdcDepositAmount = await units("1000", usdc); + + // Vault transfers USDC to strategy + await usdc + .connect(vaultSigner) + .transfer(morphoOUSDv2Strategy.address, usdcDepositAmount); + + await vault.connect(strategist).rebase(); + + const ousdSupplyBefore = await ousd.totalSupply(); + + const tx = await morphoOUSDv2Strategy + .connect(vaultSigner) + .deposit(usdc.address, usdcDepositAmount); + + // Check emitted event + await expect(tx) + .to.emit(morphoOUSDv2Strategy, "Deposit") + .withArgs( + usdc.address, + addresses.mainnet.MorphoOUSDv2Vault, + usdcDepositAmount + ); + + // Check the OUSD total supply increase + const ousdSupplyAfter = await ousd.totalSupply(); + expect(ousdSupplyAfter).to.approxEqualTolerance( + ousdSupplyBefore.add(usdcDepositAmount), + 0.1 // 0.1% or 10 basis point + ); + expect( + await morphoOUSDv2Strategy.checkBalance(usdc.address) + ).to.approxEqualTolerance( + checkBalanceBefore.add(usdcDepositAmount), + 0.01 + ); // 0.01% or 1 basis point + }); + it("Only vault can deposit some USDC to the strategy", async function () { + const { + usdc, + morphoOUSDv2Strategy, + vaultSigner, + strategist, + timelock, + oldTimelock, + josh, + } = fixture; + + const depositAmount = await units("50", usdc); + await usdc + .connect(vaultSigner) + .transfer(morphoOUSDv2Strategy.address, depositAmount); + + for (const signer of [strategist, oldTimelock, timelock, josh]) { + const tx = morphoOUSDv2Strategy + .connect(signer) + .deposit(usdc.address, depositAmount); + + await expect(tx).to.revertedWith("Caller is not the Vault"); + } + }); + it("Only vault can deposit all USDC to strategy", async function () { + const { + usdc, + morphoOUSDv2Strategy, + vaultSigner, + strategist, + timelock, + oldTimelock, + josh, + } = fixture; + + const depositAmount = await units("50", usdc); + await usdc + .connect(vaultSigner) + .transfer(morphoOUSDv2Strategy.address, depositAmount); + + for (const signer of [strategist, oldTimelock, timelock, josh]) { + const tx = morphoOUSDv2Strategy.connect(signer).depositAll(); + + await expect(tx).to.revertedWith("Caller is not the Vault"); + } + + const tx = await morphoOUSDv2Strategy.connect(vaultSigner).depositAll(); + await expect(tx).to.emit(morphoOUSDv2Strategy, "Deposit"); + }); + }); + + describe("with the strategy having some USDC in Morpho Strategy", () => { + const loadFixture = createFixtureLoader(morphoOUSDv2Fixture, { + usdcMintAmount: 12000, + depositToStrategy: true, + }); + beforeEach(async () => { + fixture = await loadFixture(); + }); + + it("Vault should be able to withdraw all", async () => { + const { + usdc, + morphoOUSDv2Vault, + morphoOUSDv2Strategy, + ousd, + vault, + vaultSigner, + } = fixture; + + expect(await morphoOUSDv2Vault.totalSupply()).to.be.gt(0); + const usdcWithdrawAmountExpected = await morphoOUSDv2Vault.maxWithdraw( + morphoOUSDv2Strategy.address + ); + expect( + usdcWithdrawAmountExpected, + "Morpho Vault maxWithdraw greater than 0" + ).to.be.gt(0); + + log( + `Expected to withdraw ${formatUnits( + usdcWithdrawAmountExpected, + 6 + )} USDC` + ); + + const ousdSupplyBefore = await ousd.totalSupply(); + const vaultUSDCBalanceBefore = await usdc.balanceOf(vault.address); + + log("Before withdraw all from strategy"); + + // Now try to withdraw all the WETH from the strategy + const tx = await morphoOUSDv2Strategy.connect(vaultSigner).withdrawAll(); + + log("After withdraw all from strategy"); + + // Check emitted event + await expect(tx).to.emittedEvent("Withdrawal", [ + usdc.address, + morphoOUSDv2Vault.address, + (amount) => + expect(amount).approxEqualTolerance( + usdcWithdrawAmountExpected, + 0.01, + "Withdrawal amount" + ), + ]); + + // Check the OUSD total supply stays the same + expect(await ousd.totalSupply()).to.approxEqualTolerance( + ousdSupplyBefore, + 0.01 // 0.01% or 1 basis point + ); + + // Check the USDC amount in the vault increases + expect(await usdc.balanceOf(vault.address)).to.approxEqualTolerance( + vaultUSDCBalanceBefore.add(usdcWithdrawAmountExpected), + 0.01 + ); + }); + it("Vault should be able to withdraw some USDC", async () => { + const { + usdc, + morphoOUSDv2Vault, + morphoOUSDv2Strategy, + ousd, + vault, + vaultSigner, + } = fixture; + + const withdrawAmount = await units("1000", usdc); + + const ousdSupplyBefore = await ousd.totalSupply(); + const vaultUSDCBalanceBefore = await usdc.balanceOf(vault.address); + + log(`Before withdraw of ${formatUnits(withdrawAmount, 6)} from strategy`); + + // Now try to withdraw the USDC from the strategy + const tx = await morphoOUSDv2Strategy + .connect(vaultSigner) + .withdraw(vault.address, usdc.address, withdrawAmount); + + log("After withdraw from strategy"); + + // Check emitted event + await expect(tx) + .to.emit(morphoOUSDv2Strategy, "Withdrawal") + .withArgs(usdc.address, morphoOUSDv2Vault.address, withdrawAmount); + + // Check the OUSD total supply stays the same + const ousdSupplyAfter = await ousd.totalSupply(); + expect(ousdSupplyAfter).to.approxEqualTolerance( + ousdSupplyBefore, + 0.01 // 0.01% or 1 basis point + ); + + // Check the USDC balance in the Vault + expect(await usdc.balanceOf(vault.address)).to.equal( + vaultUSDCBalanceBefore.add(withdrawAmount) + ); + }); + it("Only vault can withdraw some USDC from strategy", async function () { + const { + morphoOUSDv2Strategy, + oethVault, + strategist, + timelock, + oldTimelock, + josh, + weth, + } = fixture; + + for (const signer of [strategist, timelock, oldTimelock, josh]) { + const tx = morphoOUSDv2Strategy + .connect(signer) + .withdraw(oethVault.address, weth.address, parseUnits("50")); + + await expect(tx).to.revertedWith("Caller is not the Vault"); + } + }); + it("Only vault and governor can withdraw all USDC from Maker DSR strategy", async function () { + const { morphoOUSDv2Strategy, strategist, timelock, josh } = fixture; + + for (const signer of [strategist, josh]) { + const tx = morphoOUSDv2Strategy.connect(signer).withdrawAll(); + + await expect(tx).to.revertedWith("Caller is not the Vault or Governor"); + } + + // Governor can withdraw all + const tx = morphoOUSDv2Strategy.connect(timelock).withdrawAll(); + await expect(tx).to.emit(morphoOUSDv2Strategy, "Withdrawal"); + }); + }); +}); diff --git a/contracts/utils/addresses.js b/contracts/utils/addresses.js index 03209fb9ac..bec94b3b2c 100644 --- a/contracts/utils/addresses.js +++ b/contracts/utils/addresses.js @@ -207,7 +207,7 @@ addresses.mainnet.MorphoGauntletPrimeUSDCVault = "0xdd0f28e19C1780eb6396170735D45153D261490d"; addresses.mainnet.MorphoGauntletPrimeUSDTVault = "0x8CB3649114051cA5119141a34C200D65dc0Faa73"; -addresses.mainnet.MorphoYearnOUSDv2Vault = +addresses.mainnet.MorphoOUSDv2Vault = "0xFB154c729A16802c4ad1E8f7FF539a8b9f49c960"; addresses.mainnet.UniswapOracle = "0xc15169Bad17e676b3BaDb699DEe327423cE6178e"; From 309f2efea0ee76e67a08f2741dda0a42c8be6945 Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Thu, 18 Dec 2025 13:18:56 +1100 Subject: [PATCH 05/11] Fixed fork tests --- .../strategies/ousd-v2-morpho.mainnet.fork-test.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/contracts/test/strategies/ousd-v2-morpho.mainnet.fork-test.js b/contracts/test/strategies/ousd-v2-morpho.mainnet.fork-test.js index 762fa22de5..88f27f99f7 100644 --- a/contracts/test/strategies/ousd-v2-morpho.mainnet.fork-test.js +++ b/contracts/test/strategies/ousd-v2-morpho.mainnet.fork-test.js @@ -216,14 +216,13 @@ describe("ForkTest: Yearn's Morpho OUSD v2 Strategy", function () { vaultSigner, } = fixture; - expect(await morphoOUSDv2Vault.totalSupply()).to.be.gt(0); - const usdcWithdrawAmountExpected = await morphoOUSDv2Vault.maxWithdraw( + const minBalance = await units("12000", usdc); + const strategyVaultShares = await morphoOUSDv2Vault.balanceOf( morphoOUSDv2Strategy.address ); - expect( - usdcWithdrawAmountExpected, - "Morpho Vault maxWithdraw greater than 0" - ).to.be.gt(0); + const usdcWithdrawAmountExpected = + await morphoOUSDv2Vault.convertToAssets(strategyVaultShares); + expect(usdcWithdrawAmountExpected).to.be.gte(minBalance.sub(1)); log( `Expected to withdraw ${formatUnits( From 87533effb411cb5cf4527b81c4b4b44540e0c05a Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Thu, 18 Dec 2025 17:32:57 +1100 Subject: [PATCH 06/11] Handle no Merkl rewards Added fork tests for claiming Merkl rewards for OUSD v2 Morpho Vault --- contracts/tasks/merkl.js | 10 ++++ contracts/test/_fixture.js | 4 ++ .../ousd-v2-morpho.mainnet.fork-test.js | 57 +++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/contracts/tasks/merkl.js b/contracts/tasks/merkl.js index e83dc53041..0b7bbcd7b8 100644 --- a/contracts/tasks/merkl.js +++ b/contracts/tasks/merkl.js @@ -10,8 +10,18 @@ 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, diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index dd086b1b2a..d870a965ce 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -1898,6 +1898,10 @@ async function morphoOUSDv2Fixture( // Impersonate the OUSD Vault fixture.vaultSigner = await impersonateAndFund(vault.address); + fixture.buyBackSigner = await impersonateAndFund( + addresses.multichainBuybackOperator + ); + // mint some OUSD using USDC if configured if (config?.usdcMintAmount > 0) { const usdcMintAmount = parseUnits(config.usdcMintAmount.toString(), 6); diff --git a/contracts/test/strategies/ousd-v2-morpho.mainnet.fork-test.js b/contracts/test/strategies/ousd-v2-morpho.mainnet.fork-test.js index 88f27f99f7..1e21d6b992 100644 --- a/contracts/test/strategies/ousd-v2-morpho.mainnet.fork-test.js +++ b/contracts/test/strategies/ousd-v2-morpho.mainnet.fork-test.js @@ -2,6 +2,7 @@ const { expect } = require("chai"); const { formatUnits, parseUnits } = require("ethers/lib/utils"); const addresses = require("../../utils/addresses"); +const { getMerklRewards } = require("../../tasks/merkl"); const { units, isCI } = require("../helpers"); const { createFixtureLoader, morphoOUSDv2Fixture } = require("../_fixture"); @@ -339,4 +340,60 @@ describe("ForkTest: Yearn's Morpho OUSD v2 Strategy", function () { await expect(tx).to.emit(morphoOUSDv2Strategy, "Withdrawal"); }); }); + + describe("claim and collect MORPHO rewards", () => { + const loadFixture = createFixtureLoader(morphoOUSDv2Fixture); + beforeEach(async () => { + fixture = await loadFixture(); + }); + it("Should claim MORPHO rewards", async () => { + const { josh, morphoOUSDv2Strategy, morphoToken } = fixture; + + const { amount, proofs } = await getMerklRewards({ + userAddress: morphoOUSDv2Strategy.address, + chainId: 1, + }); + log(`MORPHO rewards available to claim: ${formatUnits(amount, 18)}`); + + if (amount != "0") { + const tx = await morphoOUSDv2Strategy + .connect(josh) + .merkleClaim(morphoToken.address, amount, proofs); + await expect(tx) + .to.emit(morphoOUSDv2Strategy, "ClaimedRewards") + .withArgs(morphoToken.address, amount); + } + }); + + it("Should be able to collect MORPHO rewards", async () => { + const { buyBackSigner, josh, morphoOUSDv2Strategy, morphoToken } = + fixture; + + const { amount, proofs } = await getMerklRewards({ + userAddress: morphoOUSDv2Strategy.address, + chainId: 1, + }); + log(`MORPHO rewards available to claim: ${formatUnits(amount, 18)}`); + + if (amount != "0") { + await morphoOUSDv2Strategy + .connect(josh) + .merkleClaim(morphoToken.address, amount, proofs); + } + + const tx = await morphoOUSDv2Strategy + .connect(buyBackSigner) + .collectRewardTokens(); + + if (amount != "0") { + await expect(tx) + .to.emit(morphoToken, "Transfer") + .withArgs( + morphoOUSDv2Strategy.address, + buyBackSigner.address, + amount + ); + } + }); + }); }); From a2fdecbb28f6ab0e31fa8187924af5c85a3b5f44 Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Fri, 19 Dec 2025 07:34:28 +1100 Subject: [PATCH 07/11] Remove whitelisting of new OUSD v2 Morpho vault --- contracts/test/_fixture.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index d870a965ce..8173f8e4f8 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -1883,17 +1883,17 @@ async function morphoOUSDv2Fixture( const { usdc, josh, morphoOUSDv2Strategy, strategist, vault } = fixture; // TODO remove once Yearn has done this on mainnet - // Whitelist the strategy - const gateOwner = await impersonateAndFund( - "0x50B75d586929Ab2F75dC15f07E1B921b7C4Ba8fA" - ); - const gate = await ethers.getContractAt( - ["function setIsWhitelisted(address,bool) external"], - "0x6704aB7aF6787930c60DFa422104E899E823e657" - ); - await gate - .connect(gateOwner) - .setIsWhitelisted(morphoOUSDv2Strategy.address, true); + // // Whitelist the strategy + // const gateOwner = await impersonateAndFund( + // "0x50B75d586929Ab2F75dC15f07E1B921b7C4Ba8fA" + // ); + // const gate = await ethers.getContractAt( + // ["function setIsWhitelisted(address,bool) external"], + // "0x6704aB7aF6787930c60DFa422104E899E823e657" + // ); + // await gate + // .connect(gateOwner) + // .setIsWhitelisted(morphoOUSDv2Strategy.address, true); // Impersonate the OUSD Vault fixture.vaultSigner = await impersonateAndFund(vault.address); From c614298917b65daf46835a0bb5f426c419e9824c Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Fri, 19 Dec 2025 15:02:34 +1100 Subject: [PATCH 08/11] Removed the Morpho Steakhouse strategy deposit 10k to the new Morpho OUSD v2 strategy Reduced the redeem fee to 0.1% --- .../mainnet/160_upgrade_morpho_strategies.js | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js b/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js index 1ce1428d26..c6d4c9591b 100644 --- a/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js +++ b/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js @@ -132,6 +132,43 @@ module.exports = deploymentWithGovernanceProposal( 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. Withdraw all from the Morpho Steakhouse strategy + contract: cVaultAdmin, + signature: "withdrawAllFromStrategy(address)", + args: [dMorphoSteakhouseUSDCStrategyProxy.address], + }, + { + // 8. Remove the Morpho Steakhouse strategy + contract: cVaultAdmin, + signature: "removeStrategy(address)", + args: [dMorphoSteakhouseUSDCStrategyProxy.address], + }, + { + // 9. Deposit 10k to the new Morpho OUSD v2 strategy + contract: cVaultAdmin, + signature: "depositToStrategy(address,address[],uint256[])", + args: [ + cOUSDMorphoV2Strategy.address, + [addresses.mainnet.USDC], + ["10000000000"], // 10,000 USDC with 6 decimals + ], + }, + { + // 10. Change the redeem fee from 0.25% to 0.1% + contract: cVaultAdmin, + signature: "setRedeemFeeBps(uint256)", + args: [10], + }, ], }; } From 1f9f018510f5182f3c74f4e94f7ee9fb522e7cb4 Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Fri, 19 Dec 2025 19:38:20 +1100 Subject: [PATCH 09/11] Added more details to the governance proposal description --- contracts/deploy/mainnet/160_upgrade_morpho_strategies.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js b/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js index c6d4c9591b..348f1a9cc9 100644 --- a/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js +++ b/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js @@ -100,7 +100,12 @@ module.exports = deploymentWithGovernanceProposal( // Governance Actions // ---------------- return { - name: "Upgrade Morpho Steakhouse and Gauntlet Prime Strategies to claim MORPHO rewards from Merkl", + 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. +Change the redeem fee from 0.25% to 0.1%. +`, actions: [ { // 1. Upgrade Morpho Steakhouse USDC Strategy From 4d1ccecbe8b9ade1a2f38027b22e1c58dbc799f2 Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Fri, 19 Dec 2025 22:34:32 +1100 Subject: [PATCH 10/11] Removed changing the redeem fee --- contracts/deploy/mainnet/160_upgrade_morpho_strategies.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js b/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js index 348f1a9cc9..f7838b12d1 100644 --- a/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js +++ b/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js @@ -104,7 +104,6 @@ module.exports = deploymentWithGovernanceProposal( 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. -Change the redeem fee from 0.25% to 0.1%. `, actions: [ { @@ -168,12 +167,6 @@ Change the redeem fee from 0.25% to 0.1%. ["10000000000"], // 10,000 USDC with 6 decimals ], }, - { - // 10. Change the redeem fee from 0.25% to 0.1% - contract: cVaultAdmin, - signature: "setRedeemFeeBps(uint256)", - args: [10], - }, ], }; } From 47dbea989ab5a1bc212ac242e14cc7667c91c2c0 Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Fri, 19 Dec 2025 22:44:14 +1100 Subject: [PATCH 11/11] Removed deposit to the new strategy Removed the redundant withdrawAllFromStrategy --- .../mainnet/160_upgrade_morpho_strategies.js | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js b/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js index f7838b12d1..95b929e8c2 100644 --- a/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js +++ b/contracts/deploy/mainnet/160_upgrade_morpho_strategies.js @@ -146,27 +146,11 @@ Add new Morpho OUSD v2 Strategy and deposit 10k USDC. ], }, { - // 7. Withdraw all from the Morpho Steakhouse strategy - contract: cVaultAdmin, - signature: "withdrawAllFromStrategy(address)", - args: [dMorphoSteakhouseUSDCStrategyProxy.address], - }, - { - // 8. Remove the Morpho Steakhouse strategy + // 7. Remove the Morpho Steakhouse strategy contract: cVaultAdmin, signature: "removeStrategy(address)", args: [dMorphoSteakhouseUSDCStrategyProxy.address], }, - { - // 9. Deposit 10k to the new Morpho OUSD v2 strategy - contract: cVaultAdmin, - signature: "depositToStrategy(address,address[],uint256[])", - args: [ - cOUSDMorphoV2Strategy.address, - [addresses.mainnet.USDC], - ["10000000000"], // 10,000 USDC with 6 decimals - ], - }, ], }; }