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

Rename to sky branding #29

Merged
merged 2 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
# XChain DSR Oracle
# XChain SSR Oracle

Reports the DSR values across various bridges. This is primarily used as an exchange rate between DAI (USD) and sDAI for use by DEXs in capital efficiency liquidity amplification. Provided the three pot values (`dsr`, `chi` and `rho`) are synced you can extrapolate an exact exchange rate to any point in the future for as long as the `dsr` value does not get updated on mainnet. Because this oracle does not need to be synced unless the `dsr` changes, it can use the chain's canonical bridge for maximum security.
Reports the Sky Savings Rate (SSR) values across various bridges. This is primarily used as an exchange rate between USDS (USD) and sUSDS for use by DEXs+PSMs in capital efficiency liquidity amplification. Provided the three pot values (`ssr`, `chi` and `rho`) are synced you can extrapolate an exact exchange rate to any point in the future for as long as the `ssr` value does not get updated on mainnet. Because this oracle does not need to be synced unless the `ssr` changes, it can use the chain's canonical bridge for maximum security.

## Contracts

### DSROracleBase
### SSROracleBase

Common functionality shared between the Mainnet and XChain instances of the oracle. Contains convenience functions to fetch the conversion rate at various levels of precision trading off gas efficiency. Pot data is compressed into a single word to save SLOAD gas cost.

### DSRMainnetOracle
### SSRMainnetOracle

Mainnet instance pulls data directly from the `pot` as it is on the same chain. It's not clear the use case for this beyond consistency and some gas savings, but it was included none-the-less.

### DSRAuthOracle
### SSRAuthOracle

Oracle receives data from an authorized data provider. This is intended to be one or more bridges which publish data to the oracle. Application-level sanity checks are included when new data is proposed to minimize damage in the event of a bridge being compromised. These sanity checks also enforce event ordering in case messages are relayed out of order. `maxDSR` is used as an upper bound to prevent exchange rates that are wildly different from reality. It is recommended to sync this oracle somewhat frequently to minimize the damage of a compromised bridge.
Oracle receives data from an authorized data provider. This is intended to be one or more bridges which publish data to the oracle. Application-level sanity checks are included when new data is proposed to minimize damage in the event of a bridge being compromised. These sanity checks also enforce event ordering in case messages are relayed out of order. `maxSSR` is used as an upper bound to prevent exchange rates that are wildly different from reality. It is recommended to sync this oracle somewhat frequently to minimize the damage of a compromised bridge.

### Forwarders + Receivers

These are bridge-specific messaging contracts. Forwarders permissionlessly relay `pot` data. Receivers decode this message and forward it to the `DSRAuthOracle`. Please note that receivers are generic and part of the `xchain-helpers` repository.
These are bridge-specific messaging contracts. Forwarders permissionlessly relay `pot` data. Receivers decode this message and forward it to the `SSRAuthOracle`. Please note that receivers are generic and part of the `xchain-helpers` repository.

## Supported Chains

Expand All @@ -32,7 +32,11 @@ These are bridge-specific messaging contracts. Forwarders permissionlessly relay

Run `make deploy-XXX` where XXX is one of the supported networks. Be sure to have the `ETH_FROM` environment variable set to the deployer address as well as the relevant environment variables set for RPCs and contract verification. You can see contract verification api key names in `foundry.toml`.

## Deployments
## Deployments (USDS)

None

## Legacy Deployments (DAI)

### World Chain

