Skip to content

Commit

Permalink
Morpho eUSD Plugin (Base) (#1214)
Browse files Browse the repository at this point in the history
  • Loading branch information
julianmrodri authored Oct 30, 2024
1 parent db527b7 commit abbbb9f
Show file tree
Hide file tree
Showing 15 changed files with 279 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ jobs:
restore-keys: |
hardhat-network-fork-${{ runner.os }}-
hardhat-network-fork-
- run: yarn hardhat test ./test/plugins/individual-collateral/{cbeth,aave-v3,compoundv3,stargate,lido}/*.test.ts
- run: yarn hardhat test ./test/plugins/individual-collateral/{cbeth,aave-v3,compoundv3,stargate,lido,meta-morpho}/*.test.ts
env:
NODE_OPTIONS: '--max-old-space-size=32768'
TS_NODE_SKIP_IGNORE: true
Expand Down
4 changes: 4 additions & 0 deletions common/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export interface ITokens {
bbUSDT?: string
steakPYUSD?: string
Re7WETH?: string
meUSD?: string

pxETH?: string
apxETH?: string
Expand Down Expand Up @@ -516,6 +517,8 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
sUSDbC: '0x4c80e24119cfb836cdf0a6b53dc23f04f7e652ca',
wstETH: '0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452',
STG: '0xE3B53AF74a4BF62Ae5511055290838050bf764Df',
eUSD: '0xCfA3Ef56d303AE4fAabA0592388F19d7C3399FB4',
meUSD: '0xbb819D845b573B5D7C538F5b85057160cfb5f313',
},
chainlinkFeeds: {
DAI: '0x591e79239a7d679378ec8c847e5038150364c78f', // 0.3%, 24hr
Expand All @@ -532,6 +535,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
stETHETH: '0xf586d0728a47229e747d824a939000Cf21dEF5A0', // 0.5%, 24h
ETHUSD: '0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70', // 0.15%, 20min
wstETHstETH: '0xB88BAc61a4Ca37C43a3725912B1f472c9A5bc061', // 0.5%, 24h
eUSD: '0x9b2C948dbA5952A1f5Ab6fA16101c1392b8da1ab', // 0.5%, 24h
},
GNOSIS_EASY_AUCTION: '0xb1875Feaeea32Bbb02DE83D81772e07E37A40f02', // mock
COMET_REWARDS: '0x123964802e6ABabBE1Bc9547D72Ef1B69B00A6b1',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import { ERC4626FiatCollateral } from "../ERC4626FiatCollateral.sol";
* Expected: {tok} != {ref}, {ref} is pegged to {target} unless defaulting, {target} == {UoA}
*
* For example: steakUSDC, steakPYUSD, bbUSDT
*
* Rewards need to be claimed manually, from off-chain. This can be done permissionlessly,
* by anyone, on behalf of the RToken's Backing Manager address.
* For more information: https://docs.morpho.org/rewards/tutorials/claim-rewards/
*
*/
contract MetaMorphoFiatCollateral is ERC4626FiatCollateral {
/// config.erc20 must be a MetaMorpho ERC4626 vault
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ import { ERC4626FiatCollateral } from "../ERC4626FiatCollateral.sol";
* Expected: {tok} != {ref}, {ref} == {target}, {target} != {UoA}
*
* For example: Re7WETH
*
* Rewards will need to be claimed manually, off-chain. This can be done permissionlessly by anyone,
* on behalf of the RToken's Backing Manager address.
* For more information: https://docs.morpho.org/rewards/tutorials/claim-rewards/
*
*/
contract MetaMorphoSelfReferentialCollateral is ERC4626FiatCollateral {
using FixLib for uint192;
Expand Down
18 changes: 18 additions & 0 deletions contracts/plugins/assets/meta-morpho/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ Morpho Blue is a permisionless lending protocol. At the time of this writing (Ma

MetaMorpho suffers from a similar to that of the Curve volatile pools which can lose assets on admin fee claim.

## Reward claiming

Rewards can be claimed permissionlessly by anyone from off-chain, following this detailed guide from the Morpho docs:

https://docs.morpho.org/rewards/tutorials/claim-rewards/

It requires the following steps:

1. Querying the Morpho API with the holder address: https://rewards.morpho.org/v1/users/0xADDRESS/distributions
2. Retrieving the distributor contract and sending a transaction to `claim()` the rewards with the following parameters (all obtained from the previous call):
- `account`: the holder address
- `reward`: the address of the reward token
- `claimable`: the amount of reward tokens to claim
- `proof`: the merkle proof

It is important to note that in the case of Rtokens, rewards will need to be claimed on behalf of the Backing Manager.

## Target tokens

**USD**
Expand All @@ -14,6 +31,7 @@ MetaMorpho suffers from a similar to that of the Curve volatile pools which can
| Steakhouse USDC | steakUSDC| 0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB | wstETH, MORPHO |
| Steakhouse PYSUD | steakPYUSD | 0xbEEF02e5E13584ab96848af90261f0C8Ee04722a | MORPHO |
| Flagship USDT | bbUSDT| 0x2C25f6C25770fFEC5959D34B94Bf898865e5D6b1 | MORPHO |
| Morpho eUSD (Base) | meUSD | 0xbb819D845b573B5D7C538F5b85057160cfb5f313 | MORPHO |

**ETH**

Expand Down
8 changes: 5 additions & 3 deletions scripts/addresses/8453-tmp-assets-collateral.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"cbETH": "0x851B461a9744f4c9E996C03072cAB6f44Fa04d0D",
"saBasUSDC": "0xC19f5d60e2Aca1174f3D5Fe189f0A69afaB76f50",
"cUSDCv3": "0xf7a9D27c3B60c78c6F6e2c2d6ED6E8B94b352461",
"wstETH": "0x8b4374005291B8FCD14C4E947604b2FB3C660A73"
"wstETH": "0x8b4374005291B8FCD14C4E947604b2FB3C660A73",
"meUSD": "0x0f1e10871e6a2D3A5Aa696b85b39d61a22A9e8C3"
},
"erc20s": {
"COMP": "0x9e1028F5F1D5eDE59748FFceE5532509976840E0",
Expand All @@ -23,6 +24,7 @@
"saBasUSDC": "0x6F6f81e5E66f503184f2202D83a79650c3285759",
"STG": "0xE3B53AF74a4BF62Ae5511055290838050bf764Df",
"cUSDCv3": "0x53f1Df4E5591Ae35Bf738742981669c3767241FA",
"wstETH": "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452"
"wstETH": "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452",
"meUSD": "0xbb819D845b573B5D7C538F5b85057160cfb5f313"
}
}
}
9 changes: 9 additions & 0 deletions scripts/addresses/base-4.0.0/8453-tmp-assets-collateral.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"assets": {},
"collateral": {
"meUSD": "0x0f1e10871e6a2D3A5Aa696b85b39d61a22A9e8C3"
},
"erc20s": {
"meUSD": "0xbb819D845b573B5D7C538F5b85057160cfb5f313"
}
}
1 change: 1 addition & 0 deletions scripts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ async function main() {
'phase2-assets/collaterals/deploy_aave_v3_usdc.ts',
'phase2-assets/collaterals/deploy_lido_wsteth_collateral.ts',
'phase2-assets/collaterals/deploy_cbeth_collateral.ts',
'phase2-assets/collaterals/deploy_morphoeUSD.ts',
'phase2-assets/assets/deploy_stg.ts'
)
} else if (chainId == '42161' || chainId == '421614') {
Expand Down
94 changes: 94 additions & 0 deletions scripts/deployment/phase2-assets/collaterals/deploy_morphoeUSD.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import fs from 'fs'
import hre from 'hardhat'
import { getChainId } from '../../../../common/blockchain-utils'
import { baseL2Chains, networkConfig } from '../../../../common/configuration'
import { fp } from '../../../../common/numbers'
import { expect } from 'chai'
import { CollateralStatus } from '../../../../common/constants'
import {
getDeploymentFile,
getAssetCollDeploymentFilename,
IAssetCollDeployments,
getDeploymentFilename,
fileExists,
} from '../../common'
import {
eUSD_ORACLE_TIMEOUT,
eUSD_ORACLE_ERROR,
eUSD_USD_FEED,
PRICE_TIMEOUT,
DELAY_UNTIL_DEFAULT,
} from '../../../../test/plugins/individual-collateral/meta-morpho/constants'
import { MetaMorphoFiatCollateral } from '../../../../typechain'
import { ContractFactory } from 'ethers'

async function main() {
// ==== Read Configuration ====
const [deployer] = await hre.ethers.getSigners()

const chainId = await getChainId(hre)

console.log(`Deploying Collateral to network ${hre.network.name} (${chainId})
with burner account: ${deployer.address}`)

if (!networkConfig[chainId]) {
throw new Error(`Missing network configuration for ${hre.network.name}`)
}

// Get phase1 deployment
const phase1File = getDeploymentFilename(chainId)
if (!fileExists(phase1File)) {
throw new Error(`${phase1File} doesn't exist yet. Run phase 1`)
}
// Check previous step completed
const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId)
const assetCollDeployments = <IAssetCollDeployments>getDeploymentFile(assetCollDeploymentFilename)

const deployedCollateral: string[] = []

/******** Deploy MetaMorpho Morpho eUSD - meUSD **************************/

// Only for base
if (baseL2Chains.includes(hre.network.name)) {
const MetaMorphoFiatCollateralFactory: ContractFactory = await hre.ethers.getContractFactory(
'MetaMorphoFiatCollateral'
)

const collateral = <MetaMorphoFiatCollateral>await MetaMorphoFiatCollateralFactory.connect(
deployer
).deploy(
{
priceTimeout: PRICE_TIMEOUT.toString(),
chainlinkFeed: eUSD_USD_FEED,
oracleError: eUSD_ORACLE_ERROR.toString(),
erc20: networkConfig[chainId].tokens.meUSD,
maxTradeVolume: fp('1e6').toString(), // 17m vault
oracleTimeout: eUSD_ORACLE_TIMEOUT.toString(),
targetName: hre.ethers.utils.formatBytes32String('USD'),
defaultThreshold: eUSD_ORACLE_ERROR.add(fp('0.01')).toString(), // +1% buffer rule
delayUntilDefault: DELAY_UNTIL_DEFAULT.toString(),
},
fp('1e-4') // can have mild drawdowns
)
await collateral.deployed()

console.log(`Deployed meUSD to ${hre.network.name} (${chainId}): ${collateral.address}`)
await (await collateral.refresh()).wait()
expect(await collateral.status()).to.equal(CollateralStatus.SOUND)

assetCollDeployments.collateral.meUSD = collateral.address
assetCollDeployments.erc20s.meUSD = networkConfig[chainId].tokens.meUSD
deployedCollateral.push(collateral.address.toString())

fs.writeFileSync(assetCollDeploymentFilename, JSON.stringify(assetCollDeployments, null, 2))

console.log(`Deployed collateral to ${hre.network.name} (${chainId})
New deployments: ${deployedCollateral}
Deployment file: ${assetCollDeploymentFilename}`)
}
}

