Skip to content

Commit

Permalink
Morpho Gauntlet Prime Strategies for OUSD (#2317)
Browse files Browse the repository at this point in the history
* Deploy script for Morpho Gauntlet Prime USDC Strategy to the OUSD Vault
Refactored the exiting MetaMorpho deploy script and fork tests as there will be many Morpho strategies

* Generalized4626Strategy now uses safeApprove as USDT is being used
Added deploy script for Morpho Gauntlet Prime USDT Strategy

* Add fork tests for Morpho Gauntlet USDT strategy

* Fix typo in Natspec

* Added Generalized4626USDTStrategy contract for Morpho Gauntlet Prime USDT strategy

* Fixed typos in tests

* Update OUSD Vault fork test

* Fix Slither for IUSDT approve

* Renamed fork test files
  • Loading branch information
naddison36 authored Dec 17, 2024
1 parent fa077cd commit f4bb1af
Show file tree
Hide file tree
Showing 14 changed files with 1,268 additions and 113 deletions.
18 changes: 18 additions & 0 deletions contracts/contracts/proxies/Proxies.sol
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,21 @@ contract OETHBaseHarvesterProxy is InitializeGovernedUpgradeabilityProxy {
contract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {

}

/**
* @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation
*/
contract MorphoGauntletPrimeUSDCStrategyProxy is
InitializeGovernedUpgradeabilityProxy
{

}

/**
* @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation
*/
contract MorphoGauntletPrimeUSDTStrategyProxy is
InitializeGovernedUpgradeabilityProxy
{

}
2 changes: 1 addition & 1 deletion contracts/contracts/strategies/FraxETHStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ contract FraxETHStrategy is Generalized4626Strategy {
}

/**
* @dev Retuns bool indicating whether asset is supported by strategy
* @dev Returns bool indicating whether asset is supported by strategy
* @param _asset Address of the asset
*/
function supportsAsset(address _asset) public view override returns (bool) {
Expand Down
6 changes: 2 additions & 4 deletions contracts/contracts/strategies/Generalized4626Strategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ pragma solidity ^0.8.0;
* @author Origin Protocol Inc
*/
import { IERC4626 } from "../../lib/openzeppelin/interfaces/IERC4626.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC20, InitializableAbstractStrategy } from "../utils/InitializableAbstractStrategy.sol";

contract Generalized4626Strategy is InitializableAbstractStrategy {
using SafeERC20 for IERC20;

/// @dev Replaced with an immutable variable
// slither-disable-next-line constable-states
Expand Down Expand Up @@ -173,14 +171,14 @@ contract Generalized4626Strategy is InitializableAbstractStrategy {
}

function _approveBase() internal virtual {
// Approval the asset to be trasferred to the ERC-4626 Tokenized Vualt.
// Approval the asset to be transferred to the ERC-4626 Tokenized Vault.
// Used by the ERC-4626 deposit() and mint() functions
// slither-disable-next-line unused-return
assetToken.approve(platformAddress, type(uint256).max);
}

/**
* @dev Retuns bool indicating whether asset is supported by strategy
* @dev Returns bool indicating whether asset is supported by strategy
* @param _asset Address of the asset
*/
function supportsAsset(address _asset)
Expand Down
36 changes: 36 additions & 0 deletions contracts/contracts/strategies/Generalized4626USDTStrategy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IUSDT {
// Tether's approve does not return a bool like standard IERC20 contracts
// slither-disable-next-line erc20-interface
function approve(address _spender, uint _value) external;
}

/**
* @title Generalized 4626 Strategy when asset is Tether USD (USDT)
* @notice Investment strategy for ERC-4626 Tokenized Vaults for the USDT asset.
* @author Origin Protocol Inc
*/
import { Generalized4626Strategy } from "./Generalized4626Strategy.sol";

contract Generalized4626USDTStrategy is Generalized4626Strategy {
/**
* @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,
* and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy
* @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI
*/
constructor(
BaseStrategyConfig memory _baseConfig,
address _assetToken
) Generalized4626Strategy(_baseConfig, _assetToken) {}

/// @dev Override for Tether as USDT does not return a bool on approve.
/// Using assetToken.approve will fail as it expects a bool return value
function _approveBase() internal virtual override {
// Approval the asset to be transferred to the ERC-4626 Tokenized Vault.
// Used by the ERC-4626 deposit() and mint() functions
// slither-disable-next-line unused-return
IUSDT(address(assetToken)).approve(platformAddress, type(uint256).max);
}
}
2 changes: 1 addition & 1 deletion contracts/deploy/mainnet/106_ousd_metamorpho_usdc.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ module.exports = deploymentWithGovernanceProposal(
const dMetaMorphoStrategyImpl = await deployWithConfirmation(
"Generalized4626Strategy",
[
[addresses.mainnet.MetaMorphoUSDCSteakHouseVault, cVaultProxy.address],
[addresses.mainnet.MorphoSteakhouseUSDCVault, cVaultProxy.address],
addresses.mainnet.USDC,
],
undefined,
Expand Down
95 changes: 95 additions & 0 deletions contracts/deploy/mainnet/112_ousd_morpho_gauntlet_usdc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
const addresses = require("../../utils/addresses");
const { deploymentWithGovernanceProposal } = require("../../utils/deploy");

module.exports = deploymentWithGovernanceProposal(
{
deployName: "112_ousd_morpho_gauntlet_usdc",
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 cHarvesterProxy = await ethers.getContract("HarvesterProxy");
const cHarvester = await ethers.getContractAt(
"Harvester",
cHarvesterProxy.address
);

// Deployer Actions
// ----------------
const { deployerAddr } = await getNamedAccounts();
const sDeployer = await ethers.provider.getSigner(deployerAddr);

// 1. Deploy new Morpho Gauntlet Prime USDC Strategy proxy
const dMorphoGauntletPrimeUSDCProxy = await deployWithConfirmation(
"MorphoGauntletPrimeUSDCStrategyProxy"
);
const cMorphoGauntletPrimeUSDCProxy = await ethers.getContract(
"MorphoGauntletPrimeUSDCStrategyProxy"
);

// 2. Deploy new Generalized4626Strategy contract as it has an immutable to the Morpho Vault contract
const dGeneralized4626Strategy = await deployWithConfirmation(
"Generalized4626Strategy",
[
[addresses.mainnet.MorphoGauntletPrimeUSDCVault, cVaultProxy.address],
addresses.mainnet.USDC,
]
);
const cMorphoGauntletPrimeUSDC = await ethers.getContractAt(
"Generalized4626Strategy",
dMorphoGauntletPrimeUSDCProxy.address
);

// 3. Construct initialize call data to initialize and configure the new strategy
const initData = cMorphoGauntletPrimeUSDC.interface.encodeFunctionData(
"initialize()",
[]
);

// 4. Init the proxy to point at the implementation, set the governor, and call initialize
const initFunction = "initialize(address,address,bytes)";
await withConfirmation(
cMorphoGauntletPrimeUSDCProxy.connect(sDeployer)[initFunction](
dGeneralized4626Strategy.address,
addresses.mainnet.Timelock, // governor
initData, // data for delegate call to the initialize function on the strategy
await getTxOpts()
)
);

// Governance Actions
// ----------------
return {
name: "Add Morpho Gauntlet Prime USDC Strategy to the OUSD Vault",
actions: [
{
// Add the new strategy to the vault
contract: cVaultAdmin,
signature: "approveStrategy(address)",
args: [cMorphoGauntletPrimeUSDC.address],
},
{
// Add the new strategy to the Harvester
contract: cHarvester,
signature: "setSupportedStrategy(address,bool)",
args: [cMorphoGauntletPrimeUSDC.address, true],
},
{
// Set the Harvester in the new strategy
contract: cMorphoGauntletPrimeUSDC,
signature: "setHarvesterAddress(address)",
args: [cHarvesterProxy.address],
},
],
};
}
);
95 changes: 95 additions & 0 deletions contracts/deploy/mainnet/113_ousd_morpho_gauntlet_usdt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
const addresses = require("../../utils/addresses");
const { deploymentWithGovernanceProposal } = require("../../utils/deploy");

module.exports = deploymentWithGovernanceProposal(
{
deployName: "113_ousd_morpho_gauntlet_usdt",
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 cHarvesterProxy = await ethers.getContract("HarvesterProxy");
const cHarvester = await ethers.getContractAt(
"Harvester",
cHarvesterProxy.address
);

// Deployer Actions
// ----------------
const { deployerAddr } = await getNamedAccounts();
const sDeployer = await ethers.provider.getSigner(deployerAddr);

// 1. Deploy new Morpho Gauntlet Prime USDT Strategy proxy
const dMorphoGauntletPrimeUSDTProxy = await deployWithConfirmation(
"MorphoGauntletPrimeUSDTStrategyProxy"
);
const cMorphoGauntletPrimeUSDTProxy = await ethers.getContract(
"MorphoGauntletPrimeUSDTStrategyProxy"
);

// 2. Deploy new Generalized4626USDTStrategy contract as it has an immutable to the Morpho Vault contract
const dGeneralized4626USDTStrategy = await deployWithConfirmation(
"Generalized4626USDTStrategy",
[
[addresses.mainnet.MorphoGauntletPrimeUSDTVault, cVaultProxy.address],
addresses.mainnet.USDT,
]
);
const cMorphoGauntletPrimeUSDT = await ethers.getContractAt(
"Generalized4626USDTStrategy",
dMorphoGauntletPrimeUSDTProxy.address
);

// 3. Construct initialize call data to initialize and configure the new strategy
const initData = cMorphoGauntletPrimeUSDT.interface.encodeFunctionData(
"initialize()",
[]
);

// 4. Init the proxy to point at the implementation, set the governor, and call initialize
const initFunction = "initialize(address,address,bytes)";
await withConfirmation(
cMorphoGauntletPrimeUSDTProxy.connect(sDeployer)[initFunction](
dGeneralized4626USDTStrategy.address,
addresses.mainnet.Timelock, // governor
initData, // data for delegate call to the initialize function on the strategy
await getTxOpts()
)
);

// Governance Actions
// ----------------
return {
name: "Add Morpho Gauntlet Prime USDT Strategy to the OUSD Vault",
actions: [
{
// Add the new strategy to the vault
contract: cVaultAdmin,
signature: "approveStrategy(address)",
args: [cMorphoGauntletPrimeUSDT.address],
},
{
// Add the new strategy to the Harvester
contract: cHarvester,
signature: "setSupportedStrategy(address,bool)",
args: [cMorphoGauntletPrimeUSDT.address, true],
},
{
// Set the Harvester in the new strategy
contract: cMorphoGauntletPrimeUSDT,
signature: "setHarvesterAddress(address)",
args: [cHarvesterProxy.address],
},
],
};
}
);
Loading

0 comments on commit f4bb1af

Please sign in to comment.