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

tweak oracle factory to also support chainlink interface #1212

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
48 changes: 0 additions & 48 deletions contracts/facade/factories/CurveOracleFactory.sol

This file was deleted.

96 changes: 96 additions & 0 deletions contracts/facade/factories/ExchangeRateOracleFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

import { FIX_ONE, divuu } from "../../libraries/Fixed.sol";

// weird circular inheritance preventing us from using proper IRToken, not worth figuring out
interface IMinimalRToken {
function basketsNeeded() external view returns (uint192);

function totalSupply() external view returns (uint256);
}

/**
* @title ExchangeRateOracle
* @notice An immutable Exchange Rate Oracle for an RToken
*
* Warning! In the event of an RToken taking a loss in excess of the StRSR overcollateralization
* layer, the devaluation will not be reflected until the RToken is done trading. This causes
* the exchange rate to be too high during the rebalancing phase. If the exchange rate is relied
* upon naively, then it could be misleading.
*
* As a consumer of this oracle, you may want to guard against this case by monitoring:
* `rToken.status() == 0 && rToken.fullyCollateralized()`
*
* However, note that `fullyCollateralized()` is extremely gas-costly. We recommend executing
* the function off-chain. `status()` is cheap and more reasonable to be called from on-chain.
*/
contract ExchangeRateOracle {
error MissingRToken();

address public immutable rToken;

constructor(address _rToken) {
// allow address(0)
rToken = _rToken;
}

function exchangeRate() public view returns (uint256) {
address _rToken = rToken;
if (_rToken == address(0)) revert MissingRToken();

uint256 supply = IMinimalRToken(_rToken).totalSupply();
if (supply == 0) return FIX_ONE;

return divuu(uint256(IMinimalRToken(_rToken).basketsNeeded()), supply);
}

// basic chainlink interface sufficient for Morpho
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
{
// TODO
// make better to work with more than just Morpho
return (0, int256(exchangeRate()), 0, 0, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think startedAt and updatedAt can be zero here, but not sure if Morpho requires that (could use block.timestamp for both?). I'd also set the roundId and answeredInRound to 1 instead of 0.

}

function decimals() external pure returns (uint8) {
return 18;
}
}

/**
* @title ExchangeRateOracleFactory
* @notice An immutable factory for Exchange Rate Oracles
*/
contract ExchangeRateOracleFactory {
error OracleAlreadyDeployed();

event OracleDeployed(address indexed rToken, address indexed oracle);

// {rtoken} => {oracle}
mapping(address => ExchangeRateOracle) public oracles;

function deployOracle(address rToken) external returns (address) {
if (address(oracles[rToken]) != address(0)) revert OracleAlreadyDeployed();
ExchangeRateOracle oracle = new ExchangeRateOracle(rToken);

if (rToken != address(0)) {
oracle.exchangeRate();
oracle.latestRoundData();
oracle.decimals();
}

oracles[rToken] = oracle;
emit OracleDeployed(address(rToken), address(oracle));
return address(oracle);
}
}
56 changes: 0 additions & 56 deletions tasks/deployment/create-curve-oracle-factory.ts

This file was deleted.

78 changes: 78 additions & 0 deletions tasks/deployment/create-exchange-rate-oracle-factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { getChainId } from '../../common/blockchain-utils'
import { task, types } from 'hardhat/config'
import { ExchangeRateOracleFactory } from '../../typechain'

task('create-exchange-rate-oracle-factory', 'Deploys an ExchangeRateOracleFactory')
.addOptionalParam('noOutput', 'Suppress output', false, types.boolean)
.setAction(async (params, hre) => {
const [wallet] = await hre.ethers.getSigners()

const chainId = await getChainId(hre)

if (!params.noOutput) {
console.log(
`Deploying ExchangeRateOracleFactory to ${hre.network.name} (${chainId}) with burner account ${wallet.address}`
)
}

const CurveOracleFactoryFactory = await hre.ethers.getContractFactory(
'ExchangeRateOracleFactory'
)
const oracleFactory = <ExchangeRateOracleFactory>(
await CurveOracleFactoryFactory.connect(wallet).deploy()
)
await oracleFactory.deployed()

if (!params.noOutput) {
console.log(
`Deployed ExchangeRateOracleFactory to ${hre.network.name} (${chainId}): ${oracleFactory.address}`
)
console.log(
`Deploying dummy ExchangeRateOracle to ${hre.network.name} (${chainId}): ${oracleFactory.address}`
)
}

// Deploy dummy zero address oracle
const addr = await oracleFactory.callStatic.deployOracle(hre.ethers.constants.AddressZero)
await (await oracleFactory.deployOracle(hre.ethers.constants.AddressZero)).wait()

if (!params.noOutput) {
console.log(`Deployed dummy ExchangeRateOracle to ${hre.network.name} (${chainId}): ${addr}`)
}

// Uncomment to verify
if (!params.noOutput) {
console.log('sleeping 10s')
}

// Sleep to ensure API is in sync with chain
await new Promise((r) => setTimeout(r, 10000)) // 10s

if (!params.noOutput) {
console.log('verifying')
}

/** ******************** Verify ExchangeRateOracleFactory ****************************************/
console.time('Verifying ExchangeRateOracleFactory')
await hre.run('verify:verify', {
address: oracleFactory.address,
constructorArguments: [],
contract:
'contracts/facade/factories/ExchangeRateOracleFactory.sol:ExchangeRateOracleFactory',
})
console.timeEnd('Verifying ExchangeRateOracleFactory')

console.time('Verifying ExchangeRateOracle')
await hre.run('verify:verify', {
address: addr,
constructorArguments: [hre.ethers.constants.AddressZero],
contract: 'contracts/facade/factories/ExchangeRateOracleFactory.sol:ExchangeRateOracle',
})
console.timeEnd('Verifying ExchangeRateOracle')

if (!params.noOutput) {
console.log('verified')
}

return { oracleFactory: oracleFactory.address }
})
2 changes: 1 addition & 1 deletion tasks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import './deployment/mock/deploy-mock-aave'
import './deployment/mock/deploy-mock-wbtc'
import './deployment/deploy-easyauction'
import './deployment/create-deployer-registry'
import './deployment/create-curve-oracle-factory'
import './deployment/create-exchange-rate-oracle-factory'
import './deployment/deploy-facade-monitor'
import './deployment/empty-wallet'
import './deployment/cancel-tx'
Expand Down
Loading