// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
/**
* @title Assertions
* @notice This library contains assertions for common requirement checks
*/
library Assertions {
// --- Errors ---
/// @dev Throws if `_x` is not greater than `_y`
error NotGreaterThan(uint256 _x, uint256 _y);
/// @dev Throws if `_x` is not lesser than `_y`
error NotLesserThan(uint256 _x, uint256 _y);
/// @dev Throws if `_x` is not greater than or equal to `_y`
error NotGreaterOrEqualThan(uint256 _x, uint256 _y);
/// @dev Throws if `_x` is not lesser than or equal to `_y`
error NotLesserOrEqualThan(uint256 _x, uint256 _y);
/// @dev Throws if `_x` is not greater than `_y`
error IntNotGreaterThan(int256 _x, int256 _y);
/// @dev Throws if `_x` is not lesser than `_y`
error IntNotLesserThan(int256 _x, int256 _y);
/// @dev Throws if `_x` is not greater than or equal to `_y`
error IntNotGreaterOrEqualThan(int256 _x, int256 _y);
/// @dev Throws if `_x` is not lesser than or equal to `_y`
error IntNotLesserOrEqualThan(int256 _x, int256 _y);
/// @dev Throws if checked amount is null
error NullAmount();
/// @dev Throws if checked address is null
error NullAddress();
/// @dev Throws if checked address contains no code
error NoCode(address _contract);
// --- Assertions ---
/// @dev Asserts that `_x` is greater than `_y` and returns `_x`
function assertGt(uint256 _x, uint256 _y) internal pure returns (uint256 __x) {
if (_x <= _y) revert NotGreaterThan(_x, _y);
return _x;
}
/// @dev Asserts that `_x` is greater than `_y` and returns `_x`
function assertGt(int256 _x, int256 _y) internal pure returns (int256 __x) {
if (_x <= _y) revert IntNotGreaterThan(_x, _y);
return _x;
}
/// @dev Asserts that `_x` is greater than or equal to `_y` and returns `_x`
function assertGtEq(uint256 _x, uint256 _y) internal pure returns (uint256 __x) {
if (_x < _y) revert NotGreaterOrEqualThan(_x, _y);
return _x;
}
/// @dev Asserts that `_x` is greater than or equal to `_y` and returns `_x`
function assertGtEq(int256 _x, int256 _y) internal pure returns (int256 __x) {
if (_x < _y) revert IntNotGreaterOrEqualThan(_x, _y);
return _x;
}
/// @dev Asserts that `_x` is lesser than `_y` and returns `_x`
function assertLt(uint256 _x, uint256 _y) internal pure returns (uint256 __x) {
if (_x >= _y) revert NotLesserThan(_x, _y);
return _x;
}
/// @dev Asserts that `_x` is lesser than `_y` and returns `_x`
function assertLt(int256 _x, int256 _y) internal pure returns (int256 __x) {
if (_x >= _y) revert IntNotLesserThan(_x, _y);
return _x;
}
/// @dev Asserts that `_x` is lesser than or equal to `_y` and returns `_x`
function assertLtEq(uint256 _x, uint256 _y) internal pure returns (uint256 __x) {
if (_x > _y) revert NotLesserOrEqualThan(_x, _y);
return _x;
}
/// @dev Asserts that `_x` is lesser than or equal to `_y` and returns `_x`
function assertLtEq(int256 _x, int256 _y) internal pure returns (int256 __x) {
if (_x > _y) revert IntNotLesserOrEqualThan(_x, _y);
return _x;
}
/// @dev Asserts that `_x` is not null and returns `_x`
function assertNonNull(uint256 _x) internal pure returns (uint256 __x) {
if (_x == 0) revert NullAmount();
return _x;
}
/// @dev Asserts that `_address` is not null and returns `_address`
function assertNonNull(address _address) internal pure returns (address __address) {
if (_address == address(0)) revert NullAddress();
return _address;
}
/// @dev Asserts that `_address` contains code and returns `_address`
function assertHasCode(address _address) internal view returns (address __address) {
if (_address.code.length == 0) revert NoCode(_address);
return _address;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
import {IAuthorizable} from '@interfaces/utils/IAuthorizable.sol';
import {EnumerableSet} from '@openzeppelin/utils/structs/EnumerableSet.sol';
/**
* @title Authorizable
* @notice Implements authorization control for contracts
* @dev Authorization control is boolean and handled by `isAuthorized` modifier
*/
abstract contract Authorizable is IAuthorizable {
using EnumerableSet for EnumerableSet.AddressSet;
// --- Data ---
/// @notice EnumerableSet of authorized accounts
EnumerableSet.AddressSet internal _authorizedAccounts;
// --- Init ---
/**
* @param _account Initial account to add authorization to
*/
constructor(address _account) {
_addAuthorization(_account);
}
// --- Views ---
/**
* @notice Checks whether an account is authorized
* @return _authorized Whether the account is authorized or not
*/
function authorizedAccounts(address _account) external view returns (bool _authorized) {
return _isAuthorized(_account);
}
/**
* @notice Getter for the authorized accounts
* @return _accounts Array of authorized accounts
*/
function authorizedAccounts() external view returns (address[] memory _accounts) {
return _authorizedAccounts.values();
}
// --- Methods ---
/**
* @notice Add auth to an account
* @param _account Account to add auth to
*/
function addAuthorization(address _account) external virtual isAuthorized {
_addAuthorization(_account);
}
/**
* @notice Remove auth from an account
* @param _account Account to remove auth from
*/
function removeAuthorization(address _account) external virtual isAuthorized {
_removeAuthorization(_account);
}
// --- Internal methods ---
function _addAuthorization(address _account) internal {
if (_account == address(0)) revert NullAddress();
if (_authorizedAccounts.add(_account)) {
emit AddAuthorization(_account);
} else {
revert AlreadyAuthorized();
}
}
function _removeAuthorization(address _account) internal {
if (_authorizedAccounts.remove(_account)) {
emit RemoveAuthorization(_account);
} else {
revert NotAuthorized();
}
}
function _isAuthorized(address _account) internal view virtual returns (bool _authorized) {
return _authorizedAccounts.contains(_account);
}
// --- Modifiers ---
/**
* @notice Checks whether msg.sender can call an authed function
* @dev Will revert with `Unauthorized` if the sender is not authorized
*/
modifier isAuthorized() {
if (!_isAuthorized(msg.sender)) revert Unauthorized();
_;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
/**
* @title Encoding
* @notice This library contains functions for decoding data into common types
*/
library Encoding {
// --- Methods ---
/// @dev Decodes a bytes array into a uint256
function toUint256(bytes memory _data) internal pure returns (uint256 _uint256) {
assembly {
_uint256 := mload(add(_data, 0x20))
}
}
/// @dev Decodes a bytes array into an int256
function toInt256(bytes memory _data) internal pure returns (int256 _int256) {
assembly {
_int256 := mload(add(_data, 0x20))
}
}
/// @dev Decodes a bytes array into an address
function toAddress(bytes memory _data) internal pure returns (address _address) {
assembly {
_address := mload(add(_data, 0x20))
}
}
/// @dev Decodes a bytes array into a bool
function toBool(bytes memory _data) internal pure returns (bool _bool) {
assembly {
_bool := mload(add(_data, 0x20))
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
interface IAuthorizable {
// --- Events ---
/**
* @notice Emitted when an account is authorized
* @param _account Account that is authorized
*/
event AddAuthorization(address _account);
/**
* @notice Emitted when an account is unauthorized
* @param _account Account that is unauthorized
*/
event RemoveAuthorization(address _account);
// --- Errors ---
/// @notice Throws if the account is already authorized on `addAuthorization`
error AlreadyAuthorized(); // 0x6027d27e
/// @notice Throws if the account is not authorized on `removeAuthorization`
error NotAuthorized(); // 0xea8e4eb5
/// @notice Throws if the account is not authorized and tries to call an `onlyAuthorized` method
error Unauthorized(); // 0x82b42900
/// @notice Throws if zero address is passed
error NullAddress();
// --- Data ---
/**
* @notice Checks whether an account is authorized on the contract
* @param _account Account to check
* @return _authorized Whether the account is authorized or not
*/
function authorizedAccounts(address _account) external view returns (bool _authorized);
/**
* @notice Getter for the authorized accounts
* @return _accounts Array of authorized accounts
*/
function authorizedAccounts() external view returns (address[] memory _accounts);
// --- Administration ---
/**
* @notice Add authorization to an account
* @param _account Account to add authorization to
* @dev Method will revert if the account is already authorized
*/
function addAuthorization(address _account) external;
/**
* @notice Remove authorization from an account
* @param _account Account to remove authorization from
* @dev Method will revert if the account is not authorized
*/
function removeAuthorization(address _account) external;
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
/**
* @title IBaseOracle
* @notice Basic interface for a system price feed
* All price feeds should be translated into an 18 decimals format
*/
interface IBaseOracle {
// --- Errors ---
error InvalidPriceFeed();
/**
* @notice Symbol of the quote: token / baseToken (e.g. 'ETH / USD')
*/
function symbol() external view returns (string memory _symbol);
/**
* @notice Fetch the latest oracle result and whether it is valid or not
* @dev This method should never revert
*/
function getResultWithValidity() external view returns (uint256 _result, bool _validity);
/**
* @notice Fetch the latest oracle result
* @dev Will revert if is the price feed is invalid
*/
function read() external view returns (uint256 _value);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
import {IBaseOracle} from '@interfaces/oracles/IBaseOracle.sol';
interface IDelayedOracle is IBaseOracle {
// --- Events ---
/**
* @notice Emitted when the oracle is updated
* @param _newMedian The new median value
* @param _lastUpdateTime The timestamp of the update
*/
event UpdateResult(uint256 _newMedian, uint256 _lastUpdateTime);
// --- Errors ---
/// @notice Throws if the provided price source address is null
error DelayedOracle_NullPriceSource();
/// @notice Throws if the provided delay is null
error DelayedOracle_NullDelay();
/// @notice Throws when trying to update the oracle before the delay has elapsed
error DelayedOracle_DelayHasNotElapsed();
/// @notice Throws when trying to read the current value and it is invalid
error DelayedOracle_NoCurrentValue();
// --- Structs ---
struct Feed {
// The value of the price feed
uint256 /* WAD */ value;
// Whether the value is valid or not
bool /* bool */ isValid;
}
/**
* @notice Address of the non-delayed price source
* @dev Assumes that the price source is a valid IBaseOracle
*/
function priceSource() external view returns (IBaseOracle _priceSource);
/**
* @notice The next valid price feed, taking effect at the next updateResult call
* @return _result The value in 18 decimals format of the next price feed
* @return _validity Whether the next price feed is valid or not
*/
function getNextResultWithValidity() external view returns (uint256 _result, bool _validity);
/// @notice The delay in seconds that should elapse between updates
function updateDelay() external view returns (uint256 _updateDelay);
/// @notice The timestamp of the last update
function lastUpdateTime() external view returns (uint256 _lastUpdateTime);
/**
* @notice Indicates if a delay has passed since the last update
* @return _ok Whether the oracle should be updated or not
*/
function shouldUpdate() external view returns (bool _ok);
/**
* @notice Updates the current price with the last next price, and reads the next price feed
* @dev Will revert if the delay since last update has not elapsed
* @return _success Whether the update was successful or not
*/
function updateResult() external returns (bool _success);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
import {IAuthorizable} from '@interfaces/utils/IAuthorizable.sol';
interface IDisableable is IAuthorizable {
// --- Events ---
/// @notice Emitted when the inheriting contract is disabled
event DisableContract();
// --- Errors ---
/// @notice Throws when trying to call a `whenDisabled` method when the contract is enabled
error ContractIsEnabled();
/// @notice Throws when trying to call a `whenEnabled` method when the contract is disabled
error ContractIsDisabled();
/// @notice Throws when trying to disable a contract that cannot be disabled
error NonDisableable();
// --- Data ---
/**
* @notice Check if the contract is enabled
* @return _contractEnabled True if the contract is enabled
*/
function contractEnabled() external view returns (bool _contractEnabled);
// --- Methods ---
/**
* @notice External method to trigger the contract disablement
* @dev Triggers an internal call to `_onContractDisable` virtual method
*/
function disableContract() external;
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
import {IAuthorizable} from '@interfaces/utils/IAuthorizable.sol';
interface IModifiable is IAuthorizable {
// --- Events ---
/// @dev Event topic 1 is always a parameter, topic 2 can be empty (global params)
event ModifyParameters(bytes32 indexed _param, bytes32 indexed _cType, bytes _data);
// --- Errors ---
error UnrecognizedParam();
error UnrecognizedCType();
// --- Administration ---
/**
* @notice Set a new value for a global specific parameter
* @param _param String identifier of the parameter to modify
* @param _data Encoded data to modify the parameter
*/
function modifyParameters(bytes32 _param, bytes memory _data) external;
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
import {IAuthorizable} from '@interfaces/utils/IAuthorizable.sol';
import {IModifiable} from '@interfaces/utils/IModifiable.sol';
interface IModifiablePerCollateral is IAuthorizable, IModifiable {
// --- Events ---
/**
* @notice Emitted when a new collateral type is registered
* @param _cType Bytes32 representation of the collateral type
*/
event InitializeCollateralType(bytes32 _cType);
// --- Errors ---
error CollateralTypeAlreadyInitialized();
// --- Views ---
/**
* @notice List of all the collateral types registered in the OracleRelayer
* @return __collateralList Array of all the collateral types registered
*/
function collateralList() external view returns (bytes32[] memory __collateralList);
// --- Methods ---
/**
* @notice Register a new collateral type in the SAFEEngine
* @param _cType Collateral type to register
* @param _collateralParams Collateral parameters
*/
function initializeCollateralType(bytes32 _cType, bytes memory _collateralParams) external;
/**
* @notice Set a new value for a collateral specific parameter
* @param _cType String identifier of the collateral to modify
* @param _param String identifier of the parameter to modify
* @param _data Encoded data to modify the parameter
*/
function modifyParameters(bytes32 _cType, bytes32 _param, bytes memory _data) external;
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
import {IBaseOracle} from '@interfaces/oracles/IBaseOracle.sol';
import {IDelayedOracle} from '@interfaces/oracles/IDelayedOracle.sol';
import {ISAFEEngine} from '@interfaces/ISAFEEngine.sol';
import {IAuthorizable} from '@interfaces/utils/IAuthorizable.sol';
import {IModifiable} from '@interfaces/utils/IModifiable.sol';
import {IModifiablePerCollateral} from '@interfaces/utils/IModifiablePerCollateral.sol';
import {IDisableable} from '@interfaces/utils/IDisableable.sol';
interface IOracleRelayer is IAuthorizable, IDisableable, IModifiable, IModifiablePerCollateral {
// --- Events ---
/**
* @notice Emitted when the redemption price is updated
* @param _redemptionPrice The new redemption price [ray]
*/
event UpdateRedemptionPrice(uint256 _redemptionPrice);
/**
* @notice Emitted when the redemption rate is updated
* @param _redemptionRate The new redemption rate [ray]
*/
event UpdateRedemptionRate(uint256 _redemptionRate);
/**
* @notice Emitted when a collateral type price is updated
* @param _cType Bytes32 representation of the collateral type
* @param _priceFeedValue The new collateral price [wad]
* @param _safetyPrice The new safety price [ray]
* @param _liquidationPrice The new liquidation price [ray]
*/
event UpdateCollateralPrice(
bytes32 indexed _cType, uint256 _priceFeedValue, uint256 _safetyPrice, uint256 _liquidationPrice
);
// --- Errors ---
/// @notice Throws if the redemption price is not updated when updating the rate
error OracleRelayer_RedemptionPriceNotUpdated();
/// @notice Throws when trying to initialize a collateral type that is already initialized
error OracleRelayer_CollateralTypeAlreadyInitialized();
// --- Structs ---
struct OracleRelayerParams {
// Upper bound for the per-second redemption rate
uint256 /* RAY */ redemptionRateUpperBound;
// Lower bound for the per-second redemption rate
uint256 /* RAY */ redemptionRateLowerBound;
}
struct OracleRelayerCollateralParams {
// Usually a DelayedOracle that enforces delays to fresh price feeds
IDelayedOracle /* */ oracle;
// CRatio used to compute the 'safePrice' - the price used when generating debt in SAFEEngine
uint256 /* RAY */ safetyCRatio;
// CRatio used to compute the 'liquidationPrice' - the price used when liquidating SAFEs
uint256 /* RAY */ liquidationCRatio;
}
// --- Registry ---
/**
* @notice The SAFEEngine is called to update the price of the collateral in the system
* @return _safeEngine Address of the contract that handles the state of the SAFEs
*/
function safeEngine() external view returns (ISAFEEngine _safeEngine);
/**
* @notice The oracle used to fetch the system coin market price
* @return _systemCoinOracle Address of the contract that provides the system coin price
*/
function systemCoinOracle() external view returns (IBaseOracle _systemCoinOracle);
// --- Params ---
/**
* @notice Getter for the contract parameters struct
* @dev Returns a OracleRelayerParams struct
*/
function params() external view returns (OracleRelayerParams memory _oracleRelayerParams);
/**
* @notice Getter for the unpacked contract parameters struct
* @param _redemptionRateUpperBound Upper bound for the per-second redemption rate [ray]
* @param _redemptionRateLowerBound Lower bound for the per-second redemption rate [ray]
*/
// solhint-disable-next-line private-vars-leading-underscore
function _params() external view returns (uint256 _redemptionRateUpperBound, uint256 _redemptionRateLowerBound);
/**
* @notice Getter for the collateral parameters struct
* @param _cType Bytes32 representation of the collateral type
* @dev Returns a OracleRelayerCollateralParams struct
*/
function cParams(bytes32 _cType) external view returns (OracleRelayerCollateralParams memory _oracleRelayerCParams);
/**
* @notice Getter for the unpacked collateral parameters struct
* @param _cType Bytes32 representation of the collateral type
* @param _oracle Usually a DelayedOracle that enforces delays to fresh price feeds
* @param _safetyCRatio CRatio used to compute the 'safePrice' - the price used when generating debt in SAFEEngine [ray]
* @param _liquidationCRatio CRatio used to compute the 'liquidationPrice' - the price used when liquidating SAFEs [ray]
*/
// solhint-disable-next-line private-vars-leading-underscore
function _cParams(bytes32 _cType)
external
view
returns (IDelayedOracle _oracle, uint256 _safetyCRatio, uint256 _liquidationCRatio);
// --- Data ---
/**
* @notice View method to fetch the current redemption price
* @return _redemptionPrice The current calculated redemption price [ray]
*/
function calcRedemptionPrice() external view returns (uint256 _redemptionPrice);
/**
* @notice The current system coin market price
* @return _marketPrice The current system coin market price [ray]
*/
function marketPrice() external view returns (uint256 _marketPrice);
/**
* @notice The redemption rate is the rate at which the redemption price changes over time
* @return _redemptionRate The current updated redemption rate [ray]
* @dev By changing the redemption rate, it changes the incentives of the system users
* @dev The redemption rate is a per-second rate [ray]
*/
function redemptionRate() external view returns (uint256 _redemptionRate);
/**
* @notice Last time when the redemption price was changed
* @return _redemptionPriceUpdateTime The last time when the redemption price was changed [unix timestamp]
* @dev Used to calculate the current redemption price
*/
function redemptionPriceUpdateTime() external view returns (uint256 _redemptionPriceUpdateTime);
// --- Methods ---
/**
* @notice Fetch the latest redemption price by first updating it
* @return _updatedPrice The newly updated redemption price [ray]
*/
function redemptionPrice() external returns (uint256 _updatedPrice);
/**
* @notice Update the collateral price inside the system (inside SAFEEngine)
* @dev Usually called by a keeper, incentivized by the system to keep the prices up to date
* @param _cType Bytes32 representation of the collateral type
*/
function updateCollateralPrice(bytes32 _cType) external;
/**
* @notice Update the system redemption rate, the rate at which the redemption price changes over time
* @dev Usually called by the PIDRateSetter
* @param _redemptionRate The newly calculated redemption rate [ray]
*/
function updateRedemptionRate(uint256 _redemptionRate) external;
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
import {IAuthorizable} from '@interfaces/utils/IAuthorizable.sol';
import {IModifiable} from '@interfaces/utils/IModifiable.sol';
interface IPIDController is IAuthorizable, IModifiable {
// --- Events ---
/**
* @notice Emitted when the state of the controller is updated
* @param _proportionalDeviation The new proportional term
* @param _integralDeviation The new integral term
* @param _deltaIntegralDeviation The delta between the new and the previous integral term
*/
event UpdateDeviation(int256 _proportionalDeviation, int256 _integralDeviation, int256 _deltaIntegralDeviation);
// --- Errors ---
/// @notice Throws if the caller of `updateRate` is not the seed proposer
error PIDController_OnlySeedProposer();
/// @notice Throws if the call to `updateRate` is too soon since last update
error PIDController_ComputeRateCooldown();
/// @notice Throws when trying to set the integral term with the integral gain set on
error PIDController_CannotSetPriceDeviationCumulative();
// --- Structs ---
struct PIDControllerParams {
// The minimum delay between two computeRate calls
uint256 /* seconds */ integralPeriodSize;
// The per second leak applied to priceDeviationCumulative before the latest deviation is added
uint256 /* RAY */ perSecondCumulativeLeak;
// The minimum percentage deviation from the redemption price that allows the contract to calculate a non null redemption rate
uint256 /* WAD */ noiseBarrier;
// The maximum value allowed for the redemption rate
uint256 /* RAY */ feedbackOutputUpperBound;
// The minimum value allowed for the redemption rate
int256 /* RAY */ feedbackOutputLowerBound;
}
struct DeviationObservation {
// The timestamp when this observation was stored
uint256 timestamp;
// The proportional term stored in this observation
int256 proportional;
// The integral term stored in this observation
int256 integral;
}
struct ControllerGains {
// This value is multiplied with the proportional term
int256 /* WAD */ kp;
// This value is multiplied with priceDeviationCumulative
int256 /* WAD */ ki;
}
// --- Registry ---
/**
* @notice Returns the address allowed to call computeRate method
*/
function seedProposer() external view returns (address _seedProposer);
// --- Data ---
/**
* @notice Getter for the contract parameters struct
* @return _pidParams The PID controller parameters struct
*/
function params() external view returns (PIDControllerParams memory _pidParams);
/**
* @notice Getter for the unpacked contract parameters struct
* @return _integralPeriodSize The minimum delay between two computeRate calls
* @return _perSecondCumulativeLeak The per second leak applied to priceDeviationCumulative before the latest deviation is added [ray]
* @return _noiseBarrier The minimum percentage deviation from the redemption price that allows the contract to calculate a non null redemption rate [wad]
* @return _feedbackOutputUpperBound The maximum value allowed for the redemption rate [ray]
* @return _feedbackOutputLowerBound The minimum value allowed for the redemption rate [ray]
*/
// solhint-disable-next-line private-vars-leading-underscore
function _params()
external
view
returns (
uint256 _integralPeriodSize,
uint256 _perSecondCumulativeLeak,
uint256 _noiseBarrier,
uint256 _feedbackOutputUpperBound,
int256 _feedbackOutputLowerBound
);
/**
* @notice Returns the last deviation observation, containing latest timestamp, proportional and integral terms
* @return __deviationObservation The last deviation observation struct
*/
function deviationObservation() external view returns (DeviationObservation memory __deviationObservation);
/**
* @notice Raw data about the last deviation observation
* @return _timestamp The timestamp when this observation was stored
* @return _proportional The proportional term stored in this observation
* @return _integral The integral term stored in this observation
*/
// solhint-disable-next-line private-vars-leading-underscore
function _deviationObservation() external view returns (uint256 _timestamp, int256 _proportional, int256 _integral);
/**
* @notice Returns the Kp and Ki values used in this calculator
* @dev The values are expressed in WAD, Kp stands for proportional and Ki for integral terms
*/
function controllerGains() external view returns (ControllerGains memory _cGains);
/**
* @notice Raw data about the Kp and Ki values used in this calculator
* @return _kp This value is multiplied with the proportional term
* @return _ki This value is multiplied with priceDeviationCumulative
*/
// solhint-disable-next-line private-vars-leading-underscore
function _controllerGains() external view returns (int256 _kp, int256 _ki);
/**
* @notice Return a redemption rate bounded by feedbackOutputLowerBound and feedbackOutputUpperBound as well as the
* timeline over which that rate will take effect
* @param _piOutput The raw redemption rate computed from the proportional and integral terms
* @return _redemptionRate The bounded redemption rate
*/
function getBoundedRedemptionRate(int256 _piOutput) external view returns (uint256 _redemptionRate);
/**
* @notice Compute a new redemption rate
* @param _marketPrice The system coin market price
* @param _redemptionPrice The system coin redemption price
* @return _redemptionRate The computed redemption rate
*/
function computeRate(uint256 _marketPrice, uint256 _redemptionPrice) external returns (uint256 _redemptionRate);
/**
* @notice Apply Kp to the proportional term and Ki to the integral term (by multiplication) and then sum P and I
* @param _proportionalTerm The proportional term
* @param _integralTerm The integral term
* @return _piOutput The sum of P and I
*/
function getGainAdjustedPIOutput(
int256 _proportionalTerm,
int256 _integralTerm
) external view returns (int256 _piOutput);
/**
* @notice Independently return and calculate P * Kp and I * Ki
* @param _proportionalTerm The proportional term
* @param _integralTerm The integral term
* @return _proportionalGain The proportional gain
* @return _integralGain The integral gain
*/
function getGainAdjustedTerms(
int256 _proportionalTerm,
int256 _integralTerm
) external view returns (int256 _proportionalGain, int256 _integralGain);
/**
* @notice Compute a new priceDeviationCumulative (integral term)
* @param _proportionalTerm The proportional term (redemptionPrice - marketPrice)
* @param _accumulatedLeak The total leak applied to priceDeviationCumulative before it is summed with the new time adjusted deviation
* @return _priceDeviationCumulative The new priceDeviationCumulative
* @return _timeAdjustedDeviation The new time adjusted deviation
*/
function getNextDeviationCumulative(
int256 _proportionalTerm,
uint256 _accumulatedLeak
) external returns (int256 _priceDeviationCumulative, int256 _timeAdjustedDeviation);
/**
* @notice Returns whether the P + I sum exceeds the noise barrier
* @param _piSum Represents a sum between P + I
* @param _redemptionPrice The system coin redemption price
* @return _breaksNb Whether the P + I sum exceeds the noise barrier
*/
function breaksNoiseBarrier(uint256 _piSum, uint256 _redemptionPrice) external view returns (bool _breaksNb);
/**
* @notice Compute and return the upcoming redemption rate
* @param _marketPrice The system coin market price
* @param _redemptionPrice The system coin redemption price
* @param _accumulatedLeak The total leak applied to priceDeviationCumulative before it is summed with the proportionalTerm
* @return _redemptionRate The upcoming redemption rate
* @return _proportionalTerm The upcoming proportional term
* @return _integralTerm The upcoming integral term
*/
function getNextRedemptionRate(
uint256 _marketPrice,
uint256 _redemptionPrice,
uint256 _accumulatedLeak
) external view returns (uint256 _redemptionRate, int256 _proportionalTerm, int256 _integralTerm);
/**
* @notice Returns the time elapsed since the last computeRate call
*/
function timeSinceLastUpdate() external view returns (uint256 _timeSinceLastValue);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
import {IPIDController} from '@interfaces/IPIDController.sol';
import {IBaseOracle} from '@interfaces/oracles/IBaseOracle.sol';
import {IOracleRelayer} from '@interfaces/IOracleRelayer.sol';
import {IAuthorizable} from '@interfaces/utils/IAuthorizable.sol';
import {IModifiable} from '@interfaces/utils/IModifiable.sol';
interface IPIDRateSetter is IAuthorizable, IModifiable {
// --- Events ---
/**
* @notice Emitted when the redemption rate is updated
* @param _marketPrice Computed price of the system coin
* @param _redemptionPrice Redemption price of the system coin
* @param _redemptionRate Resulting new redemption rate
*/
event UpdateRedemptionRate(uint256 _marketPrice, uint256 _redemptionPrice, uint256 _redemptionRate);
// --- Errors ---
/// @notice Throws if the market price feed returns an invalid value
error PIDRateSetter_InvalidPriceFeed();
/// @notice Throws if the call to `updateRate` is too soon since last update
error PIDRateSetter_RateSetterCooldown();
// --- Structs ---
struct PIDRateSetterParams {
// Enforced gap between calls
uint256 /* seconds */ updateRateDelay;
}
// --- Registry ---
/**
* @notice The oracle relayer where the redemption price and rate are stored
*/
function oracleRelayer() external view returns (IOracleRelayer _oracleRelayer);
/**
* @notice The PID calculator used to compute the redemption rate
*/
function pidCalculator() external view returns (IPIDController _pidCalculator);
// --- Params ---
/**
* @notice Getter for the contract parameters struct
* @return _pidRateSetterParams PIDRateSetter parameters struct
*/
function params() external view returns (PIDRateSetterParams memory _pidRateSetterParams);
/**
* @notice Getter for the unpacked contract parameters struct
* @return _updateRateDelay Enforced gap between calls
*/
// solhint-disable-next-line private-vars-leading-underscore
function _params() external view returns (uint256 _updateRateDelay);
// --- Data ---
/**
* @notice The timestamp of the last update
*/
function lastUpdateTime() external view returns (uint256 _lastUpdateTime);
// --- Methods ---
/**
* @notice Compute and set a new redemption rate
*/
function updateRate() external;
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
import {IAuthorizable} from '@interfaces/utils/IAuthorizable.sol';
import {IModifiable} from '@interfaces/utils/IModifiable.sol';
import {IModifiablePerCollateral} from '@interfaces/utils/IModifiablePerCollateral.sol';
import {IDisableable} from '@interfaces/utils/IDisableable.sol';
interface ISAFEEngine is IAuthorizable, IDisableable, IModifiable, IModifiablePerCollateral {
// --- Events ---
/**
* @notice Emitted when an address authorizes another address to modify its SAFE
* @param _sender Address that sent the authorization
* @param _account Address that is authorized to modify the SAFE
*/
event ApproveSAFEModification(address _sender, address _account);
/**
* @notice Emitted when an address denies another address to modify its SAFE
* @param _sender Address that sent the denial
* @param _account Address that is denied to modify the SAFE
*/
event DenySAFEModification(address _sender, address _account);
/**
* @notice Emitted when collateral is transferred between accounts
* @param _cType Bytes32 representation of the collateral type
* @param _src Address that sent the collateral
* @param _dst Address that received the collateral
* @param _wad Amount of collateral transferred
*/
event TransferCollateral(bytes32 indexed _cType, address indexed _src, address indexed _dst, uint256 _wad);
/**
* @notice Emitted when internal coins are transferred between accounts
* @param _src Address that sent the coins
* @param _dst Address that received the coins
* @param _rad Amount of coins transferred
*/
event TransferInternalCoins(address indexed _src, address indexed _dst, uint256 _rad);
/**
* @notice Emitted when the SAFE state is modified by the owner or authorized accounts
* @param _cType Bytes32 representation of the collateral type
* @param _safe Address of the SAFE
* @param _collateralSource Address that sent/receives the collateral
* @param _debtDestination Address that sent/receives the debt
* @param _deltaCollateral Amount of collateral added/extracted from the SAFE [wad]
* @param _deltaDebt Amount of debt to generate/repay [wad]
*/
event ModifySAFECollateralization(
bytes32 indexed _cType,
address indexed _safe,
address _collateralSource,
address _debtDestination,
int256 _deltaCollateral,
int256 _deltaDebt
);
/**
* @notice Emitted when collateral and/or debt is transferred between SAFEs
* @param _cType Bytes32 representation of the collateral type
* @param _src Address that sent the collateral
* @param _dst Address that received the collateral
* @param _deltaCollateral Amount of collateral to take/add into src and give/take from dst [wad]
* @param _deltaDebt Amount of debt to take/add into src and give/take from dst [wad]
*/
event TransferSAFECollateralAndDebt(
bytes32 indexed _cType, address indexed _src, address indexed _dst, int256 _deltaCollateral, int256 _deltaDebt
);
/**
* @notice Emitted when collateral and debt is confiscated from a SAFE
* @param _cType Bytes32 representation of the collateral type
* @param _safe Address of the SAFE
* @param _collateralSource Address that sent/receives the collateral
* @param _debtDestination Address that sent/receives the debt
* @param _deltaCollateral Amount of collateral added/extracted from the SAFE [wad]
* @param _deltaDebt Amount of debt to generate/repay [wad]
*/
event ConfiscateSAFECollateralAndDebt(
bytes32 indexed _cType,
address indexed _safe,
address _collateralSource,
address _debtDestination,
int256 _deltaCollateral,
int256 _deltaDebt
);
/**
* @notice Emitted when an account's debt is settled with coins
* @dev Accounts (not SAFEs) can only settle unbacked debt
* @param _account Address of the account
* @param _rad Amount of debt & coins to destroy
*/
event SettleDebt(address indexed _account, uint256 _rad);
/**
* @notice Emitted when an unbacked debt is created to an account
* @param _debtDestination Address that received the newly created debt
* @param _coinDestination Address that received the newly created coins
* @param _rad Amount of debt to create
*/
event CreateUnbackedDebt(address indexed _debtDestination, address indexed _coinDestination, uint256 _rad);
/**
* @notice Emit when the accumulated rate of a collateral type is updated
* @param _cType Bytes32 representation of the collateral type
* @param _surplusDst Address that received the newly created surplus
* @param _rateMultiplier Delta of the accumulated rate [ray]
*/
event UpdateAccumulatedRate(bytes32 indexed _cType, address _surplusDst, int256 _rateMultiplier);
/**
* @notice Emitted when the safety price and liquidation price of a collateral type is updated
* @param _cType Bytes32 representation of the collateral type
* @param _safetyPrice New price at which a SAFE is allowed to generate debt [ray]
* @param _liquidationPrice New price at which a SAFE gets liquidated [ray]
*/
event UpdateCollateralPrice(bytes32 indexed _cType, uint256 _safetyPrice, uint256 _liquidationPrice);
// --- Errors ---
/// @notice Throws when trying to modify parameters of an uninitialized collateral type
error SAFEEng_CollateralTypeNotInitialized(); // 0xcc8fbb29
/// @notice Throws when trying to modify a SAFE into an unsafe state
error SAFEEng_SAFENotSafe(); // 0x1f441794
/// @notice Throws when trying to modify a SAFE into a dusty safe (debt non-zero and below `debtFloor`)
error SAFEEng_DustySAFE(); // 0xbc8beb5f
/// @notice Throws when trying to generate debt that would put the system over the global debt ceiling
error SAFEEng_GlobalDebtCeilingHit(); // 0x4d0b26ae
/// @notice Throws when trying to generate debt that would put the system over the collateral debt ceiling
error SAFEEng_CollateralDebtCeilingHit(); // 0x787cf02c
/// @notice Throws when trying to generate debt that would put the SAFE over the SAFE debt ceiling
error SAFEEng_SAFEDebtCeilingHit(); // 0x8c77698d
/// @notice Throws when an account tries to modify a SAFE without the proper permissions
error SAFEEng_NotSAFEAllowed(); // 0x4df694a1
/// @notice Throws when an account tries to pull collateral from a SAFE without the proper permissions
error SAFEEng_NotCollateralSrcAllowed(); // 0x3820cfbf
/// @notice Throws when an account tries to push debt to a SAFE without the proper permissions
error SAFEEng_NotDebtDstAllowed(); // 0x62c26e9a
// --- Structs ---
struct SAFE {
// Total amount of collateral locked in a SAFE
uint256 /* WAD */ lockedCollateral;
// Total amount of debt generated by a SAFE
uint256 /* WAD */ generatedDebt;
}
struct SAFEEngineParams {
// Total amount of debt that a single safe can generate
uint256 /* WAD */ safeDebtCeiling;
// Maximum amount of debt that can be issued across all safes
uint256 /* RAD */ globalDebtCeiling;
}
struct SAFEEngineCollateralData {
// Total amount of debt issued by the collateral type
uint256 /* WAD */ debtAmount;
// Total amount of collateral locked in SAFEs using the collateral type
uint256 /* WAD */ lockedAmount;
// Accumulated rate of the collateral type
uint256 /* RAY */ accumulatedRate;
// Floor price at which a SAFE is allowed to generate debt
uint256 /* RAY */ safetyPrice;
// Price at which a SAFE gets liquidated
uint256 /* RAY */ liquidationPrice;
}
struct SAFEEngineCollateralParams {
// Maximum amount of debt that can be generated with the collateral type
uint256 /* RAD */ debtCeiling;
// Minimum amount of debt that must be generated by a SAFE using the collateral
uint256 /* RAD */ debtFloor;
}
// --- Data ---
/**
* @notice Getter for the contract parameters struct
* @dev Returns a SAFEEngineParams struct
*/
function params() external view returns (SAFEEngineParams memory _safeEngineParams);
/**
* @notice Getter for the unpacked contract parameters struct
* @return _safeDebtCeiling Total amount of debt that a single safe can generate [wad]
* @return _globalDebtCeiling Maximum amount of debt that can be issued [rad]
*/
// solhint-disable-next-line private-vars-leading-underscore
function _params() external view returns (uint256 _safeDebtCeiling, uint256 _globalDebtCeiling);
/**
* @notice Getter for the collateral parameters struct
* @param _cType Bytes32 representation of the collateral type
* @dev Returns a SAFEEngineCollateralParams struct
*/
function cParams(bytes32 _cType) external view returns (SAFEEngineCollateralParams memory _safeEngineCParams);
/**
* @notice Getter for the unpacked collateral parameters struct
* @param _cType Bytes32 representation of the collateral type
* @return _debtCeiling Maximum amount of debt that can be generated with this collateral type
* @return _debtFloor Minimum amount of debt that must be generated by a SAFE using this collateral
*/
// solhint-disable-next-line private-vars-leading-underscore
function _cParams(bytes32 _cType) external view returns (uint256 _debtCeiling, uint256 _debtFloor);
/**
* @notice Getter for the collateral data struct
* @param _cType Bytes32 representation of the collateral type
* @dev Returns a SAFEEngineCollateralData struct
*/
function cData(bytes32 _cType) external view returns (SAFEEngineCollateralData memory _safeEngineCData);
/**
* @notice Getter for the address of the safe manager
* @return _safeManager Address of safe manager
*/
function odSafeManager() external view returns (address _safeManager);
/**
* @notice Getter for the unpacked collateral data struct
* @param _cType Bytes32 representation of the collateral type
* @return _debtAmount Total amount of debt issued by a collateral type [wad]
* @return _lockedAmount Total amount of collateral locked in all SAFEs of the collateral type [wad]
* @return _accumulatedRate Accumulated rate of a collateral type [ray]
* @return _safetyPrice Floor price at which a SAFE is allowed to generate debt [ray]
* @return _liquidationPrice Price at which a SAFE gets liquidated [ray]
*/
// solhint-disable-next-line private-vars-leading-underscore
function _cData(bytes32 _cType)
external
view
returns (
uint256 _debtAmount,
uint256 _lockedAmount,
uint256 _accumulatedRate,
uint256 _safetyPrice,
uint256 _liquidationPrice
);
/**
* @notice Data about each SAFE
* @param _cType Bytes32 representation of the collateral type
* @param _safeAddress Address of the SAFE
* @dev Returns a SAFE struct
*/
function safes(bytes32 _cType, address _safeAddress) external view returns (SAFE memory _safeData);
/**
* @notice Unpacked data about each SAFE
* @param _cType Bytes32 representation of the collateral type
* @param _safeAddress Address of the SAFE
* @return _lockedCollateral Total amount of collateral locked in a SAFE [wad]
* @return _generatedDebt Total amount of debt generated by a SAFE [wad]
*/
// solhint-disable-next-line private-vars-leading-underscore
function _safes(
bytes32 _cType,
address _safeAddress
) external view returns (uint256 _lockedCollateral, uint256 _generatedDebt);
/**
* @notice Who can transfer collateral & debt in/out of a SAFE
* @param _caller Address to check for SAFE permissions for
* @param _account Account to check if caller has permissions for
* @return _safeRights Numerical representation of the SAFE rights (0/1)
*/
function safeRights(address _caller, address _account) external view returns (uint256 _safeRights);
// --- Balances ---
/**
* @notice Balance of each collateral type
* @param _cType Bytes32 representation of the collateral type to check balance for
* @param _account Account to check balance for
* @return _collateralBalance Collateral balance of the account [wad]
*/
function tokenCollateral(bytes32 _cType, address _account) external view returns (uint256 _collateralBalance);
/**
* @notice Internal balance of system coins held by an account
* @param _account Account to check balance for
* @return _balance Internal coin balance of the account [rad]
*/
function coinBalance(address _account) external view returns (uint256 _balance);
/**
* @notice Amount of debt held by an account
* @param _account Account to check balance for
* @return _debtBalance Debt balance of the account [rad]
*/
function debtBalance(address _account) external view returns (uint256 _debtBalance);
/**
* @notice Total amount of debt (coins) currently issued
* @dev Returns the global debt [rad]
*/
function globalDebt() external returns (uint256 _globalDebt);
/**
* @notice 'Bad' debt that's not covered by collateral
* @dev Returns the global unbacked debt [rad]
*/
function globalUnbackedDebt() external view returns (uint256 _globalUnbackedDebt);
// --- Init ---
// --- Fungibility ---
/**
* @notice Transfer collateral between accounts
* @param _cType Collateral type transferred
* @param _source Collateral source
* @param _destination Collateral destination
* @param _wad Amount of collateral transferred
*/
function transferCollateral(bytes32 _cType, address _source, address _destination, uint256 _wad) external;
/**
* @notice Transfer internal coins (does not affect external balances from Coin.sol)
* @param _source Coins source
* @param _destination Coins destination
* @param _rad Amount of coins transferred
*/
function transferInternalCoins(address _source, address _destination, uint256 _rad) external;
/**
* @notice Join/exit collateral into and and out of the system
* @param _cType Collateral type to join/exit
* @param _account Account that gets credited/debited
* @param _wad Amount of collateral
*/
function modifyCollateralBalance(bytes32 _cType, address _account, int256 _wad) external;
// --- SAFE Manipulation ---
/**
* @notice Add/remove collateral or put back/generate more debt in a SAFE
* @param _cType Type of collateral to withdraw/deposit in and from the SAFE
* @param _safe Target SAFE
* @param _collateralSource Account we take collateral from/put collateral into
* @param _debtDestination Account from which we credit/debit coins and debt
* @param _deltaCollateral Amount of collateral added/extracted from the SAFE [wad]
* @param _deltaDebt Amount of debt to generate/repay [wad]
*/
function modifySAFECollateralization(
bytes32 _cType,
address _safe,
address _collateralSource,
address _debtDestination,
int256 /* WAD */ _deltaCollateral,
int256 /* WAD */ _deltaDebt
) external;
// --- SAFE Fungibility ---
/**
* @notice Transfer collateral and/or debt between SAFEs
* @param _cType Collateral type transferred between SAFEs
* @param _src Source SAFE
* @param _dst Destination SAFE
* @param _deltaCollateral Amount of collateral to take/add into src and give/take from dst [wad]
* @param _deltaDebt Amount of debt to take/add into src and give/take from dst [wad]
*/
function transferSAFECollateralAndDebt(
bytes32 _cType,
address _src,
address _dst,
int256 /* WAD */ _deltaCollateral,
int256 /* WAD */ _deltaDebt
) external;
// --- SAFE Confiscation ---
/**
* @notice Normally used by the LiquidationEngine in order to confiscate collateral and
* debt from a SAFE and give them to someone else
* @param _cType Collateral type the SAFE has locked inside
* @param _safe Target SAFE
* @param _collateralSource Who we take/give collateral to
* @param _debtDestination Who we take/give debt to
* @param _deltaCollateral Amount of collateral taken/added into the SAFE [wad]
* @param _deltaDebt Amount of debt taken/added into the SAFE [wad]
*/
function confiscateSAFECollateralAndDebt(
bytes32 _cType,
address _safe,
address _collateralSource,
address _debtDestination,
int256 _deltaCollateral,
int256 _deltaDebt
) external;
// --- Settlement ---
/**
* @notice Nullify an amount of coins with an equal amount of debt
* @dev Coins & debt are like matter and antimatter, they nullify each other
* @param _rad Amount of debt & coins to destroy
*/
function settleDebt(uint256 _rad) external;
/**
* @notice Allows an authorized contract to create debt without collateral
* @param _debtDestination The account that will receive the newly created debt
* @param _coinDestination The account that will receive the newly created coins
* @param _rad Amount of debt to create
* @dev Usually called by DebtAuctionHouse in order to terminate auctions prematurely post settlement
*/
function createUnbackedDebt(address _debtDestination, address _coinDestination, uint256 _rad) external;
// --- Update ---
/**
* @notice Allows an authorized contract to accrue interest on a specific collateral type
* @param _cType Collateral type we accrue interest for
* @param _surplusDst Destination for the newly created surplus
* @param _rateMultiplier Multiplier applied to the debtAmount in order to calculate the surplus [ray]
* @dev The rateMultiplier is usually calculated by the TaxCollector contract
*/
function updateAccumulatedRate(bytes32 _cType, address _surplusDst, int256 _rateMultiplier) external;
/**
* @notice Allows an authorized contract to update the safety price and liquidation price of a collateral type
* @param _cType Collateral type we update the prices for
* @param _safetyPrice New safety price [ray]
* @param _liquidationPrice New liquidation price [ray]
*/
function updateCollateralPrice(bytes32 _cType, uint256 _safetyPrice, uint256 _liquidationPrice) external;
// --- Authorization ---
/**
* @notice Allow an address to modify your SAFE
* @param _account Account to give SAFE permissions to
*/
function approveSAFEModification(address _account) external;
/**
* @notice Deny an address the rights to modify your SAFE
* @param _account Account that is denied SAFE permissions
*/
function denySAFEModification(address _account) external;
/**
* @notice Checks whether msg.sender has the right to modify a SAFE
*/
function canModifySAFE(address _safe, address _account) external view returns (bool _allowed);
/**
* @notice called by ODSafeManager during deployment
*/
function initializeSafeManager() external;
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
import {IModifiable} from '@interfaces/utils/IModifiable.sol';
import {Authorizable} from '@contracts/utils/Authorizable.sol';
/**
* @title Modifiable
* @notice Allows inheriting contracts to modify parameters values
* @dev Requires inheriting contracts to override `_modifyParameters` virtual methods
*/
abstract contract Modifiable is Authorizable, IModifiable {
// --- Constants ---
/// @dev Used to emit a global parameter modification event
bytes32 internal constant _GLOBAL_PARAM = bytes32(0);
// --- External methods ---
/// @inheritdoc IModifiable
function modifyParameters(bytes32 _param, bytes memory _data) external isAuthorized validParams {
emit ModifyParameters(_param, _GLOBAL_PARAM, _data);
_modifyParameters(_param, _data);
}
// --- Internal virtual methods ---
/**
* @notice Internal function to be overriden with custom logic to modify parameters
* @dev This function is set to revert if not overriden
*/
// solhint-disable-next-line no-unused-vars
function _modifyParameters(bytes32 _param, bytes memory _data) internal virtual;
/// @notice Internal function to be overriden with custom logic to validate parameters
function _validateParameters() internal view virtual {}
// --- Modifiers ---
/// @notice Triggers a routine to validate parameters after a modification
modifier validParams() {
_;
_validateParameters();
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;
import {IPIDRateSetter} from '@interfaces/IPIDRateSetter.sol';
import {IOracleRelayer} from '@interfaces/IOracleRelayer.sol';
import {IPIDController} from '@interfaces/IPIDController.sol';
import {Authorizable} from '@contracts/utils/Authorizable.sol';
import {Modifiable} from '@contracts/utils/Modifiable.sol';
import {Encoding} from '@libraries/Encoding.sol';
import {Assertions} from '@libraries/Assertions.sol';
/**
* @title PIDRateSetter
* @notice This contract is used to trigger the update of the redemption rate using the PID controller
*/
contract PIDRateSetter is Authorizable, Modifiable, IPIDRateSetter {
using Encoding for bytes;
using Assertions for uint256;
using Assertions for address;
// --- Registry ---
/// @inheritdoc IPIDRateSetter
IOracleRelayer public oracleRelayer;
/// @inheritdoc IPIDRateSetter
IPIDController public pidCalculator;
// --- Params ---
/// @inheritdoc IPIDRateSetter
// solhint-disable-next-line private-vars-leading-underscore
PIDRateSetterParams public _params;
/// @inheritdoc IPIDRateSetter
function params() external view returns (PIDRateSetterParams memory _pidRateSetterParams) {
return _params;
}
// --- Data ---
/// @inheritdoc IPIDRateSetter
uint256 public lastUpdateTime;
// --- Init ---
/**
* @param _oracleRelayer Address of the oracle relayer
* @param _pidCalculator Address of the PID calculator
* @param _pidRateSetterParams Initial valid PID rate setter parameters struct
*/
constructor(
address _oracleRelayer,
address _pidCalculator,
PIDRateSetterParams memory _pidRateSetterParams
) Authorizable(msg.sender) validParams {
oracleRelayer = IOracleRelayer(_oracleRelayer);
pidCalculator = IPIDController(_pidCalculator);
_params = _pidRateSetterParams;
}
// --- Methods ---
/// @inheritdoc IPIDRateSetter
function updateRate() external {
// Check delay between calls
if (block.timestamp - lastUpdateTime < _params.updateRateDelay) revert PIDRateSetter_RateSetterCooldown();
// Get market price and check if it's non-zero
uint256 _marketPrice = oracleRelayer.marketPrice();
if (_marketPrice == 0) revert PIDRateSetter_InvalidPriceFeed();
// Get (and update if old) the latest redemption price
uint256 _redemptionPrice = oracleRelayer.redemptionPrice();
// Send latest redemption price to the PID calculator to calculate the redemption rate
uint256 _redemptionRate = pidCalculator.computeRate(_marketPrice, _redemptionPrice);
// Store the timestamp of the update
lastUpdateTime = block.timestamp;
// Update the rate using the setter relayer
oracleRelayer.updateRedemptionRate(_redemptionRate);
}
// --- Administration ---
/// @inheritdoc Modifiable
function _modifyParameters(bytes32 _param, bytes memory _data) internal override {
address _address = _data.toAddress();
uint256 _uint256 = _data.toUint256();
if (_param == 'oracleRelayer') oracleRelayer = IOracleRelayer(_address);
else if (_param == 'pidCalculator') pidCalculator = IPIDController(_address);
else if (_param == 'updateRateDelay') _params.updateRateDelay = _uint256;
else revert UnrecognizedParam();
}
/// @inheritdoc Modifiable
function _validateParameters() internal view override {
_params.updateRateDelay.assertGt(0);
address(oracleRelayer).assertHasCode();
address(pidCalculator).assertHasCode();
}
}
{
"compilationTarget": {
"src/contracts/PIDRateSetter.sol": "PIDRateSetter"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 10000
},
"remappings": [
":@console2/=forge-std/console2.sol;/",
":@contracts/=src/contracts/",
":@defi-wonderland/solidity-utils/=node_modules/@defi-wonderland/solidity-utils/solidity/",
":@interfaces/=src/interfaces/",
":@isolmate/=lib/isolmate/src/",
":@libraries/=src/libraries/",
":@openzeppelin-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/",
":@openzeppelin/=node_modules/@openzeppelin/contracts/",
":@script/=script/",
":@test/=test/",
":ds-test/=lib/ds-test/src/",
":forge-std/=lib/forge-std/src/",
":isolmate/=lib/isolmate/src/",
":prb-test/=lib/prb-test/src/"
]
}
[{"inputs":[{"internalType":"address","name":"_oracleRelayer","type":"address"},{"internalType":"address","name":"_pidCalculator","type":"address"},{"components":[{"internalType":"uint256","name":"updateRateDelay","type":"uint256"}],"internalType":"struct IPIDRateSetter.PIDRateSetterParams","name":"_pidRateSetterParams","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyAuthorized","type":"error"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"NoCode","type":"error"},{"inputs":[],"name":"NotAuthorized","type":"error"},{"inputs":[{"internalType":"uint256","name":"_x","type":"uint256"},{"internalType":"uint256","name":"_y","type":"uint256"}],"name":"NotGreaterThan","type":"error"},{"inputs":[],"name":"NullAddress","type":"error"},{"inputs":[],"name":"PIDRateSetter_InvalidPriceFeed","type":"error"},{"inputs":[],"name":"PIDRateSetter_RateSetterCooldown","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnrecognizedCType","type":"error"},{"inputs":[],"name":"UnrecognizedParam","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_account","type":"address"}],"name":"AddAuthorization","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"_param","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"_cType","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"_data","type":"bytes"}],"name":"ModifyParameters","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_account","type":"address"}],"name":"RemoveAuthorization","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_marketPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_redemptionPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_redemptionRate","type":"uint256"}],"name":"UpdateRedemptionRate","type":"event"},{"inputs":[],"name":"_params","outputs":[{"internalType":"uint256","name":"updateRateDelay","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"addAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"authorizedAccounts","outputs":[{"internalType":"bool","name":"_authorized","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authorizedAccounts","outputs":[{"internalType":"address[]","name":"_accounts","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_param","type":"bytes32"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"modifyParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"oracleRelayer","outputs":[{"internalType":"contract IOracleRelayer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"params","outputs":[{"components":[{"internalType":"uint256","name":"updateRateDelay","type":"uint256"}],"internalType":"struct IPIDRateSetter.PIDRateSetterParams","name":"_pidRateSetterParams","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pidCalculator","outputs":[{"internalType":"contract IPIDController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"removeAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateRate","outputs":[],"stateMutability":"nonpayable","type":"function"}]