Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deployment + verification scripts for sFRAX #1010

Merged
merged 5 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ jobs:
- run: yarn devchain &
env:
MAINNET_RPC_URL: https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161
FORK_BLOCK: 18329921
FORK_NETWORK: mainnet
- run: yarn deploy:run --network localhost
env:
Expand Down
15 changes: 9 additions & 6 deletions common/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export interface ITokens {
cUSDCv3?: string
cUSDbCv3?: string
ONDO?: string
sFRAX?: string
sDAI?: string
cbETH?: string
STG?: string
Expand Down Expand Up @@ -177,6 +178,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
rETH: '0xae78736Cd615f374D3085123A210448E74Fc6393',
cUSDCv3: '0xc3d688B66703497DAA19211EEdff47f25384cdc3',
ONDO: '0xfAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3',
sFRAX: '0xA663B02CF0a4b149d2aD41910CB81e23e1c41c32',
sDAI: '0x83f20f44975d03b1b09e64809b757c47f942beea',
cbETH: '0xBe9895146f7AF43049ca1c1AE358B0541Ea49704',
STG: '0xAf5191B0De278C7286d6C7CC6ab6BB8A73bA2Cd6',
Expand Down Expand Up @@ -228,7 +230,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
COMET_EXT: '0x285617313887d43256F852cAE0Ee4de4b68D45B0',
AAVE_V3_POOL: '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2',
AAVE_V3_INCENTIVES_CONTROLLER: '0x8164Cc65827dcFe994AB23944CBC90e0aa80bFcb',
STARGATE_STAKING_CONTRACT: '0xB0D502E938ed5f4df2E681fE6E419ff29631d62b'
STARGATE_STAKING_CONTRACT: '0xB0D502E938ed5f4df2E681fE6E419ff29631d62b',
},
'1': {
name: 'mainnet',
Expand Down Expand Up @@ -279,6 +281,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
rETH: '0xae78736Cd615f374D3085123A210448E74Fc6393',
cUSDCv3: '0xc3d688B66703497DAA19211EEdff47f25384cdc3',
ONDO: '0xfAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3',
sFRAX: '0xA663B02CF0a4b149d2aD41910CB81e23e1c41c32',
sDAI: '0x83f20f44975d03b1b09e64809b757c47f942beea',
cbETH: '0xBe9895146f7AF43049ca1c1AE358B0541Ea49704',
STG: '0xAf5191B0De278C7286d6C7CC6ab6BB8A73bA2Cd6',
Expand Down Expand Up @@ -327,7 +330,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
COMET_EXT: '0x285617313887d43256F852cAE0Ee4de4b68D45B0',
AAVE_V3_POOL: '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2',
AAVE_V3_INCENTIVES_CONTROLLER: '0x8164Cc65827dcFe994AB23944CBC90e0aa80bFcb',
STARGATE_STAKING_CONTRACT: '0xB0D502E938ed5f4df2E681fE6E419ff29631d62b'
STARGATE_STAKING_CONTRACT: '0xB0D502E938ed5f4df2E681fE6E419ff29631d62b',
},
'3': {
name: 'tenderly',
Expand Down Expand Up @@ -426,7 +429,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
COMET_EXT: '0x285617313887d43256F852cAE0Ee4de4b68D45B0',
AAVE_V3_POOL: '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2',
AAVE_V3_INCENTIVES_CONTROLLER: '0x8164Cc65827dcFe994AB23944CBC90e0aa80bFcb',
STARGATE_STAKING_CONTRACT: '0xB0D502E938ed5f4df2E681fE6E419ff29631d62b'
STARGATE_STAKING_CONTRACT: '0xB0D502E938ed5f4df2E681fE6E419ff29631d62b',
},
'5': {
name: 'goerli',
Expand Down Expand Up @@ -530,7 +533,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
aWETHv3: '0xD4a0e0b9149BCee3C920d2E00b5dE09138fd8bb7',
acbETHv3: '0xcf3D55c10DB69f28fD1A75Bd73f3D8A2d9c595ad',
sUSDbC: '0x4c80e24119cfb836cdf0a6b53dc23f04f7e652ca',
STG: '0xE3B53AF74a4BF62Ae5511055290838050bf764Df'
STG: '0xE3B53AF74a4BF62Ae5511055290838050bf764Df',
},
chainlinkFeeds: {
DAI: '0x591e79239a7d679378ec8c847e5038150364c78f', // 0.3%, 24hr
Expand All @@ -543,7 +546,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
RSR: '0xAa98aE504658766Dfe11F31c5D95a0bdcABDe0b1', // 2%, 24hr
wstETHstETHexr: '0xB88BAc61a4Ca37C43a3725912B1f472c9A5bc061', // 0.5%, 24hr
cbETHETHexr: '0x868a501e68F3D1E89CfC0D22F6b22E8dabce5F04', // 0.5%, 24hr
STG: '0x63Af8341b62E683B87bB540896bF283D96B4D385'
STG: '0x63Af8341b62E683B87bB540896bF283D96B4D385',
},
GNOSIS_EASY_AUCTION: '0xb1875Feaeea32Bbb02DE83D81772e07E37A40f02', // mock
COMET_REWARDS: '0x123964802e6ABabBE1Bc9547D72Ef1B69B00A6b1',
Expand All @@ -552,7 +555,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
COMET_EXT: '0x2F9E3953b2Ef89fA265f2a32ed9F80D00229125B',
AAVE_V3_POOL: '0xA238Dd80C259a72e81d7e4664a9801593F98d1c5',
AAVE_V3_INCENTIVES_CONTROLLER: '0xf9cc4F0D883F1a1eb2c253bdb46c254Ca51E1F44',
STARGATE_STAKING_CONTRACT: '0x06Eb48763f117c7Be887296CDcdfad2E4092739C'
STARGATE_STAKING_CONTRACT: '0x06Eb48763f117c7Be887296CDcdfad2E4092739C',
},
}