Expand All @@ -41,8 +45,6 @@ AuthOracle (World Chain): [0x779053E25267B591Dcfbb20b2397462aaaD6B776](https://w
Receiver (World Chain): [0x33a3aB524A43E69f30bFd9Ae97d1Ec679FF00B64](https://worldchain-mainnet.explorer.alchemy.com/address/0x33a3aB524A43E69f30bFd9Ae97d1Ec679FF00B64?tab=contract)
Balancer Rate Provider (World Chain): [0xE206AEbca7B28e3E8d6787df00B010D4a77c32F3](https://worldchain-mainnet.explorer.alchemy.com/address/0xE206AEbca7B28e3E8d6787df00B010D4a77c32F3?tab=contract)

## Legacy Deployments

### Optimism

Forwarder (Ethereum): [0x4042127DecC0cF7cc0966791abebf7F76294DeF3](https://etherscan.io/address/0x4042127DecC0cF7cc0966791abebf7F76294DeF3#code)
Expand Down
28 changes: 14 additions & 14 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import "forge-std/Script.sol";

import { Gnosis } from "sparklend-address-registry/src/Gnosis.sol";

import { DSRBalancerRateProviderAdapter } from "src/adapters/DSRBalancerRateProviderAdapter.sol";
import { DSRAuthOracle } from "src/DSRAuthOracle.sol";
import { SSRBalancerRateProviderAdapter } from "src/adapters/SSRBalancerRateProviderAdapter.sol";
import { SSRAuthOracle } from "src/SSRAuthOracle.sol";

import { DSROracleForwarderOptimism, OptimismForwarder } from "src/forwarders/DSROracleForwarderOptimism.sol";
import { DSROracleForwarderGnosis } from "src/forwarders/DSROracleForwarderGnosis.sol";
import { DSROracleForwarderArbitrum, ArbitrumForwarder } from "src/forwarders/DSROracleForwarderArbitrum.sol";
import { SSROracleForwarderOptimism, OptimismForwarder } from "src/forwarders/SSROracleForwarderOptimism.sol";
import { SSROracleForwarderGnosis } from "src/forwarders/SSROracleForwarderGnosis.sol";
import { SSROracleForwarderArbitrum, ArbitrumForwarder } from "src/forwarders/SSROracleForwarderArbitrum.sol";

import { AMBReceiver } from "xchain-helpers/receivers/AMBReceiver.sol";
import { ArbitrumReceiver } from "xchain-helpers/receivers/ArbitrumReceiver.sol";
Expand Down Expand Up @@ -38,10 +38,10 @@ contract Deploy is Script {
vm.createSelectFork(remoteRpcUrl);

vm.startBroadcast();
DSRAuthOracle oracle = new DSRAuthOracle();
SSRAuthOracle oracle = new SSRAuthOracle();
address receiver = deployReceiver(forwarder, address(oracle));
require(receiver == expectedReceiver, "receiver mismatch");
DSRBalancerRateProviderAdapter adapter = new DSRBalancerRateProviderAdapter(oracle);
SSRBalancerRateProviderAdapter adapter = new SSRBalancerRateProviderAdapter(oracle);

// Configure
oracle.grantRole(oracle.DATA_PROVIDER_ROLE(), receiver);
Expand All @@ -53,8 +53,8 @@ contract Deploy is Script {

console.log("Deployed Forwarder at: ", forwarder);
console.log("Deployed Receiver at: ", receiver);
console.log("Deployed DSRAuthOracle at: ", address(oracle));
console.log("Deployed DSRBalancerRateProviderAdapter at:", address(adapter));
console.log("Deployed SSRAuthOracle at: ", address(oracle));
console.log("Deployed SSRBalancerRateProviderAdapter at:", address(adapter));
}

function deployForwarder(address) internal virtual returns (address) {
Expand All @@ -74,7 +74,7 @@ contract DeployOptimism is Deploy {
}

function deployForwarder(address receiver) internal override returns (address) {
return address(new DSROracleForwarderOptimism(MCD_POT, receiver, OptimismForwarder.L1_CROSS_DOMAIN_OPTIMISM));
return address(new SSROracleForwarderOptimism(MCD_POT, receiver, OptimismForwarder.L1_CROSS_DOMAIN_OPTIMISM));
}

function deployReceiver(address forwarder, address oracle) internal override returns (address) {
Expand All @@ -90,7 +90,7 @@ contract DeployBase is Deploy {
}

function deployForwarder(address receiver) internal override returns (address) {
return address(new DSROracleForwarderOptimism(MCD_POT, receiver, OptimismForwarder.L1_CROSS_DOMAIN_BASE));
return address(new SSROracleForwarderOptimism(MCD_POT, receiver, OptimismForwarder.L1_CROSS_DOMAIN_BASE));
}

function deployReceiver(address forwarder, address oracle) internal override returns (address) {
Expand All @@ -106,7 +106,7 @@ contract DeployWorldChain is Deploy {
}

function deployForwarder(address receiver) internal override returns (address) {
return address(new DSROracleForwarderOptimism(MCD_POT, receiver, OptimismForwarder.L1_CROSS_DOMAIN_WORLD_CHAIN));
return address(new SSROracleForwarderOptimism(MCD_POT, receiver, OptimismForwarder.L1_CROSS_DOMAIN_WORLD_CHAIN));
}

function deployReceiver(address forwarder, address oracle) internal override returns (address) {
Expand All @@ -122,7 +122,7 @@ contract DeployGnosis is Deploy {
}

function deployForwarder(address receiver) internal override returns (address) {
return address(new DSROracleForwarderGnosis(MCD_POT, receiver));
return address(new SSROracleForwarderGnosis(MCD_POT, receiver));
}

function deployReceiver(address forwarder, address oracle) internal override returns (address) {
Expand All @@ -138,7 +138,7 @@ contract DeployArbitrumOne is Deploy {
}

function deployForwarder(address receiver) internal override returns (address) {
return address(new DSROracleForwarderArbitrum(MCD_POT, receiver, ArbitrumForwarder.L1_CROSS_DOMAIN_ARBITRUM_ONE));
return address(new SSROracleForwarderArbitrum(MCD_POT, receiver, ArbitrumForwarder.L1_CROSS_DOMAIN_ARBITRUM_ONE));
}

function deployReceiver(address forwarder, address oracle) internal override returns (address) {
Expand Down
72 changes: 0 additions & 72 deletions src/DSRAuthOracle.sol

This file was deleted.

72 changes: 72 additions & 0 deletions src/SSRAuthOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.0;

import { AccessControl } from 'openzeppelin-contracts/contracts/access/AccessControl.sol';

import { SSROracleBase, ISSROracle } from './SSROracleBase.sol';
import { ISSRAuthOracle } from './interfaces/ISSRAuthOracle.sol';

/**
* @title SSRAuthOracle
* @notice SSR Oracle that allows permissioned setting of the pot data.
*/
contract SSRAuthOracle is AccessControl, SSROracleBase, ISSRAuthOracle {

uint256 private constant RAY = 1e27;

bytes32 public constant DATA_PROVIDER_ROLE = keccak256('DATA_PROVIDER_ROLE');

uint256 public maxSSR;

constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}

function setMaxSSR(uint256 _maxSSR) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(_maxSSR >= RAY || _maxSSR == 0, 'SSRAuthOracle/invalid-max-ssr');

maxSSR = _maxSSR;
emit SetMaxSSR(_maxSSR);
}

function setPotData(ISSROracle.PotData calldata nextData) external onlyRole(DATA_PROVIDER_ROLE) {
ISSROracle.PotData memory previousData = _data;

// Timestamp must be in the past
require(nextData.rho <= block.timestamp, 'SSRAuthOracle/invalid-rho');

// SSR lower bound
require(nextData.ssr >= RAY, 'SSRAuthOracle/invalid-ssr');

// Optional SSR upper bound
uint256 _maxSSR = maxSSR;
if (_maxSSR != 0) {
require(nextData.ssr <= _maxSSR, 'SSRAuthOracle/invalid-ssr');
}

if (_data.rho == 0) {
// This is a first update
// No need to run checks
_setPotData(nextData);
return;
}

// Perform sanity checks to minimize damage in case of malicious data being proposed

// Enforce non-decreasing values of rho in case of message reordering
// The same timestamp is allowed as the other values will only change upon increasing rho
require(nextData.rho >= previousData.rho, 'SSRAuthOracle/invalid-rho');

// `chi` must be non-decreasing
require(nextData.chi >= previousData.chi, 'SSRAuthOracle/invalid-chi');

// Accumulation cannot be larger than the time elapsed at the max ssr
if (_maxSSR != 0) {
uint256 chiMax = _rpow(_maxSSR, nextData.rho - previousData.rho) * previousData.chi / RAY;
require(nextData.chi <= chiMax, 'SSRAuthOracle/invalid-chi');
}

_setPotData(nextData);
}

}
14 changes: 7 additions & 7 deletions src/DSRMainnetOracle.sol → src/SSRMainnetOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ pragma solidity ^0.8.0;

import { IPot } from './interfaces/IPot.sol';

import { DSROracleBase, IDSROracle } from './DSROracleBase.sol';
import { SSROracleBase, ISSROracle } from './SSROracleBase.sol';

/**
* @title DSRMainnetOracle
* @notice DSR Oracle that sits on the same chain as MCD.
* @title SSRMainnetOracle
* @notice SSR Oracle that sits on the same chain as MCD.
*/
contract DSRMainnetOracle is DSROracleBase {
contract SSRMainnetOracle is SSROracleBase {

IPot public immutable pot;

Expand All @@ -22,11 +22,11 @@ contract DSRMainnetOracle is DSROracleBase {
/**
* @notice Will refresh the local storage with the updated values.
* @dev This does not need to be called that frequently as the values provide complete precision if needed.
* `refresh()` should be called immediately whenever the `dsr` value changes.
* `refresh()` should be called immediately whenever the `ssr` value changes.
*/
function refresh() public {
_setPotData(IDSROracle.PotData({
dsr: uint96(pot.dsr()),
_setPotData(ISSROracle.PotData({
ssr: uint96(pot.ssr()),
chi: uint120(pot.chi()),
rho: uint40(pot.rho())
}));
Expand Down
Loading
Loading