main().catch((error) => {
console.error(error)
process.exitCode = 1
})
60 changes: 60 additions & 0 deletions scripts/verification/collateral-plugins/verify_morphoeUSD.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import hre from 'hardhat'
import { getChainId } from '../../../common/blockchain-utils'
import { developmentChains, networkConfig } from '../../../common/configuration'
import { fp } from '../../../common/numbers'
import {
getDeploymentFile,
getAssetCollDeploymentFilename,
IAssetCollDeployments,
} from '../../deployment/common'
import {
eUSD_ORACLE_TIMEOUT,
eUSD_ORACLE_ERROR,
eUSD_USD_FEED,
PRICE_TIMEOUT,
DELAY_UNTIL_DEFAULT,
} from '../../../test/plugins/individual-collateral/meta-morpho/constants'
import { verifyContract } from '../../deployment/utils'

let deployments: IAssetCollDeployments

async function main() {
// ********** Read config **********
const chainId = await getChainId(hre)
if (!networkConfig[chainId]) {
throw new Error(`Missing network configuration for ${hre.network.name}`)
}

if (developmentChains.includes(hre.network.name)) {
throw new Error(`Cannot verify contracts for development chain ${hre.network.name}`)
}

const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId)
deployments = <IAssetCollDeployments>getDeploymentFile(assetCollDeploymentFilename)