Expand Down
33 changes: 33 additions & 0 deletions contracts/plugins/assets/frax/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Staked FRAX (sFRAX) Collateral Plugin

## Summary

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

sFRAX is a non-upgradeable ERC4626 vault that earns the user the right to an increasing quantity of FRAX over time. The income stream is administered through a timelock + multisig. The only control the timelock has over the vault is the ability to change the rate of interest accrual. At all times `sFRAX` can be redeemed for a prorata portion of the held `FRAX`.

The timelock + multisig targets a rate of appreciation for `sFRAX` equal to the IORB, or the FED's **interest rate on reserve balances**. In the background, an AMO puts FRAX to work in defi in order to try to cover as much of the interest as possible. Any interest that is not found defi is covered by FXS. If the frax protocol were unable to make good on the targeted IORB rate, they would either have to drop the `sFRAX` yield or risk de-pegging FRAX, which would begin to be become undercollateralized.

Since it is ERC4626, the redeemable FRAX amount can be gotten by dividing `sFRAX.totalAssets()` by `sFRAX.totalSupply()`.

No function needs be called in order to update `refPerTok()`. `totalAssets()` is already a function of the block timestamp and increases as time passes.

We can use 0 revenue hiding since the vault correctly rounds defensively in favor of `sFRAX` holders during deposit/withdrawal (thx t11s).

No rewards other than the ever-increasing exchange rate.

`sFRAX` contract: <https://etherscan.io/address/0xA663B02CF0a4b149d2aD41910CB81e23e1c41c32#code>

## Implementation

### Units

| tok | ref | target | UoA |
| ----- | ---- | ------ | --- |
| sFRAX | FRAX | USD | USD |

### Functions

#### refPerTok {ref/tok}

`return divuu(IStakedFrax(address(erc20)).totalAssets(), IStakedFrax(address(erc20)).totalSupply());`
38 changes: 38 additions & 0 deletions contracts/plugins/assets/frax/SFraxCollateral.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

import "../../../libraries/Fixed.sol";
import "../../../vendor/oz/IERC4626.sol";
import "../AppreciatingFiatCollateral.sol";

interface IStakedFrax is IERC4626 {
function syncRewardsAndDistribution() external;
}

