Skip to content

Commit

Permalink
OETH Morpho AAVE (#1543)
Browse files Browse the repository at this point in the history
* Deploy OETH Morpho AAVE contract

* OETH Morpho Aave v2 Fork tests (#1544)

* Add fork tests

* Lint

* Switch to new governor

* Proxy init to change governor at the end (#1557)

* Init of Morpho strategy from proxy init

(cherry picked from commit 4fee664)

* set governor on proxy init

(cherry picked from commit d6d9fb9)

* Proxy init to do gov change after init of impl
Simplified oeth morpho deploy script

(cherry picked from commit c38c6a7)

* fix sol prettier

(cherry picked from commit ecb7d00)

* change governance in fork tests

* reduce time with deployment script

* skipping some fork tests

* remove .only

---------

Co-authored-by: Domen Grabec <[email protected]>

---------

Co-authored-by: Shahul Hameed <[email protected]>
Co-authored-by: Domen Grabec <[email protected]>
Co-authored-by: Nick Addison <[email protected]>
  • Loading branch information
4 people authored May 29, 2023
1 parent e3123f0 commit 36332fe
Show file tree
Hide file tree
Showing 8 changed files with 406 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ contract InitializeGovernedUpgradeabilityProxy is Governable {
IMPLEMENTATION_SLOT ==
bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
);
_changeGovernor(_initGovernor);
_setImplementation(_logic);
if (_data.length > 0) {
(bool success, ) = _logic.delegatecall(_data);
require(success);
}
_changeGovernor(_initGovernor);
}

/**
Expand Down
7 changes: 7 additions & 0 deletions contracts/contracts/proxies/Proxies.sol
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,10 @@ contract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {
contract BuybackProxy is InitializeGovernedUpgradeabilityProxy {

}

/**
* @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation
*/
contract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {

}
92 changes: 92 additions & 0 deletions contracts/deploy/064_oeth_mopho_aave_v2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
const { deploymentWithProposal } = require("../utils/deploy");
const addresses = require("../utils/addresses");

module.exports = deploymentWithProposal(
{
deployName: "064_oeth_morpho_aave_v2",
forceDeploy: false,
reduceQueueTime: true,
// proposalId: ,
},
async ({
assetAddresses,
deployWithConfirmation,
ethers,
getTxOpts,
withConfirmation,
}) => {
const { deployerAddr } = await getNamedAccounts();
const sDeployer = await ethers.provider.getSigner(deployerAddr);

// Current contracts
const cVaultProxy = await ethers.getContract("OETHVaultProxy");
const cVaultAdmin = await ethers.getContractAt(
"VaultAdmin",
cVaultProxy.address
);

// Deployer Actions
// ----------------

// 1. Deploy new proxy
// New strategy will be living at a clean address
const dOETHMorphoAaveStrategyProxy = await deployWithConfirmation(
"OETHMorphoAaveStrategyProxy"
);
const cOETHMorphoAaveStrategyProxy = await ethers.getContractAt(
"OETHMorphoAaveStrategyProxy",
dOETHMorphoAaveStrategyProxy.address
);

// 2. Reuse old OUSD impl
const cMorphoAaveStrategyImpl = await ethers.getContract(
"MorphoAaveStrategy"
);
const cMorphoAaveStrategy = await ethers.getContractAt(
"MorphoAaveStrategy",
cOETHMorphoAaveStrategyProxy.address
);

// 3. Construct initialize call data to init and configure the new Morpho strategy
const initData = cMorphoAaveStrategyImpl.interface.encodeFunctionData(
"initialize(address,address[],address[],address[])",
[
cVaultProxy.address,
[], // reward token addresses
[assetAddresses.WETH], // asset token addresses
[assetAddresses.aWETH], // platform tokens addresses
]
);

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

console.log(
"OUSD Morpho Aave strategy address: ",
cMorphoAaveStrategy.address
);

// Governance Actions
// ----------------
return {
name: "Deploy new OUSD Morpho Aave strategy",
governorAddr: addresses.mainnet.OldTimelock,
actions: [
// 1. Add new Morpho strategy to vault
{
contract: cVaultAdmin,
signature: "approveStrategy(address)",
args: [cMorphoAaveStrategy.address],
},
],
};
}
);
101 changes: 95 additions & 6 deletions contracts/test/_fixture.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ async function defaultFixture() {
morphoCompoundStrategy,
fraxEthStrategy,
morphoAaveStrategy,
oethMorphoAaveStrategy,
morphoLens,
LUSDMetapoolToken,
threePoolGauge,
Expand All @@ -188,7 +189,7 @@ async function defaultFixture() {
dai = await ethers.getContractAt(daiAbi, addresses.mainnet.DAI);
tusd = await ethers.getContractAt(erc20Abi, addresses.mainnet.TUSD);
usdc = await ethers.getContractAt(erc20Abi, addresses.mainnet.USDC);
weth = await ethers.getContractAt(erc20Abi, addresses.mainnet.WETH);
weth = await ethers.getContractAt("IWETH9", addresses.mainnet.WETH);
cusdt = await ethers.getContractAt(erc20Abi, addresses.mainnet.cUSDT);
cdai = await ethers.getContractAt(erc20Abi, addresses.mainnet.cDAI);
cusdc = await ethers.getContractAt(erc20Abi, addresses.mainnet.cUSDC);
Expand Down Expand Up @@ -243,6 +244,14 @@ async function defaultFixture() {
morphoAaveStrategyProxy.address
);

const oethMorphoAaveStrategyProxy = await ethers.getContract(
"OETHMorphoAaveStrategyProxy"
);
oethMorphoAaveStrategy = await ethers.getContractAt(
"MorphoAaveStrategy",
oethMorphoAaveStrategyProxy.address
);

const fraxEthStrategyProxy = await ethers.getContract(
"FraxETHStrategyProxy"
);
Expand Down Expand Up @@ -493,13 +502,45 @@ async function defaultFixture() {
frxETH,
sfrxETH,
fraxEthStrategy,
oethMorphoAaveStrategy,
woeth,
ConvexEthMetaStrategy,
oethDripper,
oethHarvester,
};
}

function defaultFixtureSetup() {
return deployments.createFixture(async () => {
return await defaultFixture();
});
}

async function oethDefaultFixture() {
// TODO: Trim it down to only do OETH things
const fixture = await defaultFixture();

if (isFork) {
const { weth, matt, josh, domen, daniel, franck, oethVault } = fixture;

for (const user of [matt, josh, domen, daniel, franck]) {
// Everyone gets free WETH
await mintWETH(weth, user);

// And vault can rug them all
await resetAllowance(weth, user, oethVault.address);
}
}

return fixture;
}

async function oethDefaultFixtureSetup() {
return deployments.createFixture(async () => {
return await oethDefaultFixture();
});
}

/**
* Configure the MockVault contract by initializing it and setting supported
* assets and then upgrade the Vault implementation via VaultProxy.
Expand Down Expand Up @@ -819,6 +860,33 @@ async function morphoAaveFixture() {
return fixture;
}

/**
* Configure a Vault with only the Morpho strategy.
*/
function oethMorphoAaveFixtureSetup() {
return deployments.createFixture(async () => {
const fixture = await oethDefaultFixture();

if (isFork) {
const { governorAddr } = await getNamedAccounts();
let sGovernor = await ethers.provider.getSigner(governorAddr);

await fixture.oethVault
.connect(sGovernor)
.setAssetDefaultStrategy(
fixture.weth.address,
fixture.oethMorphoAaveStrategy.address
);
} else {
throw new Error(
"Morpho strategy only supported in forked test environment"
);
}

return fixture;
});
}

/**
* FraxETHStrategy fixture that works only in forked environment
*
Expand Down Expand Up @@ -955,13 +1023,24 @@ async function impersonateAccount(address) {
});
}

async function impersonateAndFundContract(address) {
async function _hardhatSetBalance(address, amount = "10000") {
await hre.network.provider.request({
method: "hardhat_setBalance",
params: [
address,
utils
.parseEther(amount)
.toHexString()
.replace(/^0x0+/, "0x")
.replace(/0$/, "1"),
],
});
}

async function impersonateAndFundContract(address, amount = "100000") {
await impersonateAccount(address);

await hre.network.provider.send("hardhat_setBalance", [
address,
utils.parseEther("1000000").toHexString(),
]);
await _hardhatSetBalance(address, amount);

return await ethers.provider.getSigner(address);
}
Expand Down Expand Up @@ -1018,6 +1097,13 @@ async function resetAllowance(
await tokenContract.connect(signer).approve(toAddress, allowance);
}

async function mintWETH(weth, recipient, amount = "100") {
await _hardhatSetBalance(recipient.address, (Number(amount) * 2).toString());
await weth.connect(recipient).deposit({
value: utils.parseEther(amount),
});
}

async function withImpersonatedAccount(address, cb) {
const signer = await impersonateAndFundContract(address);

Expand Down Expand Up @@ -1367,6 +1453,8 @@ module.exports = {
fundWith3Crv,
resetAllowance,
defaultFixture,
defaultFixtureSetup,
oethDefaultFixtureSetup,
mockVaultFixture,
compoundFixture,
compoundVaultFixture,
Expand All @@ -1387,4 +1475,5 @@ module.exports = {
impersonateAndFundContract,
impersonateAccount,
fraxETHStrategyForkedFixture,
oethMorphoAaveFixtureSetup,
};
17 changes: 15 additions & 2 deletions contracts/test/harvest/ousd-harvest-crv.fork-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,15 @@ forkOnlyDescribe("ForkTest: Harvest OUSD", function () {
oldCrvTokenConfig.doSwapRewardToken
);
});
it("should not harvest and swap", async function () {
/*
* Skipping this test as it should only fail on a specific block number, where
* there is:
* - no liquidation limit
* - strategy has accrued a lot of CRV rewards
* - depth of the SushiSwap pool is not deep enough to handle the swap without
* hitting the slippage limit.
*/
it.skip("should not harvest and swap", async function () {
const { anna, OUSDmetaStrategy, harvester } = fixture;

// prettier-ignore
Expand Down Expand Up @@ -93,7 +101,12 @@ forkOnlyDescribe("ForkTest: Harvest OUSD", function () {
oldCrvTokenConfig.doSwapRewardToken
);
});
it("should harvest and swap", async function () {
/*
* Skipping this test as it will only succeed again on a specific block number.
* If strategy doesn't have enough CRV not nearly enough rewards are going to be
* harvested for the test to pass.
*/
it.skip("should harvest and swap", async function () {
const { crv, OUSDmetaStrategy, dripper, harvester, timelock, usdt } =
fixture;

Expand Down
1 change: 1 addition & 0 deletions contracts/test/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ const getAssetAddresses = async (deployments) => {
aDAI_v2: addresses.mainnet.aDAI_v2,
aUSDC: addresses.mainnet.aUSDC,
aUSDT: addresses.mainnet.aUSDT,
aWETH: addresses.mainnet.aWETH,
AAVE: addresses.mainnet.Aave,
AAVE_TOKEN: addresses.mainnet.Aave,
AAVE_ADDRESS_PROVIDER: addresses.mainnet.AAVE_ADDRESS_PROVIDER,
Expand Down
Loading

0 comments on commit 36332fe

Please sign in to comment.