/******** Verify meUSD **************************/
await verifyContract(
chainId,
deployments.collateral.meUSD,
[
{
priceTimeout: PRICE_TIMEOUT.toString(),
chainlinkFeed: eUSD_USD_FEED,
oracleError: eUSD_ORACLE_ERROR.toString(),
erc20: networkConfig[chainId].tokens.meUSD,
maxTradeVolume: fp('1e6').toString(),
oracleTimeout: eUSD_ORACLE_TIMEOUT.toString(),
targetName: hre.ethers.utils.formatBytes32String('USD'),
defaultThreshold: eUSD_ORACLE_ERROR.add(fp('0.01')).toString(), // +1% buffer rule
delayUntilDefault: DELAY_UNTIL_DEFAULT.toString(),
},
fp('1e-4'), // can have small drawdowns
],
'contracts/plugins/assets/meta-morpho/MetaMorphoFiatCollateral.sol:MetaMorphoFiatCollateral'
)
}

main().catch((error) => {
console.error(error)
process.exitCode = 1
})
1 change: 1 addition & 0 deletions scripts/verify_etherscan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ async function main() {
'collateral-plugins/verify_aave_v3_usdc.ts',
'collateral-plugins/verify_wsteth.ts',
'collateral-plugins/verify_cbeth.ts',
'collateral-plugins/verify_morphoeUSD.ts',
'assets/verify_stg.ts'
)
} else if (chainId == '42161' || chainId == '421614') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { networkConfig } from '#/common/configuration'
import { bn, fp } from '#/common/numbers'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
import { MockV3Aggregator } from '@typechain/MockV3Aggregator'
Expand All @@ -15,19 +14,30 @@ import { MAX_UINT192 } from '#/common/constants'
import {
DELAY_UNTIL_DEFAULT,
FORK_BLOCK,
STEAKPYUSD,
STEAKUSDC,
BBUSDT,
PYUSD_USD_FEED,
PYUSD_ORACLE_ERROR,
PYUSD_ORACLE_TIMEOUT,
USDT_USD_FEED,
USDT_ORACLE_TIMEOUT,
USDT_ORACLE_ERROR,
USDC_USD_FEED,
USDC_ORACLE_TIMEOUT,
USDC_ORACLE_ERROR,
eUSD_USD_FEED,
eUSD_ORACLE_TIMEOUT,
eUSD_ORACLE_ERROR,
PRICE_TIMEOUT,
MEUSD,
} from './constants'
import { mintCollateralTo } from './mintCollateralTo'