/**
* @title sFRAX Collateral
* @notice Collateral plugin for staked FRAX (sFRAX)
* tok = sFRAX (ERC4626 vault)
* ref = FRAX
* tar = USD
* UoA = USD
*/
contract SFraxCollateral is AppreciatingFiatCollateral {
// solhint-disable no-empty-blocks

/// @param config.chainlinkFeed {UoA/ref} price of DAI in USD terms
constructor(CollateralConfig memory config, uint192 revenueHiding)
AppreciatingFiatCollateral(config, revenueHiding)
{}

// solhint-enable no-empty-blocks

/// @return {ref/tok} Actual quantity of whole reference units per whole collateral tokens
function _underlyingRefPerTok() internal view override returns (uint192) {
return
divuu(
IStakedFrax(address(erc20)).totalAssets(),
IStakedFrax(address(erc20)).totalSupply()
);
}
}
7 changes: 4 additions & 3 deletions scripts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ async function main() {
// See `confirm.ts` for part 2

// Phase 1- Implementations
let scripts = [
const scripts = [
'phase1-common/0_setup_deployments.ts',
'phase1-common/1_deploy_libraries.ts',
'phase1-common/2_deploy_implementations.ts',
Expand Down Expand Up @@ -61,7 +61,8 @@ async function main() {
'phase2-assets/collaterals/deploy_dsr_sdai.ts',
'phase2-assets/collaterals/deploy_cbeth_collateral.ts',
'phase2-assets/collaterals/deploy_morpho_aavev2_plugin.ts',
'phase2-assets/collaterals/deploy_aave_v3_usdc.ts'
'phase2-assets/collaterals/deploy_aave_v3_usdc.ts',
'phase2-assets/collaterals/deploy_sfrax.ts'
)
} else if (chainId == '8453' || chainId == '84531') {
// Base L2 chains
Expand All @@ -73,7 +74,7 @@ async function main() {
'phase2-assets/collaterals/deploy_ctokenv3_usdbc_collateral.ts',
'phase2-assets/collaterals/deploy_aave_v3_usdbc.ts',
'phase2-assets/collaterals/deploy_stargate_usdc_collateral.ts',
'phase2-assets/assets/deploy_stg.ts',
'phase2-assets/assets/deploy_stg.ts'
)
}

Expand Down
83 changes: 83 additions & 0 deletions scripts/deployment/phase2-assets/collaterals/deploy_sfrax.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import fs from 'fs'
import hre from 'hardhat'
import { getChainId } from '../../../../common/blockchain-utils'
import { networkConfig } from '../../../../common/configuration'
import { bn, fp } from '../../../../common/numbers'
import { expect } from 'chai'
import { CollateralStatus } from '../../../../common/constants'
import {
getDeploymentFile,
getAssetCollDeploymentFilename,
IAssetCollDeployments,
getDeploymentFilename,
fileExists,
} from '../../common'
import { priceTimeout, oracleTimeout } from '../../utils'
import { SFraxCollateral } 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 SFRAX Collateral - sFRAX **************************/

const SFraxCollateralFactory: ContractFactory = await hre.ethers.getContractFactory(
'SFraxCollateral'
)

const collateral = <SFraxCollateral>await SFraxCollateralFactory.connect(deployer).deploy(
{
priceTimeout: priceTimeout.toString(),
chainlinkFeed: networkConfig[chainId].chainlinkFeeds.FRAX,
oracleError: fp('0.01').toString(), // 1%
erc20: networkConfig[chainId].tokens.sFRAX,
maxTradeVolume: fp('1e6').toString(), // $1m,
oracleTimeout: oracleTimeout(chainId, '3600').toString(), // 1 hr
targetName: hre.ethers.utils.formatBytes32String('USD'),
defaultThreshold: fp('0.02').toString(), // 2% = 1% oracleError + 1% buffer
delayUntilDefault: bn('86400').toString(), // 24h
tbrent marked this conversation as resolved.
Show resolved Hide resolved
},
'0' // revenueHiding = 0
)
await collateral.deployed()

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

assetCollDeployments.collateral.sFRAX = collateral.address
assetCollDeployments.erc20s.sFRAX = networkConfig[chainId].tokens.sFRAX
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
})
53 changes: 53 additions & 0 deletions scripts/verification/collateral-plugins/verify_sfrax.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import hre from 'hardhat'
import { getChainId } from '../../../common/blockchain-utils'
import { developmentChains, networkConfig } from '../../../common/configuration'
import { fp, bn } from '../../../common/numbers'
import {
getDeploymentFile,
getAssetCollDeploymentFilename,
IAssetCollDeployments,
} from '../../deployment/common'
import { priceTimeout, oracleTimeout, 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 sFRAX **************************/
await verifyContract(
chainId,
deployments.collateral.sFRAX,
[
{
priceTimeout: priceTimeout.toString(),
chainlinkFeed: networkConfig[chainId].chainlinkFeeds.FRAX,
oracleError: fp('0.01').toString(), // 1%
erc20: networkConfig[chainId].tokens.sFRAX,
maxTradeVolume: fp('1e6').toString(), // $1m,
oracleTimeout: oracleTimeout(chainId, '3600').toString(), // 1 hr
targetName: hre.ethers.utils.formatBytes32String('USD'),
defaultThreshold: fp('0.02').toString(), // 2%
delayUntilDefault: bn('86400').toString(), // 24h
},
'0', // revenueHiding = 0
],
'contracts/plugins/assets/frax/SFraxCollateral.sol:SFraxCollateral'
)
}

main().catch((error) => {
console.error(error)
process.exitCode = 1
})
5 changes: 3 additions & 2 deletions scripts/verify_etherscan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async function main() {
// even if some portions have already been verified

// Phase 1- Common
let scripts = [
const scripts = [
'0_verify_libraries.ts',
'1_verify_implementations.ts',
'2_verify_rsrAsset.ts',
Expand All @@ -61,7 +61,8 @@ async function main() {
'collateral-plugins/verify_cbeth.ts',
'collateral-plugins/verify_sdai.ts',
'collateral-plugins/verify_morpho.ts',
'collateral-plugins/verify_aave_v3_usdc.ts'
'collateral-plugins/verify_aave_v3_usdc.ts',
'collateral-plugins/verify_sfrax.ts'
)
} else if (chainId == '8453' || chainId == '84531') {
// Base L2 chains
Expand Down
2 changes: 1 addition & 1 deletion test/integration/fork-block-numbers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const forkBlockNumber = {
'mainnet-deployment': 15690042, // Ethereum
'flux-finance': 16836855, // Ethereum
'mainnet-2.0': 17522362, // Ethereum
default: 16934828, // Ethereum
default: 18522901, // Ethereum
}

export default forkBlockNumber
Loading