Skip to content

Commit

Permalink
USDe plugin and scripts (#1147)
Browse files Browse the repository at this point in the history
Co-authored-by: Taylor Brent <[email protected]>
  • Loading branch information
julianmrodri and tbrent authored Jul 30, 2024
1 parent 63bdb51 commit c5b08d9
Show file tree
Hide file tree
Showing 12 changed files with 490 additions and 5 deletions.
7 changes: 7 additions & 0 deletions common/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ export interface ITokens {
steakPYUSD?: string
Re7WETH?: string

// Ethena
USDe?: string
sUSDe?: string

// Mountain
USDM?: string
wUSDM?: string
Expand Down Expand Up @@ -245,6 +249,8 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
bbUSDT: '0x2C25f6C25770fFEC5959D34B94Bf898865e5D6b1',
Re7WETH: '0x78Fc2c2eD1A4cDb5402365934aE5648aDAd094d0',
sdUSDCUSDCPlus: '0x9bbF31E99F30c38a5003952206C31EEa77540BeF',
USDe: '0x4c9edd5852cd905f086c759e8383e09bff1e68b3',
sUSDe: '0x9D39A5DE30e57443BfF2A8307A4256c8797A3497',
},
chainlinkFeeds: {
RSR: '0x759bBC1be8F90eE6457C44abc7d443842a976d02',
Expand All @@ -271,6 +277,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
cbETH: '0xf017fcb346a1885194689ba23eff2fe6fa5c483b', // cbETH/ETH
frxETH: '0xc58f3385fbc1c8ad2c0c9a061d7c13b141d7a5df', // frxETH/ETH
pyUSD: '0x8f1dF6D7F2db73eECE86a18b4381F4707b918FB1',
USDe: '0xa569d910839Ae8865Da8F8e70FfFb0cBA869F961',
},
AAVE_INCENTIVES: '0xd784927Ff2f95ba542BfC824c8a8a98F3495f6b5',
AAVE_EMISSIONS_MGR: '0xEE56e2B3D491590B5b31738cC34d5232F378a8D5',
Expand Down
27 changes: 27 additions & 0 deletions contracts/plugins/assets/ethena/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Ethena USDe Collateral Plugin

## Summary

`USDe` is a synthetic dollar protocol built on Ethereum that derives its value from a delta-neutral basis trade based on funding rates: long ETH-LSD + short ETH. This combined position captures the funding rate on perpetual exchanges, which has been historically positive throughout crypto’s history, which is also used to provision an additional `sUSDe` token.

This plugin allows `sUSDe` holders to use their tokens as collateral in the Reserve Protocol.

`sUSDe` is a high-yield (today) ERC4626 vault, most similar to the DAI savings module. The redeemable USDe amount can be obtained by dividing `sUSDe.totalAssets()` by `sUSDe.totalSupply()`.

`USDe` contract: <https://etherscan.io/address/0x4c9edd5852cd905f086c759e8383e09bff1e68b3#code>

`sUSDe` contract: <https://etherscan.io/address/0x9D39A5DE30e57443BfF2A8307A4256c8797A3497#code>

## Implementation

### Units

| tok | ref | target | UoA |
| ----- | ---- | ------ | --- |
| sUSDe | USDe | USD | USD |

### Functions

#### refPerTok {ref/tok}

`return shiftl_toFix(erc4626.convertToAssets(oneShare), -refDecimals)`
25 changes: 25 additions & 0 deletions contracts/plugins/assets/ethena/USDeFiatCollateral.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

import { CollateralConfig } from "../AppreciatingFiatCollateral.sol";
import { ERC4626FiatCollateral } from "../ERC4626FiatCollateral.sol";

/**
* @title USDe Fiat Collateral
* @notice Collateral plugin for USDe (Ethena)
* tok = sUSDe (ERC4626 vault)
* ref = USDe
* tar = USD
* UoA = USD
*/

contract USDeFiatCollateral is ERC4626FiatCollateral {
/// config.erc20 must be sUSDe
/// @param config.chainlinkFeed Feed units: {UoA/ref}
/// @param revenueHiding {1} A value like 1e-6 that represents the maximum refPerTok to hide
constructor(CollateralConfig memory config, uint192 revenueHiding)
ERC4626FiatCollateral(config, revenueHiding)
{
require(config.defaultThreshold != 0, "defaultThreshold zero");
}
}
6 changes: 4 additions & 2 deletions scripts/addresses/1-tmp-assets-collateral.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
"Re7WETH": "0xa0a6C06e45437d4Ae1D778AaeB4605AC2B62A870",
"cvxCrvUSDUSDC": "0x9Fc0F31e2D26C437461a9eEBfe858d17e2611Ea5",
"cvxCrvUSDUSDT": "0x69c6597690B8Df61D15F201519C03725bdec40c1",
"sfrxETH": "0x4c891fCa6319d492866672E3D2AfdAAA5bDcfF67"
"sfrxETH": "0x4c891fCa6319d492866672E3D2AfdAAA5bDcfF67",
"sUSDe": "0x35081Ca24319835e5f759163F7e75eaB753e0b7E"
},
"erc20s": {
"stkAAVE": "0x4da27a545c0c5B758a6BA100e3a049001de870f5",
Expand Down Expand Up @@ -117,6 +118,7 @@
"cvxCrvUSDUSDT": "0x5d1B749bA7f689ef9f260EDC54326C48919cA88b",
"sfrxETH": "0xac3E018457B222d93114458476f3E3416Abbe38F",
"CRV": "0xD533a949740bb3306d119CC777fa900bA034cd52",
"CVX": "0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B"
"CVX": "0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B",
"sUSDe": "0x9D39A5DE30e57443BfF2A8307A4256c8797A3497"
}
}
6 changes: 4 additions & 2 deletions scripts/addresses/mainnet-3.4.0/1-tmp-assets-collateral.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
"Re7WETH": "0xa0a6C06e45437d4Ae1D778AaeB4605AC2B62A870",
"cvxCrvUSDUSDC": "0x9Fc0F31e2D26C437461a9eEBfe858d17e2611Ea5",
"cvxCrvUSDUSDT": "0x69c6597690B8Df61D15F201519C03725bdec40c1",
"sfrxETH": "0x4c891fCa6319d492866672E3D2AfdAAA5bDcfF67"
"sfrxETH": "0x4c891fCa6319d492866672E3D2AfdAAA5bDcfF67",
"sUSDe": "0x35081Ca24319835e5f759163F7e75eaB753e0b7E"
},
"erc20s": {
"stkAAVE": "0x4da27a545c0c5B758a6BA100e3a049001de870f5",
Expand Down Expand Up @@ -117,6 +118,7 @@
"cvxCrvUSDUSDT": "0x5d1B749bA7f689ef9f260EDC54326C48919cA88b",
"sfrxETH": "0xac3E018457B222d93114458476f3E3416Abbe38F",
"CRV": "0xD533a949740bb3306d119CC777fa900bA034cd52",
"CVX": "0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B"
"CVX": "0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B",
"sUSDe": "0x9D39A5DE30e57443BfF2A8307A4256c8797A3497"
}
}
1 change: 1 addition & 0 deletions scripts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ async function main() {
'phase2-assets/collaterals/deploy_steakpyusd.ts',
'phase2-assets/collaterals/deploy_bbusdt.ts',
'phase2-assets/collaterals/deploy_re7weth.ts',
'phase2-assets/collaterals/deploy_USDe.ts',
'phase2-assets/assets/deploy_crv.ts',
'phase2-assets/assets/deploy_cvx.ts'
)
Expand Down
92 changes: 92 additions & 0 deletions scripts/deployment/phase2-assets/collaterals/deploy_USDe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import fs from 'fs'
import hre from 'hardhat'
import { getChainId } from '../../../../common/blockchain-utils'
import { 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 { USDeFiatCollateral } from '../../../../typechain'
import { ContractFactory } from 'ethers'
import {
DELAY_UNTIL_DEFAULT,
PRICE_TIMEOUT,
ORACLE_TIMEOUT,
ORACLE_ERROR,
} from '../../../../test/plugins/individual-collateral/ethena/constants'

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 USDe Collateral - sUSDe **************************/
let collateral: USDeFiatCollateral

const USDeFiatCollateralFactory: ContractFactory = await hre.ethers.getContractFactory(
'USDeFiatCollateral'
)

collateral = <USDeFiatCollateral>await USDeFiatCollateralFactory.connect(deployer).deploy(
{
priceTimeout: PRICE_TIMEOUT.toString(),
chainlinkFeed: networkConfig[chainId].chainlinkFeeds.USDe,
oracleError: ORACLE_ERROR.toString(),
erc20: networkConfig[chainId].tokens.sUSDe,
maxTradeVolume: fp('1e6').toString(), // $1m,
oracleTimeout: ORACLE_TIMEOUT.toString(), // 24 hr
targetName: hre.ethers.utils.formatBytes32String('USD'),
defaultThreshold: fp('0.01').add(ORACLE_ERROR).toString(), // ~1.5%
delayUntilDefault: DELAY_UNTIL_DEFAULT.toString(), // 72h
},
fp('1e-6').toString()
)

await collateral.deployed()

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

assetCollDeployments.collateral.sUSDe = collateral.address
assetCollDeployments.erc20s.sUSDe = networkConfig[chainId].tokens.sUSDe
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
})
59 changes: 59 additions & 0 deletions scripts/verification/collateral-plugins/verify_USDe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
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 { verifyContract } from '../../deployment/utils'
import {
PRICE_TIMEOUT,
ORACLE_ERROR,
ORACLE_TIMEOUT,
DELAY_UNTIL_DEFAULT,
} from '../../../test/plugins/individual-collateral/ethena/constants'

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 sUSDe COllateral **************************/
await verifyContract(
chainId,
deployments.collateral.sUSDe,
[
{
priceTimeout: PRICE_TIMEOUT.toString(),
chainlinkFeed: networkConfig[chainId].chainlinkFeeds.USDe,
oracleError: ORACLE_ERROR.toString(),
erc20: networkConfig[chainId].tokens.sUSDe,
maxTradeVolume: fp('1e6').toString(), // $1m,
oracleTimeout: ORACLE_TIMEOUT.toString(),
targetName: hre.ethers.utils.formatBytes32String('USD'),
defaultThreshold: fp('0.01').add(ORACLE_ERROR).toString(),
delayUntilDefault: DELAY_UNTIL_DEFAULT.toString(),
},
fp('1e-6').toString(),
],
'contracts/plugins/assets/ethena/USDeFiatCollateral.sol:USDeFiatCollateral'
)
}

main().catch((error) => {
console.error(error)
process.exitCode = 1
})
3 changes: 2 additions & 1 deletion scripts/verify_etherscan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ async function main() {
'collateral-plugins/verify_sfrax.ts',
'collateral-plugins/verify_sfrax_eth.ts',
'collateral-plugins/verify_steakusdc.ts',
'collateral-plugins/verify_re7weth.ts'
'collateral-plugins/verify_re7weth.ts',
'collateral-plugins/verify_USDe.ts'
)
} else if (chainId == '8453' || chainId == '84531') {
// Base L2 chains
Expand Down
Loading

0 comments on commit c5b08d9

Please sign in to comment.