interface MAFiatCollateralOpts extends CollateralOpts {
defaultPrice?: BigNumberish
defaultRefPerTok?: BigNumberish
forkNetwork?: string
}

const makeFiatCollateralTestSuite = (
Expand Down Expand Up @@ -167,6 +177,7 @@ const makeFiatCollateralTestSuite = (
itChecksNonZeroDefaultThreshold: it,
itHasRevenueHiding: it,
resetFork: getResetFork(FORK_BLOCK),
targetNetwork: defaultCollateralOpts.forkNetwork,
collateralName,
chainlinkDefaultAnswer: defaultCollateralOpts.defaultPrice!,
itIsPricedByPeg: true,
Expand All @@ -180,7 +191,8 @@ const makeOpts = (
vault: string,
chainlinkFeed: string,
oracleTimeout: BigNumber,
oracleError: BigNumber
oracleError: BigNumber,
forkNetwork: string
): MAFiatCollateralOpts => {
return {
targetName: ethers.utils.formatBytes32String('USD'),
Expand All @@ -195,22 +207,28 @@ const makeOpts = (
defaultRefPerTok: fp('1'),
erc20: vault,
chainlinkFeed,
forkNetwork,
}
}

/*
Run the test suite
*/
const { tokens, chainlinkFeeds } = networkConfig[31337]

makeFiatCollateralTestSuite(
'MetaMorphoFiatCollateral - steakUSDC',
makeOpts(tokens.steakUSDC!, chainlinkFeeds.USDC!, USDC_ORACLE_TIMEOUT, USDC_ORACLE_ERROR)
makeOpts(STEAKUSDC, USDC_USD_FEED, USDC_ORACLE_TIMEOUT, USDC_ORACLE_ERROR, 'mainnet')
)
makeFiatCollateralTestSuite(
'MetaMorphoFiatCollateral - steakPYUSD',
makeOpts(tokens.steakPYUSD!, chainlinkFeeds.pyUSD!, PYUSD_ORACLE_TIMEOUT, PYUSD_ORACLE_ERROR)
makeOpts(STEAKPYUSD, PYUSD_USD_FEED, PYUSD_ORACLE_TIMEOUT, PYUSD_ORACLE_ERROR, 'mainnet')
)
makeFiatCollateralTestSuite(
'MetaMorphoFiatCollateral - bbUSDT',
makeOpts(tokens.bbUSDT!, chainlinkFeeds.USDT!, USDT_ORACLE_TIMEOUT, USDT_ORACLE_ERROR)
makeOpts(BBUSDT, USDT_USD_FEED, USDT_ORACLE_TIMEOUT, USDT_ORACLE_ERROR, 'mainnet')
)

makeFiatCollateralTestSuite(
'MetaMorphoFiatCollateral - meUSD',
makeOpts(MEUSD, eUSD_USD_FEED, eUSD_ORACLE_TIMEOUT, eUSD_ORACLE_ERROR, 'base')
)
Loading

0 comments on commit abbbb9f

Please sign in to comment.