Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 1 from a total of 1 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Refresh | 23827970 | 105 days ago | IN | 0 ETH | 0.00008133 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
MorphoSelfReferentialCollateral
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
// solhint-disable max-line-length
import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import { Asset, AppreciatingFiatCollateral, CollateralConfig, IRewardable } from "../AppreciatingFiatCollateral.sol";
import { MorphoTokenisedDeposit } from "./MorphoTokenisedDeposit.sol";
import { OracleLib } from "../OracleLib.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { shiftl_toFix, FIX_ONE, FLOOR, FixLib, CEIL } from "../../../libraries/Fixed.sol";
// solhint-enable max-line-length
/**
* @title MorphoSelfReferentialCollateral
* @notice Collateral plugin for a Morpho pool with self referential collateral, like WETH
* Expected: {tok} != {ref}, {ref} == {target}, {target} != {UoA}
*/
contract MorphoSelfReferentialCollateral is AppreciatingFiatCollateral {
using OracleLib for AggregatorV3Interface;
using FixLib for uint192;
MorphoTokenisedDeposit public immutable vault;
IERC20Metadata private immutable morpho; // MORPHO token
uint256 private immutable oneShare;
int8 private immutable refDecimals;
/// config.erc20 must be a MorphoTokenisedDeposit
/// @param config.chainlinkFeed Feed units: {UoA/ref}
/// @param revenueHiding {1} A value like 1e-6 that represents the maximum refPerTok to hide
constructor(CollateralConfig memory config, uint192 revenueHiding)
AppreciatingFiatCollateral(config, revenueHiding)
{
require(config.defaultThreshold == 0, "default threshold not supported");
require(address(config.erc20) != address(0), "missing erc20");
vault = MorphoTokenisedDeposit(address(config.erc20));
morpho = IERC20Metadata(address(vault.rewardToken()));
oneShare = 10**vault.decimals();
refDecimals = int8(uint8(IERC20Metadata(vault.asset()).decimals()));
}
/// Can revert, used by other contract functions in order to catch errors
/// Should NOT be manipulable by MEV
/// @return low {UoA/tok} The low price estimate
/// @return high {UoA/tok} The high price estimate
/// @return pegPrice {target/ref}
function tryPrice()
external
view
override
returns (
uint192 low,
uint192 high,
uint192 pegPrice
)
{
// {UoA/tok} = {UoA/ref} * {ref/tok}
uint192 p = chainlinkFeed.price(oracleTimeout).mul(underlyingRefPerTok());
uint192 err = p.mul(oracleError, CEIL);
low = p - err;
high = p + err;
// assert(low <= high); obviously true just by inspection
pegPrice = targetPerRef();
}
/// @return {ref/tok} Actual quantity of whole reference units per whole collateral tokens
function underlyingRefPerTok() public view override returns (uint192) {
return shiftl_toFix(vault.convertToAssets(oneShare), -refDecimals, FLOOR);
}
/// Claim rewards earned by holding a balance of the ERC20 token
/// @custom:delegate-call
function claimRewards() external virtual override(Asset, IRewardable) {
uint256 _bal = morpho.balanceOf(address(this));
IRewardable(address(erc20)).claimRewards();
emit RewardsClaimed(morpho, morpho.balanceOf(address(this)) - _bal);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControlUpgradeable {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; // EIP-2612 is Final as of 2022-11-01. This file is deprecated. import "./IERC20PermitUpgradeable.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20PermitUpgradeable {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(account),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* May emit a {RoleGranted} event.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)
pragma solidity ^0.8.0;
import "./IAccessControlEnumerable.sol";
import "./AccessControl.sol";
import "../utils/structs/EnumerableSet.sol";
/**
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
*/
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
using EnumerableSet for EnumerableSet.AddressSet;
mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
return _roleMembers[role].at(index);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
return _roleMembers[role].length();
}
/**
* @dev Overload {_grantRole} to track enumerable memberships
*/
function _grantRole(bytes32 role, address account) internal virtual override {
super._grantRole(role, account);
_roleMembers[role].add(account);
}
/**
* @dev Overload {_revokeRole} to track enumerable memberships
*/
function _revokeRole(bytes32 role, address account) internal virtual override {
super._revokeRole(role, account);
_roleMembers[role].remove(account);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
/**
* @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
*/
interface IAccessControlEnumerable is IAccessControl {
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) external view returns (address);
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*
* _Available since v4.1._
*/
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../token/ERC20/extensions/IERC20Metadata.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}// 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: MIT
pragma solidity ^0.8.19;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol";
interface IBaseTrustedFiller is IERC1271 {
error BaseTrustedFiller__SwapActive();
function initialize(
address _creator,
IERC20 _sellToken,
IERC20 _buyToken,
uint256 _sellAmount,
uint256 _minBuyAmount
) external;
function buyToken() external view returns (IERC20);
function sellToken() external view returns (IERC20);
function swapActive() external view returns (bool);
function closeFiller() external;
function rescueToken(IERC20 token) external;
function setPartiallyFillable(bool _partiallyFillable) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import { IBaseTrustedFiller } from "./IBaseTrustedFiller.sol";
interface ITrustedFillerRegistry {
error TrustedFillerRegistry__InvalidCaller();
error TrustedFillerRegistry__InvalidRoleRegistry();
error TrustedFillerRegistry__InvalidFiller();
event TrustedFillerCreated(address creator, IBaseTrustedFiller filler);
event TrustedFillerAdded(IBaseTrustedFiller filler);
event TrustedFillerDeprecated(IBaseTrustedFiller filler);
function addTrustedFiller(IBaseTrustedFiller _filler) external;
function deprecateTrustedFiller(IBaseTrustedFiller _filler) external;
function createTrustedFiller(
address senderSource,
address trustedFiller,
bytes32 deploymentSalt
) external returns (IBaseTrustedFiller trustedFillerInstance);
function isAllowed(address _filler) external view returns (bool);
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "../libraries/Fixed.sol";
import "./IMain.sol";
import "./IRewardable.sol";
// Not used directly in the IAsset interface, but used by many consumers to save stack space
struct Price {
uint192 low; // {UoA/tok}
uint192 high; // {UoA/tok}
}
/**
* @title IAsset
* @notice Supertype. Any token that interacts with our system must be wrapped in an asset,
* whether it is used as RToken backing or not. Any token that can report a price in the UoA
* is eligible to be an asset.
*/
interface IAsset is IRewardable {
/// Refresh saved price
/// The Reserve protocol calls this at least once per transaction, before relying on
/// the Asset's other functions.
/// @dev Called immediately after deployment, before use
function refresh() external;
/// Should not revert
/// low should be nonzero if the asset could be worth selling
/// @return low {UoA/tok} The lower end of the price estimate
/// @return high {UoA/tok} The upper end of the price estimate
function price() external view returns (uint192 low, uint192 high);
/// @return {tok} The balance of the ERC20 in whole tokens
function bal(address account) external view returns (uint192);
/// @return The ERC20 contract of the token with decimals() available
function erc20() external view returns (IERC20Metadata);
/// @return The number of decimals in the ERC20; just for gas optimization
function erc20Decimals() external view returns (uint8);
/// @return If the asset is an instance of ICollateral or not
function isCollateral() external view returns (bool);
/// @return {UoA} The max trade volume, in UoA
function maxTradeVolume() external view returns (uint192);
/// @return {s} The timestamp of the last refresh() that saved prices
function lastSave() external view returns (uint48);
}
// Used only in Testing. Strictly speaking an Asset does not need to adhere to this interface
interface TestIAsset is IAsset {
/// @return The address of the chainlink feed
function chainlinkFeed() external view returns (AggregatorV3Interface);
/// {1} The max % deviation allowed by the oracle
function oracleError() external view returns (uint192);
/// @return {s} Seconds that an oracle value is considered valid
function oracleTimeout() external view returns (uint48);
/// @return {s} The maximum of all oracle timeouts on the plugin
function maxOracleTimeout() external view returns (uint48);
/// @return {s} Seconds that the price() should decay over, after stale price
function priceTimeout() external view returns (uint48);
/// @return {UoA/tok} The last saved low price
function savedLowPrice() external view returns (uint192);
/// @return {UoA/tok} The last saved high price
function savedHighPrice() external view returns (uint192);
}
/// CollateralStatus must obey a linear ordering. That is:
/// - being DISABLED is worse than being IFFY, or SOUND
/// - being IFFY is worse than being SOUND.
enum CollateralStatus {
SOUND,
IFFY, // When a peg is not holding or a chainlink feed is stale
DISABLED // When the collateral has completely defaulted
}
/// Upgrade-safe maximum operator for CollateralStatus
library CollateralStatusComparator {
/// @return Whether a is worse than b
function worseThan(CollateralStatus a, CollateralStatus b) internal pure returns (bool) {
return uint256(a) > uint256(b);
}
}
/**
* @title ICollateral
* @notice A subtype of Asset that consists of the tokens eligible to back the RToken.
*/
interface ICollateral is IAsset {
/// Emitted whenever the collateral status is changed
/// @param newStatus The old CollateralStatus
/// @param newStatus The updated CollateralStatus
event CollateralStatusChanged(
CollateralStatus indexed oldStatus,
CollateralStatus indexed newStatus
);
/// @dev refresh()
/// Refresh exchange rates and update default status.
/// VERY IMPORTANT: In any valid implementation, status() MUST become DISABLED in refresh() if
/// refPerTok() has ever decreased since last call.
/// @return The canonical name of this collateral's target unit.
function targetName() external view returns (bytes32);
/// @return The status of this collateral asset. (Is it defaulting? Might it soon?)
function status() external view returns (CollateralStatus);
// ==== Exchange Rates ====
/// @return {ref/tok} Quantity of whole reference units per whole collateral tokens
function refPerTok() external view returns (uint192);
/// @return {target/ref} Quantity of whole target units per whole reference unit in the peg
function targetPerRef() external view returns (uint192);
/// @return {target/ref} The peg price of the token during the last update
function savedPegPrice() external view returns (uint192);
}
// Used only in Testing. Strictly speaking a Collateral does not need to adhere to this interface
interface TestICollateral is TestIAsset, ICollateral {
/// @return The epoch timestamp when the collateral will default from IFFY to DISABLED
function whenDefault() external view returns (uint256);
/// @return The amount of time a collateral must be in IFFY status until being DISABLED
function delayUntilDefault() external view returns (uint48);
/// @return The underlying refPerTok, likely not included in all collaterals however.
function underlyingRefPerTok() external view returns (uint192);
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IAsset.sol";
import "./IComponent.sol";
/// A serialization of the AssetRegistry to be passed around in the P1 impl for gas optimization
struct Registry {
IERC20[] erc20s;
IAsset[] assets;
}
/**
* @title IAssetRegistry
* @notice The AssetRegistry is in charge of maintaining the ERC20 tokens eligible
* to be handled by the rest of the system. If an asset is in the registry, this means:
* 1. Its ERC20 contract has been vetted
* 2. The asset is the only asset for that ERC20
* 3. The asset can be priced in the UoA, usually via an oracle
*/
interface IAssetRegistry is IComponent {
/// Emitted when an asset is added to the registry
/// @param erc20 The ERC20 contract for the asset
/// @param asset The asset contract added to the registry
event AssetRegistered(IERC20 indexed erc20, IAsset indexed asset);
/// Emitted when an asset is removed from the registry
/// @param erc20 The ERC20 contract for the asset
/// @param asset The asset contract removed from the registry
event AssetUnregistered(IERC20 indexed erc20, IAsset indexed asset);
// Initialization
function init(IMain main_, IAsset[] memory assets_) external;
/// Fully refresh all asset state
/// @custom:refresher
function refresh() external;
/// Register a new JIT-deployed RTokenAsset instance
/// @param maxTradeVolume {UoA} The maximum trade volume for the RTokenAsset
/// @return swapped If the asset was swapped for a previously-registered asset
/// @custom:governance
function registerNewRTokenAsset(uint192 maxTradeVolume) external returns (bool swapped);
/// Register `asset`
/// If either the erc20 address or the asset was already registered, fail
/// @return true if the erc20 address was not already registered.
/// @custom:governance
function register(IAsset asset) external returns (bool);
/// Register `asset` if and only if its erc20 address is already registered.
/// If the erc20 address was not registered, revert.
/// @return swapped If the asset was swapped for a previously-registered asset
/// @custom:governance
function swapRegistered(IAsset asset) external returns (bool swapped);
/// Unregister an asset, requiring that it is already registered
/// @custom:governance
function unregister(IAsset asset) external;
/// @return {s} The timestamp of the last refresh
function lastRefresh() external view returns (uint48);
/// @return The corresponding asset for ERC20, or reverts if not registered
function toAsset(IERC20 erc20) external view returns (IAsset);
/// @return The corresponding collateral, or reverts if unregistered or not collateral
function toColl(IERC20 erc20) external view returns (ICollateral);
/// @return If the ERC20 is registered
function isRegistered(IERC20 erc20) external view returns (bool);
/// @return A list of all registered ERC20s
function erc20s() external view returns (IERC20[] memory);
/// @return reg The list of registered ERC20s and Assets, in the same order
function getRegistry() external view returns (Registry memory reg);
/// Validate that the current assets in the registry are compatible with the current version
function validateCurrentAssets() external view;
/// @return The number of registered ERC20s
function size() external view returns (uint256);
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IAssetRegistry.sol";
import "./IBasketHandler.sol";
import "./IComponent.sol";
import "./IRToken.sol";
import "./IStRSR.sol";
import "./ITrading.sol";
/// Memory struct for RecollateralizationLibP1 + RTokenAsset
/// Struct purposes:
/// 1. Configure trading
/// 2. Stay under stack limit with fewer vars
/// 3. Cache information such as component addresses and basket quantities, to save on gas
struct TradingContext {
BasketRange basketsHeld; // {BU}
// basketsHeld.top is the number of partial baskets units held
// basketsHeld.bottom is the number of full basket units held
// Components
IBasketHandler bh;
IAssetRegistry ar;
IStRSR stRSR;
IERC20 rsr;
IRToken rToken;
// Gov Vars
uint192 minTradeVolume; // {UoA}
uint192 maxTradeSlippage; // {1}
// Cached values
uint192[] quantities; // {tok/BU} basket quantities
uint192[] bals; // {tok} balances in BackingManager + out on trades
}
/**
* @title IBackingManager
* @notice The BackingManager handles changes in the ERC20 balances that back an RToken.
* - It computes which trades to perform, if any, and initiates these trades with the Broker.
* - rebalance()
* - If already collateralized, excess assets are transferred to RevenueTraders.
* - forwardRevenue(IERC20[] calldata erc20s)
*/
interface IBackingManager is IComponent, ITrading {
/// Emitted when the trading delay is changed
/// @param oldVal The old trading delay
/// @param newVal The new trading delay
event TradingDelaySet(uint48 oldVal, uint48 newVal);
/// Emitted when the backing buffer is changed
/// @param oldVal The old backing buffer
/// @param newVal The new backing buffer
event BackingBufferSet(uint192 oldVal, uint192 newVal);
// Initialization
function init(
IMain main_,
uint48 tradingDelay_,
uint192 backingBuffer_,
uint192 maxTradeSlippage_,
uint192 minTradeVolume_
) external;
// Give RToken max allowance over a registered token
/// @custom:refresher
/// @custom:interaction
function grantRTokenAllowance(IERC20) external;
/// Apply the overall backing policy using the specified TradeKind, taking a haircut if unable
/// @param kind TradeKind.DUTCH_AUCTION or TradeKind.BATCH_AUCTION
/// @custom:interaction RCEI
function rebalance(TradeKind kind) external;
/// Forward revenue to RevenueTraders; reverts if not fully collateralized
/// @param erc20s The tokens to forward
/// @custom:interaction RCEI
function forwardRevenue(IERC20[] calldata erc20s) external;
/// Structs for trading
/// @param basketsHeld The number of baskets held by the BackingManager
/// @return ctx The TradingContext
/// @return reg Contents of AssetRegistry.getRegistry()
function tradingContext(BasketRange memory basketsHeld)
external
view
returns (TradingContext memory ctx, Registry memory reg);
}
interface TestIBackingManager is IBackingManager, TestITrading {
function tradingDelay() external view returns (uint48);
function backingBuffer() external view returns (uint192);
function setTradingDelay(uint48 val) external;
function setBackingBuffer(uint192 val) external;
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../libraries/Fixed.sol";
import "./IAsset.sol";
import "./IComponent.sol";
struct BasketRange {
uint192 bottom; // {BU}
uint192 top; // {BU}
}
/**
* @title IBasketHandler
* @notice The BasketHandler aims to maintain a reference basket of constant target unit amounts.
* When a collateral token defaults, a new reference basket of equal target units is set.
* When _all_ collateral tokens default for a target unit, only then is the basket allowed to fall
* in terms of target unit amounts. The basket is considered defaulted in this case.
*/
interface IBasketHandler is IComponent {
/// Emitted when the prime basket is set
/// @param erc20s The collateral tokens for the prime basket
/// @param targetAmts {target/BU} A list of quantities of target unit per basket unit
/// @param targetNames Each collateral token's targetName
event PrimeBasketSet(IERC20[] erc20s, uint192[] targetAmts, bytes32[] targetNames);
/// Emitted when the reference basket is set
/// @param nonce {basketNonce} The basket nonce
/// @param erc20s The list of collateral tokens in the reference basket
/// @param refAmts {ref/BU} The reference amounts of the basket collateral tokens
/// @param disabled True when the list of erc20s + refAmts may not be correct
event BasketSet(uint256 indexed nonce, IERC20[] erc20s, uint192[] refAmts, bool disabled);
/// Emitted when a backup config is set for a target unit
/// @param targetName The name of the target unit as a bytes32
/// @param max The max number to use from `erc20s`
/// @param erc20s The set of backup collateral tokens
event BackupConfigSet(bytes32 indexed targetName, uint256 max, IERC20[] erc20s);
/// Emitted when the warmup period is changed
/// @param oldVal The old warmup period
/// @param newVal The new warmup period
event WarmupPeriodSet(uint48 oldVal, uint48 newVal);
/// Emitted when the issuance premium logic is changed
/// @param oldVal The old value of enableIssuancePremium
/// @param newVal The new value of enableIssuancePremium
event EnableIssuancePremiumSet(bool oldVal, bool newVal);
/// Emitted when the status of a basket has changed
/// @param oldStatus The previous basket status
/// @param newStatus The new basket status
event BasketStatusChanged(CollateralStatus oldStatus, CollateralStatus newStatus);
/// Emitted when the last basket nonce available for redemption is changed
/// @param oldVal The old value of lastCollateralized
/// @param newVal The new value of lastCollateralized
event LastCollateralizedChanged(uint48 oldVal, uint48 newVal);
// Initialization
function init(
IMain main_,
uint48 warmupPeriod_,
bool reweightable_,
bool enableIssuancePremium_
) external;
/// Set the prime basket, checking target amounts are constant
/// @param erc20s The collateral tokens for the new prime basket
/// @param targetAmts The target amounts (in) {target/BU} for the new prime basket
/// required range: 1e9 values; absolute range irrelevant.
/// @custom:governance
function setPrimeBasket(IERC20[] calldata erc20s, uint192[] calldata targetAmts) external;
/// Set the prime basket, skipping any constant target amount checks if RToken is reweightable
/// Warning: Reweightable RTokens SHOULD use a spell to execute this function to avoid
/// accidentally changing the UoA value of the RToken.
/// @param erc20s The collateral tokens for the new prime basket
/// @param targetAmts The target amounts (in) {target/BU} for the new prime basket
/// required range: 1e9 values; absolute range irrelevant.
/// @custom:governance
function forceSetPrimeBasket(IERC20[] calldata erc20s, uint192[] calldata targetAmts) external;
/// Set the backup configuration for a given target
/// @param targetName The name of the target as a bytes32
/// @param max The maximum number of collateral tokens to use from this target
/// Required range: 1-255
/// @param erc20s A list of ordered backup collateral tokens
/// @custom:governance
function setBackupConfig(
bytes32 targetName,
uint256 max,
IERC20[] calldata erc20s
) external;
/// Default the basket in order to schedule a basket refresh
/// @custom:protected
function disableBasket() external;
/// Governance-controlled setter to cause a basket switch explicitly
/// @custom:governance
/// @custom:interaction
function refreshBasket() external;
/// Track basket status and collateralization changes
/// @custom:refresher
function trackStatus() external;
/// @return If the BackingManager has sufficient collateral to redeem the entire RToken supply
function fullyCollateralized() external view returns (bool);
/// @return status The worst CollateralStatus of all collateral in the basket
function status() external view returns (CollateralStatus status);
/// @return If the basket is ready to issue and trade
function isReady() external view returns (bool);
/// Returns basket quantity rounded up, wihout any issuance premium
/// @param erc20 The ERC20 token contract for the asset
/// @return {tok/BU} The redemption quantity of token in the reference basket, rounded up
/// Returns 0 if erc20 is not registered or not in the basket
/// Returns FIX_MAX (in lieu of +infinity) if Collateral.refPerTok() is 0.
/// Otherwise, returns (token's basket.refAmts / token's Collateral.refPerTok())
function quantity(IERC20 erc20) external view returns (uint192);
/// Returns basket quantity rounded up, wihout any issuance premium
/// Like quantity(), but unsafe because it DOES NOT CONFIRM THAT THE ASSET IS CORRECT
/// @param erc20 The ERC20 token contract for the asset
/// @param asset The registered asset plugin contract for the erc20
/// @return {tok/BU} The redemption quantity of token in the reference basket, rounded up
/// Returns 0 if erc20 is not registered or not in the basket
/// Returns FIX_MAX (in lieu of +infinity) if Collateral.refPerTok() is 0.
/// Otherwise, returns (token's basket.refAmts / token's Collateral.refPerTok())
function quantityUnsafe(IERC20 erc20, IAsset asset) external view returns (uint192);
/// @param amount {BU}
/// @param applyIssuancePremium Whether to apply the issuance premium
/// @return erc20s The addresses of the ERC20 tokens in the reference basket
/// @return quantities {qTok} The quantity of each ERC20 token to issue `amount` baskets
function quote(
uint192 amount,
bool applyIssuancePremium,
RoundingMode rounding
) external view returns (address[] memory erc20s, uint256[] memory quantities);
/// Return the redemption value of `amount` BUs for a linear combination of historical baskets
/// @param basketNonces An array of basket nonces to do redemption from
/// @param portions {1} An array of Fix quantities that must add up to FIX_ONE
/// @param amount {BU}
/// @return erc20s The backing collateral erc20s
/// @return quantities {qTok} ERC20 token quantities equal to `amount` BUs
function quoteCustomRedemption(
uint48[] memory basketNonces,
uint192[] memory portions,
uint192 amount
) external view returns (address[] memory erc20s, uint256[] memory quantities);
/// @return top {BU} The number of partial basket units: e.g max(coll.map((c) => c.balAsBUs())
/// bottom {BU} The number of whole basket units held by the account
function basketsHeldBy(address account) external view returns (BasketRange memory);
/// Should not revert
/// low should be nonzero when BUs are worth selling
/// @param applyIssuancePremium Whether to apply the issuance premium to the high price
/// @return low {UoA/BU} The lower end of the price estimate
/// @return high {UoA/BU} The upper end of the price estimate
function price(bool applyIssuancePremium) external view returns (uint192 low, uint192 high);
/// @return timestamp The timestamp at which the basket was last set
function timestamp() external view returns (uint48);
/// @return The current basket nonce, regardless of status
function nonce() external view returns (uint48);
}
interface TestIBasketHandler is IBasketHandler {
function getPrimeBasket()
external
view
returns (
IERC20[] memory erc20s,
bytes32[] memory targetNames,
uint192[] memory targetAmts
);
function getBackupConfig(bytes32 targetName)
external
view
returns (IERC20[] memory erc20s, uint256 max);
function lastCollateralized() external view returns (uint48);
function warmupPeriod() external view returns (uint48);
function setWarmupPeriod(uint48 val) external;
function enableIssuancePremium() external view returns (bool);
function setIssuancePremiumEnabled(bool val) external;
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@reserve-protocol/trusted-fillers/contracts/interfaces/ITrustedFillerRegistry.sol";
import "./IAsset.sol";
import "./IComponent.sol";
import "./IGnosis.sol";
import "./ITrade.sol";
enum TradeKind {
DUTCH_AUCTION,
BATCH_AUCTION
}
/// Cache of all prices for a pair to prevent re-lookup
struct TradePrices {
uint192 sellLow; // {UoA/sellTok} can be 0
uint192 sellHigh; // {UoA/sellTok} should not be 0
uint192 buyLow; // {UoA/buyTok} should not be 0
uint192 buyHigh; // {UoA/buyTok} should not be 0 or FIX_MAX
}
/// The data format that describes a request for trade with the Broker
struct TradeRequest {
IAsset sell;
IAsset buy;
uint256 sellAmount; // {qSellTok}
uint256 minBuyAmount; // {qBuyTok}
}
/**
* @title IBroker
* @notice The Broker deploys oneshot Trade contracts for Traders and monitors
* the continued proper functioning of trading platforms.
*/
interface IBroker is IComponent {
event BatchTradeImplementationSet(ITrade oldVal, ITrade newVal);
event DutchTradeImplementationSet(ITrade oldVal, ITrade newVal);
event BatchAuctionLengthSet(uint48 oldVal, uint48 newVal);
event DutchAuctionLengthSet(uint48 oldVal, uint48 newVal);
event BatchTradeDisabledSet(bool prevVal, bool newVal);
event DutchTradeDisabledSet(IERC20Metadata indexed erc20, bool prevVal, bool newVal);
event TrustedFillerRegistrySet(address trustedFillerRegistry, bool isEnabled);
// Initialization
function init(
IMain main_,
ITrade batchTradeImplemention_,
uint48 batchAuctionLength_,
ITrade dutchTradeImplemention_,
uint48 dutchAuctionLength_
) external;
/// Request a trade from the broker
/// @dev Requires setting an allowance in advance
/// @custom:interaction
function openTrade(
TradeKind kind,
TradeRequest memory req,
TradePrices memory prices
) external returns (ITrade);
/// Only callable by one of the trading contracts the broker deploys
function reportViolation() external;
function batchTradeDisabled() external view returns (bool);
function dutchTradeDisabled(IERC20Metadata erc20) external view returns (bool);
}
interface IExtendedBroker is IBroker {
function setBatchTradeImplementation(ITrade newTradeImplementation) external;
function setDutchTradeImplementation(ITrade newTradeImplementation) external;
function setTrustedFillerRegistry(address newRegistry, bool enabled) external;
function trustedFillerRegistry() external view returns (ITrustedFillerRegistry);
function trustedFillerEnabled() external view returns (bool);
}
interface TestIBroker is IBroker {
function gnosis() external view returns (IGnosis);
function batchTradeImplementation() external view returns (ITrade);
function dutchTradeImplementation() external view returns (ITrade);
function batchAuctionLength() external view returns (uint48);
function dutchAuctionLength() external view returns (uint48);
function setBatchTradeImplementation(ITrade newTradeImplementation) external;
function setBatchAuctionLength(uint48 newAuctionLength) external;
function setDutchTradeImplementation(ITrade newTradeImplementation) external;
function setDutchAuctionLength(uint48 newAuctionLength) external;
function enableBatchTrade() external;
function enableDutchTrade(IERC20Metadata erc20) external;
// only present on pre-3.0.0 Brokers; used by EasyAuction regression test
function disabled() external view returns (bool);
function setTrustedFillerRegistry(address newRegistry, bool enabled) external;
function trustedFillerRegistry() external view returns (ITrustedFillerRegistry);
function trustedFillerEnabled() external view returns (bool);
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "./IMain.sol";
import "./IVersioned.sol";
/**
* @title IComponent
* @notice A Component is the central building block of all our system contracts. Components
* contain important state that must be migrated during upgrades, and they delegate
* their ownership to Main's owner.
*/
interface IComponent is IVersioned {
function main() external view returns (IMain);
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "../libraries/Throttle.sol";
import "./IAsset.sol";
import "./IDistributor.sol";
import "./IGnosis.sol";
import "./IMain.sol";
import "./IRToken.sol";
import "./IStRSR.sol";
import "./ITrade.sol";
import "./IVersioned.sol";
import "../registry/VersionRegistry.sol";
import "../registry/AssetPluginRegistry.sol";
import "../registry/DAOFeeRegistry.sol";
import "@reserve-protocol/trusted-fillers/contracts/interfaces/ITrustedFillerRegistry.sol";
/**
* @title DeploymentParams
* @notice The set of protocol params needed to configure a new system deployment.
* meaning that after deployment there is freedom to allow parametrizations to deviate.
*/
struct DeploymentParams {
// === Revenue sharing ===
RevenueShare dist; // revenue sharing splits between RToken and RSR
//
// === Trade sizing ===
uint192 minTradeVolume; // {UoA}
uint192 rTokenMaxTradeVolume; // {UoA}
//
// === Freezing ===
uint48 shortFreeze; // {s} how long an initial freeze lasts
uint48 longFreeze; // {s} how long each freeze extension lasts
//
// === Rewards (Furnace + StRSR) ===
uint192 rewardRatio; // the fraction of available revenues that are paid out each block period
//
// === StRSR ===
uint48 unstakingDelay; // {s} the "thawing time" of staked RSR before withdrawal
uint192 withdrawalLeak; // {1} fraction of RSR that can be withdrawn without refresh
//
// === BasketHandler ===
uint48 warmupPeriod; // {s} how long to wait until issuance/trading after regaining SOUND
bool reweightable; // whether the target amounts in the prime basket can change
bool enableIssuancePremium; // whether to enable the issuance premium
//
// === BackingManager ===
uint48 tradingDelay; // {s} how long to wait until starting auctions after switching basket
uint48 batchAuctionLength; // {s} the length of a Gnosis EasyAuction
uint48 dutchAuctionLength; // {s} the length of a falling-price dutch auction
uint192 backingBuffer; // {1} how much extra backing collateral to keep
uint192 maxTradeSlippage; // {1} max slippage acceptable in a trade
//
// === RToken Supply Throttles ===
ThrottleLib.Params issuanceThrottle; // see ThrottleLib
ThrottleLib.Params redemptionThrottle;
}
/**
* @title Implementations
* @notice The set of implementation contracts to be used for proxies in the Deployer
*/
struct Implementations {
IMain main;
Components components;
TradePlugins trading;
}
struct TradePlugins {
ITrade gnosisTrade;
ITrade dutchTrade;
}
/**
* @title IDeployer
* @notice Factory contract for an RToken system instance
*/
interface IDeployer is IVersioned {
/// Emitted when a new RToken and accompanying system is deployed
/// @param main The address of `Main`
/// @param rToken The address of the RToken ERC20
/// @param stRSR The address of the StRSR ERC20 staking pool/token
/// @param owner The owner of the newly deployed system
/// @param version The semantic versioning version string (see: https://semver.org)
event RTokenCreated(
IMain indexed main,
IRToken indexed rToken,
IStRSR stRSR,
address indexed owner,
string version
);
/// Emitted when a new RTokenAsset is deployed during `deployRTokenAsset`
/// @param rToken The address of the RToken ERC20
/// @param rTokenAsset The address of the RTokenAsset
event RTokenAssetCreated(IRToken indexed rToken, IAsset rTokenAsset);
struct Registries {
VersionRegistry versionRegistry;
AssetPluginRegistry assetPluginRegistry;
DAOFeeRegistry daoFeeRegistry;
ITrustedFillerRegistry trustedFillerRegistry;
}
/// Deploys an instance of the entire system
/// @param name The name of the RToken to deploy
/// @param symbol The symbol of the RToken to deploy
/// @param mandate An IPFS link or direct string; describes what the RToken _should be_
/// @param owner The address that should own the entire system, hopefully a governance contract
/// @param params Deployment params
/// @param registries Registries list; can be 0 to unset
/// @return The address of the newly deployed Main instance.
function deploy(
string calldata name,
string calldata symbol,
string calldata mandate,
address owner,
DeploymentParams calldata params,
Registries calldata registries
) external returns (address);
function implementations() external view returns (Implementations memory);
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IComponent.sol";
uint256 constant MAX_DISTRIBUTION = 1e4; // 10,000
uint8 constant MAX_DESTINATIONS = 100; // maximum number of RevenueShare destinations
// === 4.0.0 ===
// Invariant: sum across destinations must be at *least* MAX_DISTRIBUTION
struct RevenueShare {
uint16 rTokenDist; // {revShare} A value between [0, 10,000]
uint16 rsrDist; // {revShare} A value between [0, 10,000]
}
/// Assumes no more than 100 independent distributions.
struct RevenueTotals {
uint24 rTokenTotal; // {revShare}
uint24 rsrTotal; // {revShare}
}
/**
* @title IDistributor
* @notice The Distributor Component maintains a revenue distribution table that dictates
* how to divide revenue across the Furnace, StRSR, and any other destinations.
*/
interface IDistributor is IComponent {
/// Emitted when a distribution is set
/// @param dest The address set to receive the distribution
/// @param rTokenDist The distribution of RToken that should go to `dest`
/// @param rsrDist The distribution of RSR that should go to `dest`
event DistributionSet(address indexed dest, uint16 rTokenDist, uint16 rsrDist);
/// Emitted when revenue is distributed
/// @param erc20 The token being distributed, either RSR or the RToken itself
/// @param source The address providing the revenue
/// @param amount The amount of the revenue
event RevenueDistributed(IERC20 indexed erc20, address indexed source, uint256 amount);
// Initialization
function init(IMain main_, RevenueShare calldata dist) external;
/// @custom:governance
function setDistribution(address dest, RevenueShare calldata share) external;
/// @custom:governance
function setDistributions(address[] calldata dests, RevenueShare[] calldata shares) external;
/// Distribute the `erc20` token across all revenue destinations
/// Only callable by RevenueTraders
/// @custom:protected
function distribute(IERC20 erc20, uint256 amount) external;
/// @return revTotals The total of all destinations
function totals() external view returns (RevenueTotals memory revTotals);
}
interface TestIDistributor is IDistributor {
// solhint-disable-next-line func-name-mixedcase
function FURNACE() external view returns (address);
// solhint-disable-next-line func-name-mixedcase
function ST_RSR() external view returns (address);
/// @return rTokenDist The RToken distribution for the address
/// @return rsrDist The RSR distribution for the address
function distribution(address) external view returns (uint16 rTokenDist, uint16 rsrDist);
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "../libraries/Fixed.sol";
import "./IComponent.sol";
/**
* @title IFurnace
* @notice A helper contract to burn RTokens slowly and permisionlessly.
*/
interface IFurnace is IComponent {
// Initialization
function init(IMain main_, uint192 ratio_) external;
/// Emitted when the melting ratio is changed
/// @param oldRatio The old ratio
/// @param newRatio The new ratio
event RatioSet(uint192 oldRatio, uint192 newRatio);
function ratio() external view returns (uint192);
/// Needed value range: [0, 1], granularity 1e-9
/// @custom:governance
function setRatio(uint192) external;
/// Performs any RToken melting that has vested since the last payout.
/// @custom:refresher
function melt() external;
}
interface TestIFurnace is IFurnace {
function lastPayout() external view returns (uint256);
function lastPayoutBal() external view returns (uint256);
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
struct GnosisAuctionData {
IERC20 auctioningToken;
IERC20 biddingToken;
uint256 orderCancellationEndDate;
uint256 auctionEndDate;
bytes32 initialAuctionOrder;
uint256 minimumBiddingAmountPerOrder;
uint256 interimSumBidAmount;
bytes32 interimOrder;
bytes32 clearingPriceOrder;
uint96 volumeClearingPriceOrder;
bool minFundingThresholdNotReached;
bool isAtomicClosureAllowed;
uint256 feeNumerator;
uint256 minFundingThreshold;
}
/// The relevant portion of the interface of the live Gnosis EasyAuction contract
/// https://github.com/gnosis/ido-contracts/blob/main/contracts/EasyAuction.sol
interface IGnosis {
function initiateAuction(
IERC20 auctioningToken,
IERC20 biddingToken,
uint256 orderCancellationEndDate,
uint256 auctionEndDate,
uint96 auctionedSellAmount,
uint96 minBuyAmount,
uint256 minimumBiddingAmountPerOrder,
uint256 minFundingThreshold,
bool isAtomicClosureAllowed,
address accessManagerContract,
bytes memory accessManagerContractData
) external returns (uint256 auctionId);
function auctionData(uint256 auctionId) external view returns (GnosisAuctionData memory);
/// @param auctionId The external auction id
/// @dev See here for decoding: https://git.io/JMang
/// @return encodedOrder The order, encoded in a bytes 32
function settleAuction(uint256 auctionId) external returns (bytes32 encodedOrder);
/// @return The numerator over a 1000-valued denominator
function feeNumerator() external returns (uint256);
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../registry/AssetPluginRegistry.sol";
import "../registry/VersionRegistry.sol";
import "../registry/DAOFeeRegistry.sol";
import "./IAssetRegistry.sol";
import "./IBasketHandler.sol";
import "./IBackingManager.sol";
import "./IBroker.sol";
import "./IDistributor.sol";
import "./IFurnace.sol";
import "./IGnosis.sol";
import "./IRToken.sol";
import "./IRevenueTrader.sol";
import "./IStRSR.sol";
import "./ITrading.sol";
import "./IVersioned.sol";
// === Auth roles ===
bytes32 constant OWNER = bytes32(bytes("OWNER"));
bytes32 constant SHORT_FREEZER = bytes32(bytes("SHORT_FREEZER"));
bytes32 constant LONG_FREEZER = bytes32(bytes("LONG_FREEZER"));
bytes32 constant PAUSER = bytes32(bytes("PAUSER"));
/**
* Main is a central hub that maintains a list of Component contracts.
*
* Components:
* - perform a specific function
* - defer auth to Main
* - usually (but not always) contain sizeable state that require a proxy
*/
struct Components {
// Definitely need proxy
IRToken rToken;
IStRSR stRSR;
IAssetRegistry assetRegistry;
IBasketHandler basketHandler;
IBackingManager backingManager;
IDistributor distributor;
IFurnace furnace;
IBroker broker;
IRevenueTrader rsrTrader;
IRevenueTrader rTokenTrader;
}
interface IAuth is IAccessControlUpgradeable {
/// Emitted when `unfreezeAt` is changed
/// @param oldVal The old value of `unfreezeAt`
/// @param newVal The new value of `unfreezeAt`
event UnfreezeAtSet(uint48 oldVal, uint48 newVal);
/// Emitted when the short freeze duration governance param is changed
/// @param oldDuration The old short freeze duration
/// @param newDuration The new short freeze duration
event ShortFreezeDurationSet(uint48 oldDuration, uint48 newDuration);
/// Emitted when the long freeze duration governance param is changed
/// @param oldDuration The old long freeze duration
/// @param newDuration The new long freeze duration
event LongFreezeDurationSet(uint48 oldDuration, uint48 newDuration);
/// Emitted when the system is paused or unpaused for trading
/// @param oldVal The old value of `tradingPaused`
/// @param newVal The new value of `tradingPaused`
event TradingPausedSet(bool oldVal, bool newVal);
/// Emitted when the system is paused or unpaused for issuance
/// @param oldVal The old value of `issuancePaused`
/// @param newVal The new value of `issuancePaused`
event IssuancePausedSet(bool oldVal, bool newVal);
/**
* Trading Paused: Disable everything except for OWNER actions, RToken.issue, RToken.redeem,
* StRSR.stake, and StRSR.payoutRewards
* Issuance Paused: Disable RToken.issue
* Frozen: Disable everything except for OWNER actions + StRSR.stake (for governance)
*/
function tradingPausedOrFrozen() external view returns (bool);
function issuancePausedOrFrozen() external view returns (bool);
function frozen() external view returns (bool);
function shortFreeze() external view returns (uint48);
function longFreeze() external view returns (uint48);
// ====
// onlyRole(OWNER)
function freezeForever() external;
// onlyRole(SHORT_FREEZER)
function freezeShort() external;
// onlyRole(LONG_FREEZER)
function freezeLong() external;
// onlyRole(OWNER)
function unfreeze() external;
function pauseTrading() external;
function unpauseTrading() external;
function pauseIssuance() external;
function unpauseIssuance() external;
}
interface IComponentRegistry {
// === Component setters/getters ===
event RTokenSet(IRToken indexed oldVal, IRToken indexed newVal);
function rToken() external view returns (IRToken);
event StRSRSet(IStRSR oldVal, IStRSR newVal);
function stRSR() external view returns (IStRSR);
event AssetRegistrySet(IAssetRegistry oldVal, IAssetRegistry newVal);
function assetRegistry() external view returns (IAssetRegistry);
event BasketHandlerSet(IBasketHandler oldVal, IBasketHandler newVal);
function basketHandler() external view returns (IBasketHandler);
event BackingManagerSet(IBackingManager oldVal, IBackingManager newVal);
function backingManager() external view returns (IBackingManager);
event DistributorSet(IDistributor oldVal, IDistributor newVal);
function distributor() external view returns (IDistributor);
event RSRTraderSet(IRevenueTrader oldVal, IRevenueTrader newVal);
function rsrTrader() external view returns (IRevenueTrader);
event RTokenTraderSet(IRevenueTrader oldVal, IRevenueTrader newVal);
function rTokenTrader() external view returns (IRevenueTrader);
event FurnaceSet(IFurnace oldVal, IFurnace newVal);
function furnace() external view returns (IFurnace);
event BrokerSet(IBroker oldVal, IBroker newVal);
function broker() external view returns (IBroker);
function isComponent(address addr) external view returns (bool);
}
/**
* @title IMain
* @notice The central hub for the entire system. Maintains components and an owner singleton role
*/
interface IMain is IVersioned, IAuth, IComponentRegistry {
function poke() external; // not used in p1
// === Initialization ===
event MainInitialized();
function init(
Components memory components,
IERC20 rsr_,
uint48 shortFreeze_,
uint48 longFreeze_
) external;
function rsr() external view returns (IERC20);
function assetPluginRegistry() external view returns (AssetPluginRegistry);
function versionRegistry() external view returns (VersionRegistry);
function daoFeeRegistry() external view returns (DAOFeeRegistry);
// === Control flow ===
function beginTx() external;
function endTx() external;
}
interface TestIMain is IMain {
error ReentrancyGuardReentrantCall();
function setVersionRegistry(VersionRegistry) external;
function setAssetPluginRegistry(AssetPluginRegistry) external;
function setDAOFeeRegistry(DAOFeeRegistry) external;
/// @custom:governance
function setShortFreeze(uint48) external;
/// @custom:governance
function setLongFreeze(uint48) external;
function shortFreeze() external view returns (uint48);
function longFreeze() external view returns (uint48);
function longFreezes(address account) external view returns (uint256);
function tradingPaused() external view returns (bool);
function issuancePaused() external view returns (bool);
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "./IComponent.sol";
import "./ITrading.sol";
/**
* @title IRevenueTrader
* @notice The RevenueTrader is an extension of the trading mixin that trades all
* assets at its address for a single target asset. There are two runtime instances
* of the RevenueTrader, 1 for RToken and 1 for RSR.
*/
interface IRevenueTrader is IComponent, ITrading {
// Initialization
function init(
IMain main_,
IERC20 tokenToBuy_,
uint192 maxTradeSlippage_,
uint192 minTradeVolume_
) external;
/// Distribute tokenToBuy to its destinations
/// @dev Special-case of manageTokens()
/// @custom:interaction
function distributeTokenToBuy() external;
/// Return registered ERC20s to the BackingManager if distribution for tokenToBuy is 0
/// @custom:interaction
function returnTokens(IERC20[] memory erc20s) external;
/// Process some number of tokens
/// If the tokenToBuy is included in erc20s, RevenueTrader will distribute it at end of the tx
/// @param erc20s The ERC20s to manage; can be tokenToBuy or anything registered
/// @param kinds The kinds of auctions to launch: DUTCH_AUCTION | BATCH_AUCTION
/// @custom:interaction
function manageTokens(IERC20[] memory erc20s, TradeKind[] memory kinds) external;
function tokenToBuy() external view returns (IERC20);
}
// solhint-disable-next-line no-empty-blocks
interface TestIRevenueTrader is IRevenueTrader, TestITrading {
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title IRewardable
* @notice A simple interface mixin to support claiming of rewards.
*/
interface IRewardable {
/// Emitted whenever a reward token balance is claimed
/// @param erc20 The ERC20 of the reward token
/// @param amount {qTok}
event RewardsClaimed(IERC20 indexed erc20, uint256 amount);
/// Claim rewards earned by holding a balance of the ERC20 token
/// Must emit `RewardsClaimed` for each token rewards are claimed for
/// @custom:interaction
function claimRewards() external;
}
/**
* @title IRewardableComponent
* @notice A simple interface mixin to support claiming of rewards.
*/
interface IRewardableComponent is IRewardable {
/// Claim rewards for a single ERC20
/// Must emit `RewardsClaimed` for each token rewards are claimed for
/// @custom:interaction
function claimRewardsSingle(IERC20 erc20) external;
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";
// solhint-disable-next-line max-line-length
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../libraries/Throttle.sol";
import "./IComponent.sol";
/**
* @title IRToken
* @notice An RToken is an ERC20 that is permissionlessly issuable/redeemable and tracks an
* exchange rate against a single unit: baskets, or {BU} in our type notation.
*/
interface IRToken is IComponent, IERC20MetadataUpgradeable, IERC20PermitUpgradeable {
/// Emitted when an issuance of RToken occurs, whether it occurs via slow minting or not
/// @param issuer The address holding collateral tokens
/// @param recipient The address of the recipient of the RTokens
/// @param amount The quantity of RToken being issued
/// @param baskets The corresponding number of baskets
event Issuance(
address indexed issuer,
address indexed recipient,
uint256 amount,
uint192 baskets
);
/// Emitted when a redemption of RToken occurs
/// @param redeemer The address holding RToken
/// @param recipient The address of the account receiving the backing collateral tokens
/// @param amount The quantity of RToken being redeemed
/// @param baskets The corresponding number of baskets
/// @param amount {qRTok} The amount of RTokens canceled
event Redemption(
address indexed redeemer,
address indexed recipient,
uint256 amount,
uint192 baskets
);
/// Emitted when the number of baskets needed changes
/// @param oldBasketsNeeded Previous number of baskets units needed
/// @param newBasketsNeeded New number of basket units needed
event BasketsNeededChanged(uint192 oldBasketsNeeded, uint192 newBasketsNeeded);
/// Emitted when RToken is melted, i.e the RToken supply is decreased but basketsNeeded is not
/// @param amount {qRTok}
event Melted(uint256 amount);
/// Emitted when issuance SupplyThrottle params are set
event IssuanceThrottleSet(ThrottleLib.Params oldVal, ThrottleLib.Params newVal);
/// Emitted when redemption SupplyThrottle params are set
event RedemptionThrottleSet(ThrottleLib.Params oldVal, ThrottleLib.Params newVal);
// Initialization
function init(
IMain main_,
string memory name_,
string memory symbol_,
string memory mandate_,
ThrottleLib.Params calldata issuanceThrottleParams,
ThrottleLib.Params calldata redemptionThrottleParams
) external;
/// Issue an RToken with basket collateral
/// @param amount {qRTok} The quantity of RToken to issue
/// @custom:interaction
function issue(uint256 amount) external;
/// Issue an RToken with basket collateral, to a particular recipient
/// @param recipient The address to receive the issued RTokens
/// @param amount {qRTok} The quantity of RToken to issue
/// @custom:interaction
function issueTo(address recipient, uint256 amount) external;
/// Redeem RToken for basket collateral
/// @dev Use redeemCustom for non-current baskets
/// @param amount {qRTok} The quantity {qRToken} of RToken to redeem
/// @custom:interaction
function redeem(uint256 amount) external;
/// Redeem RToken for basket collateral to a particular recipient
/// @dev Use redeemCustom for non-current baskets
/// @param recipient The address to receive the backing collateral tokens
/// @param amount {qRTok} The quantity {qRToken} of RToken to redeem
/// @custom:interaction
function redeemTo(address recipient, uint256 amount) external;
/// Redeem RToken for a linear combination of historical baskets, to a particular recipient
/// @dev Allows partial redemptions up to the minAmounts
/// @param recipient The address to receive the backing collateral tokens
/// @param amount {qRTok} The quantity {qRToken} of RToken to redeem
/// @param basketNonces An array of basket nonces to do redemption from
/// @param portions {1} An array of Fix quantities that must add up to FIX_ONE
/// @param expectedERC20sOut An array of ERC20s expected out
/// @param minAmounts {qTok} The minimum ERC20 quantities the caller should receive
/// @custom:interaction
function redeemCustom(
address recipient,
uint256 amount,
uint48[] memory basketNonces,
uint192[] memory portions,
address[] memory expectedERC20sOut,
uint256[] memory minAmounts
) external;
/// Mint an amount of RToken equivalent to baskets BUs, scaling basketsNeeded up
/// Callable only by BackingManager
/// @param baskets {BU} The number of baskets to mint RToken for
/// @custom:protected
function mint(uint192 baskets) external;
/// Melt a quantity of RToken from the caller's account
/// @param amount {qRTok} The amount to be melted
/// @custom:protected
function melt(uint256 amount) external;
/// Burn an amount of RToken from caller's account and scale basketsNeeded down
/// Callable only by BackingManager
/// @custom:protected
function dissolve(uint256 amount) external;
/// Set the number of baskets needed directly, callable only by the BackingManager
/// @param basketsNeeded {BU} The number of baskets to target
/// needed range: pretty interesting
/// @custom:protected
function setBasketsNeeded(uint192 basketsNeeded) external;
/// @return {BU} How many baskets are being targeted
function basketsNeeded() external view returns (uint192);
/// @return {qRTok} The maximum issuance that can be performed in the current block
function issuanceAvailable() external view returns (uint256);
/// @return {qRTok} The maximum redemption that can be performed in the current block
function redemptionAvailable() external view returns (uint256);
}
interface TestIRToken is IRToken {
function setIssuanceThrottleParams(ThrottleLib.Params calldata) external;
function setRedemptionThrottleParams(ThrottleLib.Params calldata) external;
function setThrottleParams(
ThrottleLib.Params calldata issuanceParams,
ThrottleLib.Params calldata redemptionParams
) external;
function issuanceThrottleParams() external view returns (ThrottleLib.Params memory);
function redemptionThrottleParams() external view returns (ThrottleLib.Params memory);
function increaseAllowance(address, uint256) external returns (bool);
function decreaseAllowance(address, uint256) external returns (bool);
function monetizeDonations(IERC20) external;
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";
// solhint-disable-next-line max-line-length
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol";
import "../libraries/Fixed.sol";
import "./IComponent.sol";
/**
* @title IStRSR
* @notice An ERC20 token representing shares of the RSR over-collateralization pool.
*
* StRSR permits the BackingManager to take RSR in times of need. In return, the BackingManager
* benefits the StRSR pool with RSR rewards purchased with a portion of its revenue.
*
* In the absence of collateral default or losses due to slippage, StRSR should have a
* monotonically increasing exchange rate with respect to RSR, meaning that over time
* StRSR is redeemable for more RSR. It is non-rebasing.
*/
interface IStRSR is IERC20MetadataUpgradeable, IERC20PermitUpgradeable, IComponent {
error InsufficientBalance();
error IndexOutOfBounds();
error WithdrawalUnavailable();
error RTokenNotReady();
error NotBackingManager();
error SeizeExceedsBalance();
error RatesStillSafe();
error DecreaseAllowanceError();
error InsufficientAllowance();
error TransferToSelf();
error ZeroAddress();
error ZeroAmount();
error ExpiredDeadline();
error InvalidUnstakingDelay();
error InvalidRewardRatio();
error InvalidWithdrawalLeak();
error SignatureExpired();
error InvalidNonce();
error FutureLookup();
error NameEmpty();
error SymbolEmpty();
/// Emitted when RSR is staked
/// @param era The era at time of staking
/// @param staker The address of the staker
/// @param rsrAmount {qRSR} How much RSR was staked
/// @param stRSRAmount {qStRSR} How much stRSR was minted by this staking
event Staked(
uint256 indexed era,
address indexed staker,
uint256 rsrAmount,
uint256 stRSRAmount
);
/// Emitted when an unstaking is started
/// @param draftId The id of the draft.
/// @param draftEra The era of the draft.
/// @param staker The address of the unstaker
/// The triple (staker, draftEra, draftId) is a unique ID
/// @param rsrAmount {qRSR} How much RSR this unstaking will be worth, absent seizures
/// @param stRSRAmount {qStRSR} How much stRSR was burned by this unstaking
event UnstakingStarted(
uint256 indexed draftId,
uint256 indexed draftEra,
address indexed staker,
uint256 rsrAmount,
uint256 stRSRAmount,
uint256 availableAt
);
/// Emitted when RSR is unstaked
/// @param firstId The beginning of the range of draft IDs withdrawn in this transaction
/// @param endId The end of range of draft IDs withdrawn in this transaction
/// (ID i was withdrawn if firstId <= i < endId)
/// @param draftEra The era of the draft.
/// The triple (staker, draftEra, id) is a unique ID among drafts
/// @param staker The address of the unstaker
/// @param rsrAmount {qRSR} How much RSR this unstaking was worth
event UnstakingCompleted(
uint256 indexed firstId,
uint256 indexed endId,
uint256 draftEra,
address indexed staker,
uint256 rsrAmount
);
/// Emitted when RSR unstaking is cancelled
/// @param firstId The beginning of the range of draft IDs withdrawn in this transaction
/// @param endId The end of range of draft IDs withdrawn in this transaction
/// (ID i was withdrawn if firstId <= i < endId)
/// @param draftEra The era of the draft.
/// The triple (staker, draftEra, id) is a unique ID among drafts
/// @param staker The address of the unstaker
/// @param rsrAmount {qRSR} How much RSR this unstaking was worth
event UnstakingCancelled(
uint256 indexed firstId,
uint256 indexed endId,
uint256 draftEra,
address indexed staker,
uint256 rsrAmount
);
/// Emitted whenever the exchange rate changes
event ExchangeRateSet(uint192 oldVal, uint192 newVal);
/// Emitted whenever RSR are paids out
event RewardsPaid(uint256 rsrAmt);
/// Emitted if all the RSR in the staking pool is seized and all balances are reset to zero.
event AllBalancesReset(uint256 indexed newEra);
/// Emitted if all the RSR in the unstakin pool is seized, and all ongoing unstaking is voided.
event AllUnstakingReset(uint256 indexed newEra);
event UnstakingDelaySet(uint48 oldVal, uint48 newVal);
event RewardRatioSet(uint192 oldVal, uint192 newVal);
event WithdrawalLeakSet(uint192 oldVal, uint192 newVal);
// Initialization
function init(
IMain main_,
string memory name_,
string memory symbol_,
uint48 unstakingDelay_,
uint192 rewardRatio_,
uint192 withdrawalLeak_
) external;
/// Gather and payout rewards from rsrTrader
/// @custom:interaction
function payoutRewards() external;
/// Stakes an RSR `amount` on the corresponding RToken to earn yield and over-collateralized
/// the system
/// @param amount {qRSR}
/// @custom:interaction
function stake(uint256 amount) external;
/// Begins a delayed unstaking for `amount` stRSR
/// @param amount {qStRSR}
/// @custom:interaction
function unstake(uint256 amount) external;
/// Complete delayed unstaking for the account, up to (but not including!) `endId`
/// @custom:interaction
function withdraw(address account, uint256 endId) external;
/// Cancel unstaking for the account, up to (but not including!) `endId`
/// @custom:interaction
function cancelUnstake(uint256 endId) external;
/// Seize RSR, only callable by main.backingManager()
/// @custom:protected
function seizeRSR(uint256 amount) external;
/// Reset all stakes and advance era
/// @custom:governance
function resetStakes() external;
/// Return the maximum valid value of endId such that withdraw(endId) should immediately work
function endIdForWithdraw(address account) external view returns (uint256 endId);
/// @return {qRSR/qStRSR} The exchange rate between RSR and StRSR
function exchangeRate() external view returns (uint192);
}
interface TestIStRSR is IStRSR {
function rewardRatio() external view returns (uint192);
function setRewardRatio(uint192) external;
function unstakingDelay() external view returns (uint48);
function setUnstakingDelay(uint48) external;
function withdrawalLeak() external view returns (uint192);
function setWithdrawalLeak(uint192) external;
function increaseAllowance(address, uint256) external returns (bool);
function decreaseAllowance(address, uint256) external returns (bool);
/// @return {qStRSR/qRSR} The exchange rate between StRSR and RSR
function exchangeRate() external view returns (uint192);
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "./IBroker.sol";
import "./IVersioned.sol";
enum TradeStatus {
NOT_STARTED, // before init()
OPEN, // after init() and before settle()
CLOSED, // after settle()
// === Intermediate-tx state ===
PENDING // during init() or settle() (reentrancy protection)
}
/**
* Simple generalized trading interface for all Trade contracts to obey
*
* Usage: if (canSettle()) settle()
*/
interface ITrade is IVersioned {
/// Complete the trade and transfer tokens back to the origin trader
/// @return soldAmt {qSellTok} The quantity of tokens sold
/// @return boughtAmt {qBuyTok} The quantity of tokens bought
function settle() external returns (uint256 soldAmt, uint256 boughtAmt);
function sell() external view returns (IERC20Metadata);
function buy() external view returns (IERC20Metadata);
/// @return {tok} The sell amount of the trade, in whole tokens
function sellAmount() external view returns (uint192);
/// @return The timestamp at which the trade is projected to become settle-able
function endTime() external view returns (uint48);
/// @return True if the trade can be settled
/// @dev Should be guaranteed to be true eventually as an invariant
function canSettle() external view returns (bool);
/// @return TradeKind.DUTCH_AUCTION or TradeKind.BATCH_AUCTION
// solhint-disable-next-line func-name-mixedcase
function KIND() external view returns (TradeKind);
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../libraries/Fixed.sol";
import "./IComponent.sol";
import "./ITrade.sol";
import "./IRewardable.sol";
/**
* @title ITrading
* @notice Common events and refresher function for all Trading contracts
*/
interface ITrading is IComponent, IRewardableComponent {
event MaxTradeSlippageSet(uint192 oldVal, uint192 newVal);
event MinTradeVolumeSet(uint192 oldVal, uint192 newVal);
/// Emitted when a trade is started
/// @param trade The one-time-use trade contract that was just deployed
/// @param sell The token to sell
/// @param buy The token to buy
/// @param sellAmount {qSellTok} The quantity of the selling token
/// @param minBuyAmount {qBuyTok} The minimum quantity of the buying token to accept
event TradeStarted(
ITrade indexed trade,
IERC20 indexed sell,
IERC20 indexed buy,
uint256 sellAmount,
uint256 minBuyAmount
);
/// Emitted after a trade ends
/// @param trade The one-time-use trade contract
/// @param sell The token to sell
/// @param buy The token to buy
/// @param sellAmount {qSellTok} The quantity of the token sold
/// @param buyAmount {qBuyTok} The quantity of the token bought
event TradeSettled(
ITrade indexed trade,
IERC20 indexed sell,
IERC20 indexed buy,
uint256 sellAmount,
uint256 buyAmount
);
/// Forcibly settle a trade, losing all value
/// Should only be called in case of censorship
/// @param trade The trade address itself
/// @custom:governance
function forceSettleTrade(ITrade trade) external;
/// Settle a single trade, expected to be used with multicall for efficient mass settlement
/// @param sell The sell token in the trade
/// @return The trade settled
/// @custom:refresher
function settleTrade(IERC20 sell) external returns (ITrade);
/// @return {%} The maximum trade slippage acceptable
function maxTradeSlippage() external view returns (uint192);
/// @return {UoA} The minimum trade volume in UoA, applies to all assets
function minTradeVolume() external view returns (uint192);
/// @return The ongoing trade for a sell token, or the zero address
function trades(IERC20 sell) external view returns (ITrade);
/// @return The number of ongoing trades open
function tradesOpen() external view returns (uint48);
/// @return The number of total trades ever opened
function tradesNonce() external view returns (uint256);
}
interface TestITrading is ITrading {
/// @custom:governance
function setMaxTradeSlippage(uint192 val) external;
/// @custom:governance
function setMinTradeVolume(uint192 val) external;
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
interface IVersioned {
function version() external view returns (string memory);
}// SPDX-License-Identifier: BlueOak-1.0.0
// solhint-disable func-name-mixedcase func-visibility
// slither-disable-start divide-before-multiply
pragma solidity 0.8.28;
/// @title FixedPoint, a fixed-point arithmetic library defining the custom type uint192
/// @author Matt Elder <matt.elder@reserve.org> and the Reserve Team <https://reserve.org>
/** The logical type `uint192 ` is a 192 bit value, representing an 18-decimal Fixed-point
fractional value. This is what's described in the Solidity documentation as
"fixed192x18" -- a value represented by 192 bits, that makes 18 digits available to
the right of the decimal point.
The range of values that uint192 can represent is [0, 2^192-1 / 10^18 = 6.2e39].
Unless a function explicitly says otherwise, it will fail on overflow.
To be clear, the following should hold:
toFix(0) == 0
toFix(1) == 1e18
*/
// Analysis notes:
// Every function should revert iff its result is out of bounds.
// Unless otherwise noted, when a rounding mode is given, that mode is applied to
// a single division that may happen as the last step in the computation.
// Unless otherwise noted, when a rounding mode is *not* given but is needed, it's FLOOR.
// For each, we comment:
// - @return is the value expressed in "value space", where uint192(1e18) "is" 1.0
// - as-ints: is the value expressed in "implementation space", where uint192(1e18) "is" 1e18
// The "@return" expression is suitable for actually using the library
// The "as-ints" expression is suitable for testing
// A uint value passed to this library was out of bounds for uint192 operations
error UIntOutOfBounds();
bytes32 constant UIntOutofBoundsHash = keccak256(abi.encodeWithSignature("UIntOutOfBounds()"));
// Used by P1 implementation for easier casting
uint256 constant FIX_ONE_256 = 1e18;
uint8 constant FIX_DECIMALS = 18;
// If a particular uint192 is represented by the uint192 n, then the uint192 represents the
// value n/FIX_SCALE.
uint64 constant FIX_SCALE = 1e18;
// FIX_SCALE Squared:
uint128 constant FIX_SCALE_SQ = 1e36;
// The largest integer that can be converted to uint192.
// This is a bit bigger than 6.2e39
uint192 constant FIX_MAX_INT = type(uint192).max / FIX_SCALE;
uint192 constant FIX_ZERO = 0; // The uint192 representation of zero.
uint192 constant FIX_ONE = FIX_SCALE; // The uint192 representation of one.
uint192 constant FIX_MAX = type(uint192).max; // The largest uint192. (Not an integer!)
uint192 constant FIX_MIN = 0; // The smallest uint192.
/// An enum that describes a rounding approach for converting to ints
enum RoundingMode {
FLOOR, // Round towards zero
ROUND, // Round to the nearest int
CEIL // Round away from zero
}
RoundingMode constant FLOOR = RoundingMode.FLOOR;
RoundingMode constant ROUND = RoundingMode.ROUND;
RoundingMode constant CEIL = RoundingMode.CEIL;
/* @dev Solidity 0.8.x only allows you to change one of type or size per type conversion.
Thus, all the tedious-looking double conversions like uint256(uint256 (foo))
See: https://docs.soliditylang.org/en/v0.8.17/080-breaking-changes.html#new-restrictions
*/
/// Explicitly convert a uint256 to a uint192. Revert if the input is out of bounds.
function _safeWrap(uint256 x) pure returns (uint192) {
if (FIX_MAX < x) revert UIntOutOfBounds();
return uint192(x);
}
/// Convert a uint to its Fix representation.
/// @return x
// as-ints: x * 1e18
function toFix(uint256 x) pure returns (uint192) {
return _safeWrap(x * FIX_SCALE);
}
/// Convert a uint to its fixed-point representation, and left-shift its value `shiftLeft`
/// decimal digits.
/// @return x * 10**shiftLeft
// as-ints: x * 10**(shiftLeft + 18)
function shiftl_toFix(uint256 x, int8 shiftLeft) pure returns (uint192) {
return shiftl_toFix(x, shiftLeft, FLOOR);
}
/// @return x * 10**shiftLeft
// as-ints: x * 10**(shiftLeft + 18)
function shiftl_toFix(
uint256 x,
int8 shiftLeft,
RoundingMode rounding
) pure returns (uint192) {
// conditions for avoiding overflow
if (x == 0) return 0;
if (shiftLeft <= -96) return (rounding == CEIL ? 1 : 0); // 0 < uint.max / 10**77 < 0.5
if (40 <= shiftLeft) revert UIntOutOfBounds(); // 10**57 < FIX_MAX < 10**58
shiftLeft += 18;
uint256 coeff = 10**abs(shiftLeft);
uint256 shifted = (shiftLeft >= 0) ? x * coeff : _divrnd(x, coeff, rounding);
return _safeWrap(shifted);
}
/// Divide a uint by a uint192, yielding a uint192
/// This may also fail if the result is MIN_uint192! not fixing this for optimization's sake.
/// @return x / y
// as-ints: x * 1e36 / y
function divFix(uint256 x, uint192 y) pure returns (uint192) {
// If we didn't have to worry about overflow, we'd just do `return x * 1e36 / _y`
// If it's safe to do this operation the easy way, do it:
if (x < uint256(type(uint256).max / FIX_SCALE_SQ)) {
return _safeWrap(uint256(x * FIX_SCALE_SQ) / y);
} else {
return _safeWrap(mulDiv256(x, FIX_SCALE_SQ, y));
}
}
/// Divide a uint by a uint, yielding a uint192
/// @return x / y
// as-ints: x * 1e18 / y
function divuu(uint256 x, uint256 y) pure returns (uint192) {
return _safeWrap(mulDiv256(FIX_SCALE, x, y));
}
/// @return min(x,y)
// as-ints: min(x,y)
function fixMin(uint192 x, uint192 y) pure returns (uint192) {
return x < y ? x : y;
}
/// @return max(x,y)
// as-ints: max(x,y)
function fixMax(uint192 x, uint192 y) pure returns (uint192) {
return x > y ? x : y;
}
/// @return absoluteValue(x,y)
// as-ints: absoluteValue(x,y)
function abs(int256 x) pure returns (uint256) {
return x < 0 ? uint256(-x) : uint256(x);
}
/// Divide two uints, returning a uint, using rounding mode `rounding`.
/// @return numerator / divisor
// as-ints: numerator / divisor
function _divrnd(
uint256 numerator,
uint256 divisor,
RoundingMode rounding
) pure returns (uint256) {
uint256 result = numerator / divisor;
if (rounding == FLOOR) return result;
if (rounding == ROUND) {
if (numerator % divisor > (divisor - 1) / 2) {
result++;
}
} else {
if (numerator % divisor != 0) {
result++;
}
}
return result;
}
library FixLib {
/// Again, all arithmetic functions fail if and only if the result is out of bounds.
/// Convert this fixed-point value to a uint. Round towards zero if needed.
/// @return x
// as-ints: x / 1e18
function toUint(uint192 x) internal pure returns (uint136) {
return toUint(x, FLOOR);
}
/// Convert this uint192 to a uint
/// @return x
// as-ints: x / 1e18 with rounding
function toUint(uint192 x, RoundingMode rounding) internal pure returns (uint136) {
return uint136(_divrnd(uint256(x), FIX_SCALE, rounding));
}
/// Return the uint192 shifted to the left by `decimal` digits
/// (Similar to a bitshift but in base 10)
/// @return x * 10**decimals
// as-ints: x * 10**decimals
function shiftl(uint192 x, int8 decimals) internal pure returns (uint192) {
return shiftl(x, decimals, FLOOR);
}
/// Return the uint192 shifted to the left by `decimal` digits
/// (Similar to a bitshift but in base 10)
/// @return x * 10**decimals
// as-ints: x * 10**decimals
function shiftl(
uint192 x,
int8 decimals,
RoundingMode rounding
) internal pure returns (uint192) {
// Handle overflow cases
if (x == 0) return 0;
if (decimals <= -59) return (rounding == CEIL ? 1 : 0); // 59, because 1e58 > 2**192
if (58 <= decimals) revert UIntOutOfBounds(); // 58, because x * 1e58 > 2 ** 192 if x != 0
uint256 coeff = uint256(10**abs(decimals));
return _safeWrap(decimals >= 0 ? x * coeff : _divrnd(x, coeff, rounding));
}
/// Add a uint192 to this uint192
/// @return x + y
// as-ints: x + y
function plus(uint192 x, uint192 y) internal pure returns (uint192) {
return x + y;
}
/// Add a uint to this uint192
/// @return x + y
// as-ints: x + y*1e18
function plusu(uint192 x, uint256 y) internal pure returns (uint192) {
return _safeWrap(x + y * FIX_SCALE);
}
/// Subtract a uint192 from this uint192
/// @return x - y
// as-ints: x - y
function minus(uint192 x, uint192 y) internal pure returns (uint192) {
return x - y;
}
/// Subtract a uint from this uint192
/// @return x - y
// as-ints: x - y*1e18
function minusu(uint192 x, uint256 y) internal pure returns (uint192) {
return _safeWrap(uint256(x) - uint256(y * FIX_SCALE));
}
/// Multiply this uint192 by a uint192
/// Round truncated values to the nearest available value. 5e-19 rounds away from zero.
/// @return x * y
// as-ints: x * y/1e18 [division using ROUND, not FLOOR]
function mul(uint192 x, uint192 y) internal pure returns (uint192) {
return mul(x, y, ROUND);
}
/// Multiply this uint192 by a uint192
/// @return x * y
// as-ints: x * y/1e18
function mul(
uint192 x,
uint192 y,
RoundingMode rounding
) internal pure returns (uint192) {
return _safeWrap(_divrnd(uint256(x) * uint256(y), FIX_SCALE, rounding));
}
/// Multiply this uint192 by a uint
/// @return x * y
// as-ints: x * y
function mulu(uint192 x, uint256 y) internal pure returns (uint192) {
return _safeWrap(x * y);
}
/// Divide this uint192 by a uint192
/// @return x / y
// as-ints: x * 1e18 / y
function div(uint192 x, uint192 y) internal pure returns (uint192) {
return div(x, y, FLOOR);
}
/// Divide this uint192 by a uint192
/// @return x / y
// as-ints: x * 1e18 / y
function div(
uint192 x,
uint192 y,
RoundingMode rounding
) internal pure returns (uint192) {
// Multiply-in FIX_SCALE before dividing by y to preserve precision.
return _safeWrap(_divrnd(uint256(x) * FIX_SCALE, y, rounding));
}
/// Divide this uint192 by a uint
/// @return x / y
// as-ints: x / y
function divu(uint192 x, uint256 y) internal pure returns (uint192) {
return divu(x, y, FLOOR);
}
/// Divide this uint192 by a uint
/// @return x / y
// as-ints: x / y
function divu(
uint192 x,
uint256 y,
RoundingMode rounding
) internal pure returns (uint192) {
return _safeWrap(_divrnd(x, y, rounding));
}
uint64 constant FIX_HALF = uint64(FIX_SCALE) / 2;
/// Raise this uint192 to a nonnegative integer power. Requires that x_ <= FIX_ONE
/// Gas cost is O(lg(y)), precision is +- 1e-18.
/// @return x_ ** y
// as-ints: x_ ** y / 1e18**(y-1) <- technically correct for y = 0. :D
function powu(uint192 x_, uint48 y) internal pure returns (uint192) {
require(x_ <= FIX_ONE);
if (y == 1) return x_;
if (x_ == FIX_ONE || y == 0) return FIX_ONE;
uint256 x = uint256(x_) * FIX_SCALE; // x is D36
uint256 result = FIX_SCALE_SQ; // result is D36
while (true) {
if (y & 1 == 1) result = (result * x + FIX_SCALE_SQ / 2) / FIX_SCALE_SQ;
if (y <= 1) break;
y = (y >> 1);
x = (x * x + FIX_SCALE_SQ / 2) / FIX_SCALE_SQ;
}
return _safeWrap(result / FIX_SCALE);
}
function sqrt(uint192 x) internal pure returns (uint192) {
return _safeWrap(sqrt256(x * FIX_ONE_256)); // FLOOR
}
/// Comparison operators...
function lt(uint192 x, uint192 y) internal pure returns (bool) {
return x < y;
}
function lte(uint192 x, uint192 y) internal pure returns (bool) {
return x <= y;
}
function gt(uint192 x, uint192 y) internal pure returns (bool) {
return x > y;
}
function gte(uint192 x, uint192 y) internal pure returns (bool) {
return x >= y;
}
function eq(uint192 x, uint192 y) internal pure returns (bool) {
return x == y;
}
function neq(uint192 x, uint192 y) internal pure returns (bool) {
return x != y;
}
/// Return whether or not this uint192 is less than epsilon away from y.
/// @return |x - y| < epsilon
// as-ints: |x - y| < epsilon
function near(
uint192 x,
uint192 y,
uint192 epsilon
) internal pure returns (bool) {
uint192 diff = x <= y ? y - x : x - y;
return diff < epsilon;
}
// ================ Chained Operations ================
// The operation foo_bar() always means:
// Do foo() followed by bar(), and overflow only if the _end_ result doesn't fit in an uint192
/// Shift this uint192 left by `decimals` digits, and convert to a uint
/// @return x * 10**decimals
// as-ints: x * 10**(decimals - 18)
function shiftl_toUint(uint192 x, int8 decimals) internal pure returns (uint256) {
return shiftl_toUint(x, decimals, FLOOR);
}
/// Shift this uint192 left by `decimals` digits, and convert to a uint.
/// @return x * 10**decimals
// as-ints: x * 10**(decimals - 18)
function shiftl_toUint(
uint192 x,
int8 decimals,
RoundingMode rounding
) internal pure returns (uint256) {
// Handle overflow cases
if (x == 0) return 0; // always computable, no matter what decimals is
if (decimals <= -42) return (rounding == CEIL ? 1 : 0);
if (96 <= decimals) revert UIntOutOfBounds();
decimals -= 18; // shift so that toUint happens at the same time.
uint256 coeff = uint256(10**abs(decimals));
return decimals >= 0 ? uint256(x * coeff) : uint256(_divrnd(x, coeff, rounding));
}
/// Multiply this uint192 by a uint, and output the result as a uint
/// @return x * y
// as-ints: x * y / 1e18
function mulu_toUint(uint192 x, uint256 y) internal pure returns (uint256) {
return mulDiv256(uint256(x), y, FIX_SCALE);
}
/// Multiply this uint192 by a uint, and output the result as a uint
/// @return x * y
// as-ints: x * y / 1e18
function mulu_toUint(
uint192 x,
uint256 y,
RoundingMode rounding
) internal pure returns (uint256) {
return mulDiv256(uint256(x), y, FIX_SCALE, rounding);
}
/// Multiply this uint192 by a uint192 and output the result as a uint
/// @return x * y
// as-ints: x * y / 1e36
function mul_toUint(uint192 x, uint192 y) internal pure returns (uint256) {
return mulDiv256(uint256(x), uint256(y), FIX_SCALE_SQ);
}
/// Multiply this uint192 by a uint192 and output the result as a uint
/// @return x * y
// as-ints: x * y / 1e36
function mul_toUint(
uint192 x,
uint192 y,
RoundingMode rounding
) internal pure returns (uint256) {
return mulDiv256(uint256(x), uint256(y), FIX_SCALE_SQ, rounding);
}
/// Compute x * y / z avoiding intermediate overflow
/// @dev Only use if you need to avoid overflow; costlier than x * y / z
/// @return x * y / z
// as-ints: x * y / z
function muluDivu(
uint192 x,
uint256 y,
uint256 z
) internal pure returns (uint192) {
return muluDivu(x, y, z, FLOOR);
}
/// Compute x * y / z, avoiding intermediate overflow
/// @dev Only use if you need to avoid overflow; costlier than x * y / z
/// @return x * y / z
// as-ints: x * y / z
function muluDivu(
uint192 x,
uint256 y,
uint256 z,
RoundingMode rounding
) internal pure returns (uint192) {
return _safeWrap(mulDiv256(x, y, z, rounding));
}
/// Compute x * y / z on Fixes, avoiding intermediate overflow
/// @dev Only use if you need to avoid overflow; costlier than x * y / z
/// @return x * y / z
// as-ints: x * y / z
function mulDiv(
uint192 x,
uint192 y,
uint192 z
) internal pure returns (uint192) {
return mulDiv(x, y, z, FLOOR);
}
/// Compute x * y / z on Fixes, avoiding intermediate overflow
/// @dev Only use if you need to avoid overflow; costlier than x * y / z
/// @return x * y / z
// as-ints: x * y / z
function mulDiv(
uint192 x,
uint192 y,
uint192 z,
RoundingMode rounding
) internal pure returns (uint192) {
return _safeWrap(mulDiv256(x, y, z, rounding));
}
// === safe*() ===
/// Multiply two fixes, rounding up to FIX_MAX and down to 0
/// @param a First param to multiply
/// @param b Second param to multiply
function safeMul(
uint192 a,
uint192 b,
RoundingMode rounding
) internal pure returns (uint192) {
// untestable:
// a will never = 0 here because of the check in _price()
if (a == 0 || b == 0) return 0;
// untestable:
// a = FIX_MAX iff b = 0
if (a == FIX_MAX || b == FIX_MAX) return FIX_MAX;
// return FIX_MAX instead of throwing overflow errors.
unchecked {
// p and mul *are* Fix values, so have 18 decimals (D18)
uint256 rawDelta = uint256(b) * a; // {D36} = {D18} * {D18}
// if we overflowed, then return FIX_MAX
if (rawDelta / b != a) return FIX_MAX;
uint256 shiftDelta = rawDelta;
// add in rounding
if (rounding == RoundingMode.ROUND) shiftDelta += (FIX_ONE / 2);
else if (rounding == RoundingMode.CEIL) shiftDelta += FIX_ONE - 1;
// untestable (here there be dragons):
// (below explanation is for the ROUND case, but it extends to the FLOOR/CEIL too)
// A) shiftDelta = rawDelta + (FIX_ONE / 2)
// shiftDelta overflows if:
// B) shiftDelta = MAX_UINT256 - FIX_ONE/2 + 1
// rawDelta + (FIX_ONE/2) = MAX_UINT256 - FIX_ONE/2 + 1
// b * a = MAX_UINT256 - FIX_ONE + 1
// therefore shiftDelta overflows if:
// C) b = (MAX_UINT256 - FIX_ONE + 1) / a
// MAX_UINT256 ~= 1e77 , FIX_MAX ~= 6e57 (6e20 difference in magnitude)
// a <= 1e21 (MAX_TARGET_AMT)
// a must be between 1e19 & 1e20 in order for b in (C) to be uint192,
// but a would have to be < 1e18 in order for (A) to overflow
if (shiftDelta < rawDelta) return FIX_MAX;
// return FIX_MAX if return result would truncate
if (shiftDelta / FIX_ONE > FIX_MAX) return FIX_MAX;
// return _div(rawDelta, FIX_ONE, rounding)
return uint192(shiftDelta / FIX_ONE); // {D18} = {D36} / {D18}
}
}
/// Divide two fixes, rounding up to FIX_MAX and down to 0
/// @param a Numerator
/// @param b Denominator
function safeDiv(
uint192 a,
uint192 b,
RoundingMode rounding
) internal pure returns (uint192) {
if (a == 0) return 0;
if (b == 0) return FIX_MAX;
uint256 raw = _divrnd(FIX_ONE_256 * a, uint256(b), rounding);
if (raw >= FIX_MAX) return FIX_MAX;
return uint192(raw); // don't need _safeWrap
}
/// Multiplies two fixes and divide by a third
/// @param a First to multiply
/// @param b Second to multiply
/// @param c Denominator
function safeMulDiv(
uint192 a,
uint192 b,
uint192 c,
RoundingMode rounding
) internal pure returns (uint192 result) {
if (a == 0 || b == 0) return 0;
if (a == FIX_MAX || b == FIX_MAX || c == 0) return FIX_MAX;
uint256 result_256;
unchecked {
(uint256 hi, uint256 lo) = fullMul(a, b);
if (hi >= c) return FIX_MAX;
uint256 mm = mulmod(a, b, c);
if (mm > lo) hi -= 1;
lo -= mm;
uint256 pow2 = c & (0 - c);
uint256 c_256 = uint256(c);
c_256 /= pow2;
lo /= pow2;
lo += hi * ((0 - pow2) / pow2 + 1);
uint256 r = 1;
r *= 2 - c_256 * r;
r *= 2 - c_256 * r;
r *= 2 - c_256 * r;
r *= 2 - c_256 * r;
r *= 2 - c_256 * r;
r *= 2 - c_256 * r;
r *= 2 - c_256 * r;
r *= 2 - c_256 * r;
result_256 = lo * r;
// Apply rounding
if (rounding == CEIL) {
if (mm != 0) result_256 += 1;
} else if (rounding == ROUND) {
if (mm > ((c - 1) / 2)) result_256 += 1; // intentional: use pre-divided c here
}
}
if (result_256 >= FIX_MAX) return FIX_MAX;
return uint192(result_256);
}
}
// ================ a couple pure-uint helpers================
// as-ints comments are omitted here, because they're the same as @return statements, because
// these are all pure uint functions
/// Return (x*y/z), avoiding intermediate overflow.
// Adapted from sources:
// https://medium.com/coinmonks/4db014e080b1, https://medium.com/wicketh/afa55870a65
// and quite a few of the other excellent "Mathemagic" posts from https://medium.com/wicketh
/// @dev Only use if you need to avoid overflow; costlier than x * y / z
/// @return result x * y / z
function mulDiv256(
uint256 x,
uint256 y,
uint256 z
) pure returns (uint256 result) {
unchecked {
(uint256 hi, uint256 lo) = fullMul(x, y);
if (hi >= z) revert UIntOutOfBounds();
uint256 mm = mulmod(x, y, z);
if (mm > lo) hi -= 1;
lo -= mm;
uint256 pow2 = z & (0 - z);
z /= pow2;
lo /= pow2;
lo += hi * ((0 - pow2) / pow2 + 1);
uint256 r = 1;
r *= 2 - z * r;
r *= 2 - z * r;
r *= 2 - z * r;
r *= 2 - z * r;
r *= 2 - z * r;
r *= 2 - z * r;
r *= 2 - z * r;
r *= 2 - z * r;
result = lo * r;
}
}
/// Return (x*y/z), avoiding intermediate overflow.
/// @dev Only use if you need to avoid overflow; costlier than x * y / z
/// @return x * y / z
function mulDiv256(
uint256 x,
uint256 y,
uint256 z,
RoundingMode rounding
) pure returns (uint256) {
uint256 result = mulDiv256(x, y, z);
if (rounding == FLOOR) return result;
uint256 mm = mulmod(x, y, z);
if (rounding == CEIL) {
if (mm != 0) result += 1;
} else {
if (mm > ((z - 1) / 2)) result += 1; // z should be z-1
}
return result;
}
/// Return (x*y) as a "virtual uint512" (lo, hi), representing (hi*2**256 + lo)
/// Adapted from sources:
/// https://medium.com/wicketh/27650fec525d, https://medium.com/coinmonks/4db014e080b1
/// @dev Intended to be internal to this library
/// @return hi (hi, lo) satisfies hi*(2**256) + lo == x * y
/// @return lo (paired with `hi`)
function fullMul(uint256 x, uint256 y) pure returns (uint256 hi, uint256 lo) {
unchecked {
uint256 mm = mulmod(x, y, uint256(0) - uint256(1));
lo = x * y;
hi = mm - lo;
if (mm < lo) hi -= 1;
}
}
// =============== from prbMath at commit 28055f6cd9a2367f9ad7ab6c8e01c9ac8e9acc61 ===============
/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - If x is not a perfect square, the result is rounded down.
/// - Credits to OpenZeppelin for the explanations in comments below.
///
/// @param x The uint256 number for which to calculate the square root.
/// @return result The result as a uint256.
function sqrt256(uint256 x) pure returns (uint256 result) {
if (x == 0) {
return 0;
}
// For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.
//
// We know that the "msb" (most significant bit) of x is a power of 2 such that we have:
//
// $$
// msb(x) <= x <= 2*msb(x)$
// $$
//
// We write $msb(x)$ as $2^k$, and we get:
//
// $$
// k = log_2(x)
// $$
//
// Thus, we can write the initial inequality as:
//
// $$
// 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\
// sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\
// 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}
// $$
//
// Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.
uint256 xAux = uint256(x);
result = 1;
if (xAux >= 2**128) {
xAux >>= 128;
result <<= 64;
}
if (xAux >= 2**64) {
xAux >>= 64;
result <<= 32;
}
if (xAux >= 2**32) {
xAux >>= 32;
result <<= 16;
}
if (xAux >= 2**16) {
xAux >>= 16;
result <<= 8;
}
if (xAux >= 2**8) {
xAux >>= 8;
result <<= 4;
}
if (xAux >= 2**4) {
xAux >>= 4;
result <<= 2;
}
if (xAux >= 2**2) {
result <<= 1;
}
// At this point, `result` is an estimation with at least one bit of precision. We know the true value has at
// most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision
// doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of
// precision into the expected uint128 result.
unchecked {
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
// If x is not a perfect square, round the result toward zero.
uint256 roundedResult = x / result;
if (result >= roundedResult) {
result = roundedResult;
}
}
}
// slither-disable-end divide-before-multiply// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "./Fixed.sol";
uint48 constant ONE_HOUR = 3600; // {seconds/hour}
/**
* @title ThrottleLib
* A library that implements a usage throttle that can be used to ensure net issuance
* or net redemption for an RToken never exceeds some bounds per unit time (hour).
*
* It is expected for the RToken to use this library with two instances, one for issuance
* and one for redemption. Issuance causes the available redemption amount to increase, and
* visa versa.
*/
library ThrottleLib {
using FixLib for uint192;
struct Params {
uint256 amtRate; // {qRTok/hour} a quantity of RToken hourly; cannot be 0
uint192 pctRate; // {1/hour} a fraction of RToken hourly; can be 0
}
struct Throttle {
// === Gov params ===
Params params;
// === Cache ===
uint48 lastTimestamp; // {seconds}
uint256 lastAvailable; // {qRTok}
}
/// Reverts if usage amount exceeds available amount
/// @param supply {qRTok} Total RToken supply beforehand
/// @param amount {qRTok} Amount of RToken to use. Should be negative for the issuance
/// throttle during redemption and for the redemption throttle during issuance.
function useAvailable(
Throttle storage throttle,
uint256 supply,
int256 amount
) internal {
// untestable: amtRate will always be > 0 due to previous validations
if (throttle.params.amtRate == 0 && throttle.params.pctRate == 0) return;
// Calculate hourly limit
uint256 limit = hourlyLimit(throttle, supply); // {qRTok}
// Calculate available amount before supply change
uint256 available = currentlyAvailable(throttle, limit);
// Update throttle.timestamp if available amount changed or at limit
if (available != throttle.lastAvailable || available == limit) {
throttle.lastTimestamp = uint48(block.timestamp);
}
// Update throttle.lastAvailable
if (amount > 0) {
require(uint256(amount) <= available, "supply change throttled");
available -= uint256(amount);
// untestable: the final else statement, amount will never be 0
} else if (amount < 0) {
available += uint256(-amount);
}
throttle.lastAvailable = available;
}
/// @param limit {qRTok/hour} The hourly limit
/// @return available {qRTok} Amount currently available for consumption
function currentlyAvailable(Throttle storage throttle, uint256 limit)
internal
view
returns (uint256 available)
{
uint48 delta = uint48(block.timestamp) - throttle.lastTimestamp; // {seconds}
available = throttle.lastAvailable + (limit * delta) / ONE_HOUR;
if (available > limit) available = limit;
}
/// @return limit {qRTok} The hourly limit
function hourlyLimit(Throttle storage throttle, uint256 supply)
internal
view
returns (uint256 limit)
{
Params storage params = throttle.params;
// Calculate hourly limit as: max(params.amtRate, supply.mul(params.pctRate))
limit = (supply * params.pctRate) / FIX_ONE_256; // {qRTok}
if (params.amtRate > limit) limit = params.amtRate;
}
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "../../interfaces/IAsset.sol";
import "../../libraries/Fixed.sol";
import "./FiatCollateral.sol";
import "./Asset.sol";
import "./OracleLib.sol";
/**
* @title AppreciatingFiatCollateral
* Collateral that may need revenue hiding to become truly "up only"
*
* For: {tok} != {ref}, {ref} != {target}, {target} == {UoA}
* Inheritors _must_ implement underlyingRefPerTok()
* Can be easily extended by (optionally) re-implementing:
* - tryPrice()
* - refPerTok()
* - targetPerRef()
* - claimRewards()
* Should not have to re-implement any other methods.
* Should also set `oracleTimeout` in constructor if adding a new oracle source with timeout.
*
* Can intentionally disable default checks by setting config.defaultThreshold to 0
* Can intentionally do no revenue hiding by setting revenueHiding to 0
*/
abstract contract AppreciatingFiatCollateral is FiatCollateral {
using FixLib for uint192;
using OracleLib for AggregatorV3Interface;
// revenueShowing = FIX_ONE.minus(revenueHiding)
uint192 public immutable revenueShowing; // {1} The maximum fraction of refPerTok to show
// does not become nonzero until after first refresh()
uint192 internal exposedReferencePrice; // {ref/tok} max ref price observed, sub revenue hiding
/// @param config.chainlinkFeed Feed units: {UoA/ref}
/// @param revenueHiding {1} A value like 1e-6 that represents the maximum refPerTok to hide
constructor(CollateralConfig memory config, uint192 revenueHiding) FiatCollateral(config) {
require(revenueHiding < FIX_ONE, "revenueHiding out of range");
revenueShowing = FIX_ONE.minus(revenueHiding);
}
/// Can revert, used by other contract functions in order to catch errors
/// Should not return FIX_MAX for low
/// Should only return FIX_MAX for high if low is 0
/// Should NOT be manipulable by MEV
/// @dev Override this when pricing is more complicated than just a single oracle
/// @return low {UoA/tok} The low price estimate
/// @return high {UoA/tok} The high price estimate
/// @return pegPrice {target/ref} The actual price observed in the peg
function tryPrice()
external
view
virtual
override
returns (
uint192 low,
uint192 high,
uint192 pegPrice
)
{
// {target/ref} = {UoA/ref} / {UoA/target} (1)
pegPrice = chainlinkFeed.price(oracleTimeout);
// {UoA/tok} = {target/ref} * {ref/tok} * {UoA/target} (1)
uint192 p = pegPrice.mul(underlyingRefPerTok());
uint192 err = p.mul(oracleError, CEIL);
low = p - err;
high = p + err;
// assert(low <= high); obviously true just by inspection
}
/// Should not revert
/// Refresh exchange rates and update default status.
/// @dev Should not need to override: can handle collateral with variable refPerTok()
function refresh() public virtual override {
CollateralStatus oldStatus = status();
// Check for hard default
// must happen before tryPrice() call since `refPerTok()` returns a stored value
// revenue hiding: do not DISABLE if drawdown is small
try this.underlyingRefPerTok() returns (uint192 underlyingRefPerTok_) {
// {ref/tok} = {ref/tok} * {1}
uint192 hiddenReferencePrice = underlyingRefPerTok_.mul(revenueShowing);
// uint192(<) is equivalent to Fix.lt
if (underlyingRefPerTok_ < exposedReferencePrice) {
exposedReferencePrice = underlyingRefPerTok_;
markStatus(CollateralStatus.DISABLED);
} else if (hiddenReferencePrice > exposedReferencePrice) {
exposedReferencePrice = hiddenReferencePrice;
}
// Check for soft default + save prices
try this.tryPrice() returns (uint192 low, uint192 high, uint192 pegPrice) {
// {UoA/tok}, {UoA/tok}, {target/ref}
// (0, 0) is a valid price; (0, FIX_MAX) is unpriced
// Save prices if priced
if (high != FIX_MAX) {
savedLowPrice = low;
savedHighPrice = high;
savedPegPrice = pegPrice;
lastSave = uint48(block.timestamp);
} else {
// must be unpriced
assert(low == 0);
}
// If the price is below the default-threshold price, default eventually
// uint192(+/-) is the same as Fix.plus/minus
if (pegPrice < pegBottom || pegPrice > pegTop || low == 0) {
markStatus(CollateralStatus.IFFY);
} else {
markStatus(CollateralStatus.SOUND);
}
} catch (bytes memory errData) {
// see: docs/solidity-style.md#Catching-Empty-Data
if (errData.length == 0) revert(); // solhint-disable-line reason-string
markStatus(CollateralStatus.IFFY);
}
} catch (bytes memory errData) {
// see: docs/solidity-style.md#Catching-Empty-Data
if (errData.length == 0) revert(); // solhint-disable-line reason-string
markStatus(CollateralStatus.DISABLED);
}
CollateralStatus newStatus = status();
if (oldStatus != newStatus) {
emit CollateralStatusChanged(oldStatus, newStatus);
}
}
/// @return {ref/tok} Exposed quantity of whole reference units per whole collateral tokens
function refPerTok() public view virtual override returns (uint192) {
return exposedReferencePrice;
}
/// Should update in inheritors
/// @return {ref/tok} Actual quantity of whole reference units per whole collateral tokens
function underlyingRefPerTok() public view virtual returns (uint192);
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "../../interfaces/IAsset.sol";
import "./OracleLib.sol";
import "./VersionedAsset.sol";
contract Asset is IAsset, VersionedAsset {
using FixLib for uint192;
using OracleLib for AggregatorV3Interface;
uint192 public constant MAX_HIGH_PRICE_BUFFER = FIX_ONE; // {UoA/tok} 100%
AggregatorV3Interface public immutable chainlinkFeed; // {UoA/tok}
IERC20Metadata public immutable erc20;
uint8 public immutable erc20Decimals;
uint192 public immutable override maxTradeVolume; // {UoA}
uint48 public immutable oracleTimeout; // {s}
uint192 public immutable oracleError; // {1}
// === Lot price ===
uint48 public immutable priceTimeout; // {s} The period over which `savedHighPrice` decays to 0
uint192 public savedLowPrice; // {UoA/tok} The low price of the token during the last update
uint48 public lastSave; // {s} The timestamp when prices were last saved
uint192 public savedHighPrice; // {UoA/tok} The high price of the token during the last update
uint48 public maxOracleTimeout; // {s} maximum of all the oracle timeouts
/// @param priceTimeout_ {s} The number of seconds over which savedHighPrice decays to 0
/// @param chainlinkFeed_ Feed units: {UoA/tok}
/// @param oracleError_ {1} The % the oracle feed can be off by
/// @param maxTradeVolume_ {UoA} The max trade volume, in UoA
/// @param oracleTimeout_ {s} The number of seconds until the chainlinkFeed becomes invalid
constructor(
uint48 priceTimeout_,
AggregatorV3Interface chainlinkFeed_,
uint192 oracleError_,
IERC20Metadata erc20_,
uint192 maxTradeVolume_,
uint48 oracleTimeout_
) {
require(priceTimeout_ != 0, "price timeout zero");
require(address(chainlinkFeed_) != address(0), "missing chainlink feed");
require(oracleError_ != 0 && oracleError_ < FIX_ONE, "oracle error out of range");
require(address(erc20_) != address(0), "missing erc20");
require(maxTradeVolume_ != 0, "invalid max trade volume");
require(oracleTimeout_ != 0, "oracleTimeout zero");
priceTimeout = priceTimeout_;
chainlinkFeed = chainlinkFeed_;
oracleError = oracleError_;
erc20 = erc20_;
erc20Decimals = erc20.decimals();
maxTradeVolume = maxTradeVolume_;
oracleTimeout = oracleTimeout_;
maxOracleTimeout = oracleTimeout_; // must be kept current by each child class
}
/// Can revert, used by other contract functions in order to catch errors
/// Should not return FIX_MAX for low
/// Should only return FIX_MAX for high if low is 0
/// Should NOT be manipulable by MEV
/// @dev The third (unused) variable is only here for compatibility with Collateral
/// @return low {UoA/tok} The low price estimate
/// @return high {UoA/tok} The high price estimate
function tryPrice()
external
view
virtual
returns (
uint192 low,
uint192 high,
uint192
)
{
uint192 p = chainlinkFeed.price(oracleTimeout); // {UoA/tok}
uint192 err = p.mul(oracleError, CEIL);
// assert(low <= high); obviously true just by inspection
return (p - err, p + err, 0);
}
/// Should not revert
/// Refresh saved prices
function refresh() public virtual override {
try this.tryPrice() returns (uint192 low, uint192 high, uint192) {
// {UoA/tok}, {UoA/tok}
// (0, 0) is a valid price; (0, FIX_MAX) is unpriced
// Save prices if priced
if (high != FIX_MAX) {
savedLowPrice = low;
savedHighPrice = high;
lastSave = uint48(block.timestamp);
} else {
// must be unpriced
assert(low == 0);
}
} catch (bytes memory errData) {
// see: docs/solidity-style.md#Catching-Empty-Data
if (errData.length == 0) revert(); // solhint-disable-line reason-string
}
}
/// Should not revert
/// low should be nonzero if the asset could be worth selling
/// @dev Should be general enough to not need to be overridden
/// @return _low {UoA/tok} The lower end of the price estimate
/// @return _high {UoA/tok} The upper end of the price estimate
/// @notice If the price feed is broken, _low will decay downwards and _high will decay upwards
/// If tryPrice() is broken for `oracleTimeout + priceTimeout + ORACLE_TIMEOUT_BUFFER` ,
/// _low will be 0 and _high will be FIX_MAX.
/// Because the price decay begins at `oracleTimeout + ORACLE_TIMEOUT_BUFFER` seconds,
/// the price feed can be broken for up to `2 * oracleTimeout` seconds without
/// affecting the price estimate. This could happen if the Asset is refreshed just before
/// the maxOracleTimeout is reached, forcing a second period to pass before
/// the price begins to decay.
function price() public view virtual returns (uint192 _low, uint192 _high) {
try this.tryPrice() returns (uint192 low, uint192 high, uint192) {
// if the price feed is still functioning, use that
_low = low;
_high = high;
} catch (bytes memory errData) {
// see: docs/solidity-style.md#Catching-Empty-Data
if (errData.length == 0) revert(); // solhint-disable-line reason-string
// if the price feed is broken, decay _low downwards and _high upwards
uint48 delta = uint48(block.timestamp) - lastSave; // {s}
uint48 decayDelay = maxOracleTimeout + OracleLib.ORACLE_TIMEOUT_BUFFER;
if (delta <= decayDelay) {
// use saved prices for at least the decayDelay
_low = savedLowPrice;
_high = savedHighPrice;
} else if (delta >= decayDelay + priceTimeout) {
// unpriced after a full timeout
return (0, FIX_MAX);
} else {
// decayDelay <= delta <= decayDelay + priceTimeout
// Decay _high upwards to 2x savedHighPrice
// {UoA/tok} = {UoA/tok} * {1}
_high = savedHighPrice.safeMul(
FIX_ONE + MAX_HIGH_PRICE_BUFFER.muluDivu(delta - decayDelay, priceTimeout),
ROUND
); // during overflow should not revert
// if _high is FIX_MAX, leave at UNPRICED
if (_high != FIX_MAX) {
// Decay _low downwards from savedLowPrice to 0
// {UoA/tok} = {UoA/tok} * {1}
_low = savedLowPrice.muluDivu(decayDelay + priceTimeout - delta, priceTimeout);
// during overflow should revert since a FIX_MAX _low breaks everything
}
}
}
assert(_low <= _high);
}
/// Should not revert
/// lotLow should be nonzero when the asset might be worth selling
/// @dev Deprecated. Phased out in 3.1.0, but left on interface for backwards compatibility
/// @return lotLow {UoA/tok} The lower end of the lot price estimate
/// @return lotHigh {UoA/tok} The upper end of the lot price estimate
function lotPrice() external view virtual returns (uint192 lotLow, uint192 lotHigh) {
return price();
}
/// @return {tok} The balance of the ERC20 in whole tokens
function bal(address account) external view virtual returns (uint192) {
return shiftl_toFix(erc20.balanceOf(account), -int8(erc20Decimals), FLOOR);
}
/// @return If the asset is an instance of ICollateral or not
function isCollateral() external pure virtual returns (bool) {
return false;
}
// solhint-disable no-empty-blocks
/// Claim rewards earned by holding a balance of the ERC20 token
/// @custom:delegate-call
function claimRewards() external virtual {}
// solhint-enable no-empty-blocks
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity ^0.8.28;
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../../../interfaces/IRewardable.sol";
uint256 constant SHARE_DECIMAL_OFFSET = 9; // to prevent reward rounding issues
/**
* @title RewardableERC20
* @notice An abstract class that can be extended to create rewardable wrapper.
* @notice `_claimAssetRewards` keeps tracks of rewards by snapshotting the balance
* and calculating the difference between the current balance and the previous balance.
* Limitation: Currently supports only one single reward token.
* @dev To inherit:
* - override _claimAssetRewards()
* - call ERC20 constructor elsewhere during construction
*/
abstract contract RewardableERC20 is IRewardable, ERC20, ReentrancyGuard {
using SafeERC20 for IERC20;
uint256 public immutable one; // 1e9 * {qShare/share}
IERC20 public immutable rewardToken;
uint256 public rewardsPerShare; // 1e9 * {qRewards/share}
mapping(address => uint256) public lastRewardsPerShare; // 1e9 * {qRewards/share}
mapping(address => uint256) public accumulatedRewards; // {qRewards}
mapping(address => uint256) public claimedRewards; // {qRewards}
// Used to keep track of how many reward the Vault has accumulated
// Whenever _claimAndSyncRewards() is called we will calculate the difference
// between the current balance and `lastRewardBalance` to figure out how much to distribute
uint256 internal lastRewardBalance = 0;
/// @dev Extending class must ensure ERC20 constructor is called
constructor(IERC20 _rewardToken, uint8 _decimals) {
rewardToken = _rewardToken;
// set via pass-in to prevent inheritance issues
one = 10**(_decimals + SHARE_DECIMAL_OFFSET);
}
// claim rewards - Only supports one single reward token
function claimRewards() external nonReentrant {
_claimAndSyncRewards();
_syncAccount(msg.sender);
_claimAccountRewards(msg.sender);
}
function _syncAccount(address account) internal {
if (account == address(0)) return;
// 1e9 * {qRewards/share}
uint256 accountRewardsPerShare = lastRewardsPerShare[account];
// {qShare}
uint256 shares = balanceOf(account);
// {qRewards}
uint256 _accumulatedRewards = accumulatedRewards[account];
// 1e9 * {qRewards/share}
uint256 _rewardsPerShare = rewardsPerShare;
if (accountRewardsPerShare < _rewardsPerShare) {
// 1e9 * {qRewards/share}
uint256 delta = _rewardsPerShare - accountRewardsPerShare;
// {qRewards} = (1e9 * {qRewards/share}) * {qShare} / (1e9 * {qShare/share})
_accumulatedRewards += (delta * shares) / one;
}
lastRewardsPerShare[account] = _rewardsPerShare;
accumulatedRewards[account] = _accumulatedRewards;
}
function _rewardTokenBalance() internal view virtual returns (uint256) {
return rewardToken.balanceOf(address(this));
}
function _distributeReward(address account, uint256 amt) internal virtual {
rewardToken.safeTransfer(account, amt);
}
function _claimAndSyncRewards() internal virtual {
uint256 _totalSupply = totalSupply();
if (_totalSupply == 0) {
return;
}
_claimAssetRewards();
uint256 balanceAfterClaimingRewards = _rewardTokenBalance();
uint256 _rewardsPerShare = rewardsPerShare;
uint256 _previousBalance = lastRewardBalance;
if (balanceAfterClaimingRewards > _previousBalance) {
uint256 delta = balanceAfterClaimingRewards - _previousBalance; // {qRewards}
// 1e9 * {qRewards/share} = {qRewards} * (1e9 * {qShare/share}) / {qShare}
uint256 deltaPerShare = (delta * one) / _totalSupply;
// {qRewards} = {qRewards} + (1e9*(qRewards/share)) * {qShare} / (1e9*{qShare/share})
balanceAfterClaimingRewards = _previousBalance + (deltaPerShare * _totalSupply) / one;
// 1e9 * {qRewards/share} += {qRewards} * (1e9*{qShare/share}) / {qShare}
_rewardsPerShare += deltaPerShare;
}
lastRewardBalance = balanceAfterClaimingRewards;
rewardsPerShare = _rewardsPerShare;
}
function _claimAccountRewards(address account) internal {
uint256 claimableRewards = accumulatedRewards[account] - claimedRewards[account];
emit RewardsClaimed(IERC20(address(rewardToken)), claimableRewards);
if (claimableRewards == 0) {
return;
}
claimedRewards[account] = accumulatedRewards[account];
uint256 currentRewardTokenBalance = _rewardTokenBalance();
// This is just to handle the edge case where totalSupply() == 0 and there
// are still reward tokens in the contract.
uint256 nonDistributed = currentRewardTokenBalance > lastRewardBalance
? currentRewardTokenBalance - lastRewardBalance
: 0;
_distributeReward(account, claimableRewards);
currentRewardTokenBalance = _rewardTokenBalance();
lastRewardBalance = currentRewardTokenBalance > nonDistributed
? currentRewardTokenBalance - nonDistributed
: 0;
}
function _beforeTokenTransfer(
address from,
address to,
uint256
) internal virtual override {
_claimAndSyncRewards();
_syncAccount(from);
_syncAccount(to);
}
/// === Must override ===
function _claimAssetRewards() internal virtual;
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity ^0.8.28;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "../../../interfaces/IRewardable.sol";
import "../../../vendor/oz/ERC4626.sol";
import "./RewardableERC20.sol";
/**
* @title RewardableERC4626Vault
* @notice A transferrable ERC4626 vault wrapping an inner position that earns rewards.
* Holding the vault token for a period of time earns the holder the right to
* their prorata share of the global rewards earned during that time.
* @dev To inherit:
* - override _claimAssetRewards()
* - consider overriding _afterDeposit() and _beforeWithdraw()
*/
abstract contract RewardableERC4626Vault is ERC4626, RewardableERC20 {
// solhint-disable no-empty-blocks
constructor(
IERC20Metadata _asset,
string memory _name,
string memory _symbol,
ERC20 _rewardToken
)
ERC4626(_asset, _name, _symbol)
RewardableERC20(_rewardToken, _asset.decimals() + _decimalsOffset())
{
require(address(_rewardToken) != address(_asset), "reward and asset cannot match");
}
// solhint-enable no-empty-blocks
function decimals() public view virtual override(ERC4626, ERC20) returns (uint8) {
return ERC4626.decimals();
}
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual override(RewardableERC20, ERC20) {
RewardableERC20._beforeTokenTransfer(from, to, amount);
}
function _decimalsOffset() internal view virtual override returns (uint8) {
return 9;
}
/// === Must override ===
// function _claimAssetRewards() internal virtual;
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "../../interfaces/IAsset.sol";
import "../../libraries/Fixed.sol";
import "./Asset.sol";
import "./OracleLib.sol";
uint48 constant MAX_DELAY_UNTIL_DEFAULT = 1209600; // {s} 2 weeks
struct CollateralConfig {
uint48 priceTimeout; // {s} The number of seconds over which saved prices decay
AggregatorV3Interface chainlinkFeed; // Feed units: {target/ref}
uint192 oracleError; // {1} The % the oracle feed can be off by
IERC20Metadata erc20; // The ERC20 of the collateral token
uint192 maxTradeVolume; // {UoA} The max trade volume, in UoA
uint48 oracleTimeout; // {s} The number of seconds until a oracle value becomes invalid
bytes32 targetName; // The bytes32 representation of the target name
uint192 defaultThreshold; // {1} A value like 0.05 that represents a deviation tolerance
// set defaultThreshold to zero to create SelfReferentialCollateral
uint48 delayUntilDefault; // {s} The number of seconds an oracle can mulfunction
}
/**
* @title FiatCollateral
* Parent class for all collateral. Can be extended to support appreciating collateral
*
* For: {tok} == {ref}, {ref} != {target}, {target} == {UoA}
* Can be easily extended by (optionally) re-implementing:
* - tryPrice()
* - refPerTok()
* - targetPerRef()
* - claimRewards()
* If you have appreciating collateral, then you should use AppreciatingFiatCollateral or
* override refresh() yourself.
*
* Can intentionally disable default checks by setting config.defaultThreshold to 0
*/
contract FiatCollateral is ICollateral, Asset {
using FixLib for uint192;
using OracleLib for AggregatorV3Interface;
// Default Status:
// _whenDefault == NEVER: no risk of default (initial value)
// _whenDefault > block.timestamp: delayed default may occur as soon as block.timestamp.
// In this case, the asset may recover, reachiving _whenDefault == NEVER.
// _whenDefault <= block.timestamp: default has already happened (permanently)
uint48 private constant NEVER = type(uint48).max;
uint48 private _whenDefault = NEVER;
uint48 public immutable delayUntilDefault; // {s} e.g 86400
// targetName: The canonical name of this collateral's target unit.
bytes32 public immutable targetName;
uint192 public immutable pegBottom; // {target/ref} The bottom of the peg
uint192 public immutable pegTop; // {target/ref} The top of the peg
uint192 public savedPegPrice; // {target/ref} The peg price of the token during the last update
/// @param config.chainlinkFeed Feed units: {UoA/ref}
constructor(CollateralConfig memory config)
Asset(
config.priceTimeout,
config.chainlinkFeed,
config.oracleError,
config.erc20,
config.maxTradeVolume,
config.oracleTimeout
)
{
require(config.targetName != bytes32(0), "targetName missing");
if (config.defaultThreshold != 0) {
require(config.delayUntilDefault != 0, "delayUntilDefault zero");
}
require(config.delayUntilDefault <= 1209600, "delayUntilDefault too long");
// Note: This contract is designed to allow setting defaultThreshold = 0 to disable
// default checks. You can apply the check below to child contracts when required
// require(config.defaultThreshold != 0, "defaultThreshold zero");
targetName = config.targetName;
delayUntilDefault = config.delayUntilDefault;
// Cache constants
uint192 peg = targetPerRef(); // {target/ref}
// {target/ref} = {target/ref} * {1}
uint192 delta = peg.mul(config.defaultThreshold);
pegBottom = peg - delta;
pegTop = peg + delta;
}
/// Can revert, used by other contract functions in order to catch errors
/// Should not return FIX_MAX for low
/// Should only return FIX_MAX for high if low is 0
/// Should NOT be manipulable by MEV
/// @dev Override this when pricing is more complicated than just a single oracle
/// @return low {UoA/tok} The low price estimate
/// @return high {UoA/tok} The high price estimate
/// @return pegPrice {target/ref} The actual price observed in the peg
function tryPrice()
external
view
virtual
override
returns (
uint192 low,
uint192 high,
uint192 pegPrice
)
{
// {target/ref} = {UoA/ref} / {UoA/target} (1)
pegPrice = chainlinkFeed.price(oracleTimeout);
// {target/ref} = {target/ref} * {1}
uint192 err = pegPrice.mul(oracleError, CEIL);
low = pegPrice - err;
high = pegPrice + err;
// assert(low <= high); obviously true just by inspection
}
/// Should not revert
/// Refresh exchange rates and update default status.
/// @dev May need to override: limited to handling collateral with refPerTok() = 1
function refresh() public virtual override(Asset, IAsset) {
CollateralStatus oldStatus = status();
// Check for soft default + save price
try this.tryPrice() returns (uint192 low, uint192 high, uint192 pegPrice) {
// {UoA/tok}, {UoA/tok}, {target/ref}
// (0, 0) is a valid price; (0, FIX_MAX) is unpriced
// Save prices if priced
if (high != FIX_MAX) {
savedLowPrice = low;
savedHighPrice = high;
savedPegPrice = pegPrice;
lastSave = uint48(block.timestamp);
} else {
// must be unpriced
assert(low == 0);
}
// If the price is below the default-threshold price, default eventually
// uint192(+/-) is the same as Fix.plus/minus
if (pegPrice < pegBottom || pegPrice > pegTop || low == 0) {
markStatus(CollateralStatus.IFFY);
} else {
markStatus(CollateralStatus.SOUND);
}
} catch (bytes memory errData) {
// see: docs/solidity-style.md#Catching-Empty-Data
if (errData.length == 0) revert(); // solhint-disable-line reason-string
markStatus(CollateralStatus.IFFY);
}
CollateralStatus newStatus = status();
if (oldStatus != newStatus) {
emit CollateralStatusChanged(oldStatus, newStatus);
}
}
/// @return The collateral's status
function status() public view returns (CollateralStatus) {
if (_whenDefault == NEVER) {
return CollateralStatus.SOUND;
} else if (_whenDefault > block.timestamp) {
return CollateralStatus.IFFY;
} else {
return CollateralStatus.DISABLED;
}
}
// === Helpers for child classes ===
function markStatus(CollateralStatus status_) internal {
// untestable:
// All calls to markStatus happen exclusively if the collateral is not defaulted
if (_whenDefault <= block.timestamp) return; // prevent DISABLED -> SOUND/IFFY
if (status_ == CollateralStatus.SOUND) {
_whenDefault = NEVER;
} else if (status_ == CollateralStatus.IFFY) {
uint256 sum = block.timestamp + uint256(delayUntilDefault);
// untestable:
// constructor enforces max length on delayUntilDefault
if (sum >= NEVER) _whenDefault = NEVER;
else if (sum < _whenDefault) _whenDefault = uint48(sum);
// else: no change to _whenDefault
// untested:
// explicit `if` to check DISABLED. else branch will never be hit
} else if (status_ == CollateralStatus.DISABLED) {
_whenDefault = uint48(block.timestamp);
}
}
function whenDefault() external view returns (uint256) {
return _whenDefault;
}
// === End child helpers ===
/// @return {ref/tok} Quantity of whole reference units per whole collateral tokens
function refPerTok() public view virtual returns (uint192) {
return FIX_ONE;
}
/// @return {target/ref} Quantity of whole target units per whole reference unit in the peg
function targetPerRef() public view virtual returns (uint192) {
return FIX_ONE;
}
/// @return If the asset is an instance of ICollateral or not
function isCollateral() external pure virtual override(Asset, IAsset) returns (bool) {
return true;
}
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import { IERC20Metadata } from "../erc20/RewardableERC4626Vault.sol";
import { IERC4626 } from "../../../vendor/oz/IERC4626.sol";
interface IMorpho {
function supply(address _poolToken, uint256 _amount) external;
function supply(
address _poolToken,
address _onBehalf,
uint256 _amount
) external;
function withdraw(address _poolToken, uint256 _amount) external;
}
interface IMorphoRewardsDistributor {
function claim(
address _account,
uint256 _claimable,
bytes32[] calldata _proof
) external;
}
// Used by Morphos Aave V2 and Compound V2 vaults
interface IMorphoUsersLens {
function getCurrentSupplyBalanceInOf(address _poolToken, address _user)
external
view
returns (
uint256 balanceInP2P,
uint256 balanceOnPool,
uint256 totalBalance
);
}
interface IMorphoToken is IERC20Metadata {
function setPublicCapability(bytes4 functionSig, bool enabled) external;
function setUserRole(
address user,
uint8 role,
bool enabled
) external;
function setRoleCapability(
uint8 role,
bytes4 functionSig,
bool enabled
) external;
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC20, IERC20Metadata } from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
import { IMorpho, IMorphoUsersLens } from "./IMorpho.sol";
import { RewardableERC4626Vault } from "../erc20/RewardableERC4626Vault.sol";
struct MorphoTokenisedDepositConfig {
IMorpho morphoController;
IERC20Metadata underlyingERC20;
IERC20Metadata poolToken;
ERC20 rewardToken;
}
abstract contract MorphoTokenisedDeposit is RewardableERC4626Vault {
struct MorphoTokenisedDepositRewardsAccountingState {
uint256 totalAccumulatedBalance;
uint256 totalPaidOutBalance;
uint256 pendingBalance;
uint256 availableBalance;
uint256 remainingPeriod;
uint256 lastSync;
}
uint256 private constant PAYOUT_PERIOD = 7 days;
IMorpho public immutable morphoController;
address public immutable poolToken;
address public immutable underlying;
MorphoTokenisedDepositRewardsAccountingState private state;
constructor(MorphoTokenisedDepositConfig memory config)
RewardableERC4626Vault(
config.underlyingERC20,
string.concat("Tokenised Morpho Position - ", config.poolToken.name()),
string.concat("mrp-", config.poolToken.symbol()),
config.rewardToken
)
{
underlying = address(config.underlyingERC20);
morphoController = config.morphoController;
poolToken = address(config.poolToken);
state.lastSync = uint48(block.timestamp);
}
function sync() external {
_claimAndSyncRewards();
}
function _claimAssetRewards() internal override {
// If we detect any new balances add it to pending and reset payout period
uint256 totalAccumulated = state.totalPaidOutBalance + rewardToken.balanceOf(address(this));
uint256 newlyAccumulated = totalAccumulated - state.totalAccumulatedBalance;
uint256 timeDelta = block.timestamp - state.lastSync;
if (timeDelta != 0 && state.remainingPeriod != 0) {
if (timeDelta > state.remainingPeriod) {
timeDelta = state.remainingPeriod;
}
uint256 amtToPayOut = (state.pendingBalance * timeDelta) / state.remainingPeriod;
state.pendingBalance -= amtToPayOut;
state.availableBalance += amtToPayOut;
}
if (newlyAccumulated != 0) {
state.totalAccumulatedBalance = totalAccumulated;
state.pendingBalance += newlyAccumulated;
state.remainingPeriod = PAYOUT_PERIOD;
} else {
state.remainingPeriod = state.remainingPeriod < timeDelta
? 0
: state.remainingPeriod - timeDelta;
}
state.lastSync = block.timestamp;
}
function _rewardTokenBalance() internal view override returns (uint256) {
return state.availableBalance;
}
function _distributeReward(address account, uint256 amt) internal override {
state.totalPaidOutBalance += amt;
state.availableBalance -= amt;
SafeERC20.safeTransfer(rewardToken, account, amt);
}
function getMorphoPoolBalance(address poolToken) internal view virtual returns (uint256);
function totalAssets() public view virtual override returns (uint256) {
return getMorphoPoolBalance(poolToken);
}
function _deposit(
address caller,
address receiver,
uint256 assets,
uint256 shares
) internal virtual override {
SafeERC20.safeTransferFrom(IERC20(underlying), caller, address(this), assets);
SafeERC20.safeApprove(IERC20(underlying), address(morphoController), assets);
morphoController.supply(poolToken, assets);
_mint(receiver, shares);
emit Deposit(caller, receiver, assets, shares);
}
function _decimalsOffset() internal view virtual override returns (uint8) {
return 9;
}
function _withdraw(
address caller,
address receiver,
address owner,
uint256 assets,
uint256 shares
) internal virtual override {
if (caller != owner) {
_spendAllowance(owner, caller, shares);
}
morphoController.withdraw(poolToken, assets);
_burn(owner, shares);
SafeERC20.safeTransfer(IERC20(underlying), receiver, assets);
emit Withdraw(caller, receiver, owner, assets, shares);
}
}// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.28; // 0x19abf40e error StalePrice(); // 0x4dfba023 error InvalidPrice();
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "../../libraries/Fixed.sol";
import "./OracleErrors.sol";
interface EACAggregatorProxy {
function aggregator() external view returns (address);
}
/// Used by asset plugins to price their collateral
library OracleLib {
uint48 internal constant ORACLE_TIMEOUT_BUFFER = 300; // {s} 5 minutes
/// @dev Use for nested calls that should revert when there is a problem
/// @param timeout The number of seconds after which oracle values should be considered stale
/// @return {UoA/tok}
function price(AggregatorV3Interface chainlinkFeed, uint48 timeout)
internal
view
returns (uint192)
{
try chainlinkFeed.latestRoundData() returns (
uint80 roundId,
int256 p,
uint256,
uint256 updateTime,
uint80 answeredInRound
) {
if (updateTime == 0 || answeredInRound < roundId) {
revert StalePrice();
}
// Downcast is safe: uint256(-) reverts on underflow; block.timestamp assumed < 2^48
uint48 secondsSince = uint48(block.timestamp - updateTime);
if (secondsSince > timeout + ORACLE_TIMEOUT_BUFFER) revert StalePrice();
if (p <= 0) revert InvalidPrice();
// {UoA/tok}
return shiftl_toFix(uint256(p), -int8(chainlinkFeed.decimals()), FLOOR);
} catch (bytes memory errData) {
// Check if the aggregator was not set: if so, the chainlink feed has been deprecated
// and a _specific_ error needs to be raised in order to avoid looking like OOG
if (errData.length == 0) {
if (EACAggregatorProxy(address(chainlinkFeed)).aggregator() == address(0)) {
revert StalePrice();
}
// solhint-disable-next-line reason-string
revert();
}
// Otherwise, preserve the error bytes
// solhint-disable-next-line no-inline-assembly
assembly {
revert(add(32, errData), mload(errData))
}
}
}
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import "../../interfaces/IVersioned.sol";
// This value should be updated on each release
string constant ASSET_VERSION = "4.2.0";
/**
* @title VersionedAsset
* @notice A mix-in to track semantic versioning uniformly across asset plugin contracts.
*/
abstract contract VersionedAsset is IVersioned {
function version() public pure virtual override returns (string memory) {
return ASSET_VERSION;
}
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { VersionRegistry } from "./VersionRegistry.sol";
import { RoleRegistry } from "./RoleRegistry.sol";
/**
* @title Asset Plugin Registry
* @notice A tiny contract for tracking asset plugins
*/
contract AssetPluginRegistry {
VersionRegistry public versionRegistry;
RoleRegistry public roleRegistry;
// versionHash => asset => isValid
mapping(bytes32 => mapping(address => bool)) private _isValidAsset;
mapping(address => bool) public isDeprecated;
error AssetPluginRegistry__InvalidAsset();
error AssetPluginRegistry__InvalidCaller();
error AssetPluginRegistry__InvalidVersion();
error AssetPluginRegistry__LengthMismatch();
event AssetPluginRegistryUpdated(bytes32 versionHash, address asset, bool validity);
constructor(address _versionRegistry) {
versionRegistry = VersionRegistry(_versionRegistry);
roleRegistry = versionRegistry.roleRegistry();
}
function registerAsset(address _asset, bytes32[] calldata validForVersions) external {
if (!roleRegistry.isOwner(msg.sender)) {
revert AssetPluginRegistry__InvalidCaller();
}
if (_asset == address(0)) {
revert AssetPluginRegistry__InvalidAsset();
}
for (uint256 i = 0; i < validForVersions.length; ++i) {
bytes32 versionHash = validForVersions[i];
if (address(versionRegistry.deployments(versionHash)) == address(0)) {
revert AssetPluginRegistry__InvalidVersion();
}
_isValidAsset[versionHash][_asset] = true;
emit AssetPluginRegistryUpdated(versionHash, _asset, true);
}
}
function updateVersionsByAsset(
address _asset,
bytes32[] calldata _versionHashes,
bool[] calldata _validities
) external {
if (!roleRegistry.isOwner(msg.sender)) {
revert AssetPluginRegistry__InvalidCaller();
}
if (_versionHashes.length != _validities.length) {
revert AssetPluginRegistry__LengthMismatch();
}
if (_asset == address(0)) {
revert AssetPluginRegistry__InvalidAsset();
}
for (uint256 i = 0; i < _versionHashes.length; ++i) {
bytes32 versionHash = _versionHashes[i];
if (address(versionRegistry.deployments(versionHash)) == address(0)) {
revert AssetPluginRegistry__InvalidVersion();
}
_isValidAsset[versionHash][_asset] = _validities[i];
emit AssetPluginRegistryUpdated(versionHash, _asset, _validities[i]);
}
}
function updateAssetsByVersion(
bytes32 _versionHash,
address[] calldata _assets,
bool[] calldata _validities
) external {
if (!roleRegistry.isOwner(msg.sender)) {
revert AssetPluginRegistry__InvalidCaller();
}
if (_assets.length != _validities.length) {
revert AssetPluginRegistry__LengthMismatch();
}
if (address(versionRegistry.deployments(_versionHash)) == address(0)) {
revert AssetPluginRegistry__InvalidVersion();
}
for (uint256 i = 0; i < _assets.length; ++i) {
address asset = _assets[i];
if (asset == address(0)) {
revert AssetPluginRegistry__InvalidAsset();
}
_isValidAsset[_versionHash][asset] = _validities[i];
emit AssetPluginRegistryUpdated(_versionHash, asset, _validities[i]);
}
}
function deprecateAsset(address _asset) external {
if (!roleRegistry.isOwnerOrEmergencyCouncil(msg.sender)) {
revert AssetPluginRegistry__InvalidCaller();
}
isDeprecated[_asset] = true;
}
function isValidAsset(bytes32 _versionHash, address _asset) external view returns (bool) {
if (!isDeprecated[_asset]) {
return _isValidAsset[_versionHash][_asset];
}
return false;
}
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import { RoleRegistry } from "./RoleRegistry.sol";
uint256 constant MAX_FEE_NUMERATOR = 15_00; // Max DAO Fee: 15%
uint256 constant FEE_DENOMINATOR = 100_00;
contract DAOFeeRegistry {
RoleRegistry public roleRegistry;
address private feeRecipient;
uint256 private defaultFeeNumerator; // 0%
mapping(address => uint256) private rTokenFeeNumerator;
mapping(address => bool) private rTokenFeeSet;
error DAOFeeRegistry__FeeRecipientAlreadySet();
error DAOFeeRegistry__InvalidFeeRecipient();
error DAOFeeRegistry__InvalidFeeNumerator();
error DAOFeeRegistry__InvalidRoleRegistry();
error DAOFeeRegistry__InvalidCaller();
event FeeRecipientSet(address indexed feeRecipient);
event DefaultFeeNumeratorSet(uint256 defaultFeeNumerator);
event RTokenFeeNumeratorSet(address indexed rToken, uint256 feeNumerator, bool isActive);
modifier onlyOwner() {
if (!roleRegistry.isOwner(msg.sender)) {
revert DAOFeeRegistry__InvalidCaller();
}
_;
}
constructor(RoleRegistry _roleRegistry, address _feeRecipient) {
if (address(_roleRegistry) == address(0)) {
revert DAOFeeRegistry__InvalidRoleRegistry();
}
roleRegistry = _roleRegistry;
feeRecipient = _feeRecipient;
}
function setFeeRecipient(address feeRecipient_) external onlyOwner {
if (feeRecipient_ == address(0)) {
revert DAOFeeRegistry__InvalidFeeRecipient();
}
if (feeRecipient_ == feeRecipient) {
revert DAOFeeRegistry__FeeRecipientAlreadySet();
}
feeRecipient = feeRecipient_;
emit FeeRecipientSet(feeRecipient_);
}
function setDefaultFeeNumerator(uint256 feeNumerator_) external onlyOwner {
if (feeNumerator_ > MAX_FEE_NUMERATOR) {
revert DAOFeeRegistry__InvalidFeeNumerator();
}
defaultFeeNumerator = feeNumerator_;
emit DefaultFeeNumeratorSet(defaultFeeNumerator);
}
/// @dev A fee below 1% not recommended due to poor precision in the Distributor
function setRTokenFeeNumerator(address rToken, uint256 feeNumerator_) external onlyOwner {
if (feeNumerator_ > MAX_FEE_NUMERATOR) {
revert DAOFeeRegistry__InvalidFeeNumerator();
}
rTokenFeeNumerator[rToken] = feeNumerator_;
rTokenFeeSet[rToken] = true;
emit RTokenFeeNumeratorSet(rToken, feeNumerator_, true);
}
function resetRTokenFee(address rToken) external onlyOwner {
rTokenFeeNumerator[rToken] = 0;
rTokenFeeSet[rToken] = false;
emit RTokenFeeNumeratorSet(rToken, 0, false);
}
function getFeeDetails(address rToken)
external
view
returns (
address recipient,
uint256 feeNumerator,
uint256 feeDenominator
)
{
recipient = feeRecipient;
feeNumerator = rTokenFeeSet[rToken] ? rTokenFeeNumerator[rToken] : defaultFeeNumerator;
feeDenominator = FEE_DENOMINATOR;
}
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
// solhint-disable-next-line max-line-length
import { AccessControlEnumerable } from "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
/**
* @title RoleRegistry
* @notice Contract to manage roles for RToken <> DAO interactions
*/
contract RoleRegistry is AccessControlEnumerable {
bytes32 public constant EMERGENCY_COUNCIL = keccak256("EMERGENCY_COUNCIL");
constructor() {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function isOwner(address account) public view returns (bool) {
return hasRole(DEFAULT_ADMIN_ROLE, account);
}
function isEmergencyCouncil(address account) public view returns (bool) {
return hasRole(EMERGENCY_COUNCIL, account);
}
function isOwnerOrEmergencyCouncil(address account) public view returns (bool) {
return hasRole(DEFAULT_ADMIN_ROLE, account) || hasRole(EMERGENCY_COUNCIL, account);
}
}// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.28;
import { IDeployer, Implementations } from "../interfaces/IDeployer.sol";
import { RoleRegistry } from "./RoleRegistry.sol";
/**
* @title VersionRegistry
* @notice A tiny contract for tracking deployment versions
* All versions registered are expected to be >=4.0.0
*/
contract VersionRegistry {
mapping(bytes32 => IDeployer) public deployments;
mapping(bytes32 => bool) public isDeprecated;
bytes32 private latestVersion;
RoleRegistry public roleRegistry;
error VersionRegistry__ZeroAddress();
error VersionRegistry__InvalidRegistration();
error VersionRegistry__AlreadyDeprecated();
error VersionRegistry__InvalidRoleRegistry();
error VersionRegistry__InvalidCaller();
event VersionRegistered(bytes32 versionHash, IDeployer deployer);
event VersionDeprecated(bytes32 versionHash);
constructor(RoleRegistry _roleRegistry) {
if (address(_roleRegistry) == address(0)) {
revert VersionRegistry__ZeroAddress();
}
roleRegistry = _roleRegistry;
}
/// Register a deployer address, keyed by version.
/// @param deployer The deployer contract address for the version to be added.
function registerVersion(IDeployer deployer) external {
if (!roleRegistry.isOwner(msg.sender)) {
revert VersionRegistry__InvalidCaller();
}
if (address(deployer) == address(0)) {
revert VersionRegistry__ZeroAddress();
}
string memory version = deployer.version();
bytes32 versionHash = keccak256(abi.encodePacked(version));
if (address(deployments[versionHash]) != address(0)) {
revert VersionRegistry__InvalidRegistration();
}
deployments[versionHash] = deployer;
latestVersion = versionHash;
emit VersionRegistered(versionHash, deployer);
}
function deprecateVersion(bytes32 versionHash) external {
if (!roleRegistry.isOwnerOrEmergencyCouncil(msg.sender)) {
revert VersionRegistry__InvalidCaller();
}
if (isDeprecated[versionHash]) {
revert VersionRegistry__AlreadyDeprecated();
}
isDeprecated[versionHash] = true;
emit VersionDeprecated(versionHash);
}
function getLatestVersion()
external
view
returns (
bytes32 versionHash,
string memory version,
IDeployer deployer,
bool deprecated
)
{
versionHash = latestVersion;
deployer = deployments[versionHash];
version = deployer.version();
deprecated = isDeprecated[versionHash];
}
function getImplementationForVersion(bytes32 versionHash)
external
view
returns (Implementations memory)
{
return deployments[versionHash].implementations();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.1) (token/ERC20/extensions/ERC4626.sol)
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./IERC4626.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
/**
* @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in
* https://eips.ethereum.org/EIPS/eip-4626[EIP-4626].
*
* This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for
* underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends
* the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this
* contract and not the "assets" token which is an independent contract.
*
* [CAUTION]
* ====
* In empty (or nearly empty) ERC-4626 vaults, deposits are at high risk of being stolen through frontrunning
* with a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation
* attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial
* deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may
* similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by
* verifying the amount received is as expected, using a wrapper that performs these checks such as
* https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router].
*
* Since v4.9, this implementation uses virtual assets and shares to mitigate that risk. The `_decimalsOffset()`
* corresponds to an offset in the decimal representation between the underlying asset's decimals and the vault
* decimals. This offset also determines the rate of virtual shares to virtual assets in the vault, which itself
* determines the initial exchange rate. While not fully preventing the attack, analysis shows that the default offset
* (0) makes it non-profitable, as a result of the value being captured by the virtual shares (out of the attacker's
* donation) matching the attacker's expected gains. With a larger offset, the attack becomes orders of magnitude more
* expensive than it is profitable. More details about the underlying math can be found
* xref:erc4626.adoc#inflation-attack[here].
*
* The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued
* to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets
* will cause the first user to exit to experience reduced losses in detriment to the last users that will experience
* bigger losses. Developers willing to revert back to the pre-v4.9 behavior just need to override the
* `_convertToShares` and `_convertToAssets` functions.
*
* To learn more, check out our xref:ROOT:erc4626.adoc[ERC-4626 guide].
* ====
*
* _Available since v4.7._
*
* code taken from openzeppelin-contracts, commit hash: eedca5d
*/
abstract contract ERC4626 is ERC20, IERC4626 {
using Math for uint256;
IERC20 private immutable _asset;
uint8 private immutable _underlyingDecimals;
/**
* @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777).
*/
constructor(
IERC20 asset_,
string memory _name,
string memory _symbol
) ERC20(_name, _symbol) {
(bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);
_underlyingDecimals = success ? assetDecimals : 18;
_asset = asset_;
}
/**
* @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way.
*/
function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool, uint8) {
(bool success, bytes memory encodedDecimals) = address(asset_).staticcall(
abi.encodeWithSelector(IERC20Metadata.decimals.selector)
);
if (success && encodedDecimals.length >= 32) {
uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));
if (returnedDecimals <= type(uint8).max) {
return (true, uint8(returnedDecimals));
}
}
return (false, 0);
}
/**
* @dev Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This
* "original" value is cached during construction of the vault contract. If this read operation fails (e.g., the
* asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals.
*
* See {IERC20Metadata-decimals}.
*/
function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) {
return _underlyingDecimals + _decimalsOffset();
}
/** @dev See {IERC4626-asset}. */
function asset() public view virtual override returns (address) {
return address(_asset);
}
/** @dev See {IERC4626-totalAssets}. */
function totalAssets() public view virtual override returns (uint256) {
return _asset.balanceOf(address(this));
}
/** @dev See {IERC4626-convertToShares}. */
function convertToShares(uint256 assets) public view virtual override returns (uint256) {
return _convertToShares(assets, Math.Rounding.Down);
}
/** @dev See {IERC4626-convertToAssets}. */
function convertToAssets(uint256 shares) public view virtual override returns (uint256) {
return _convertToAssets(shares, Math.Rounding.Down);
}
/** @dev See {IERC4626-maxDeposit}. */
function maxDeposit(address) public view virtual override returns (uint256) {
return type(uint256).max;
}
/** @dev See {IERC4626-maxMint}. */
function maxMint(address) public view virtual override returns (uint256) {
return type(uint256).max;
}
/** @dev See {IERC4626-maxWithdraw}. */
function maxWithdraw(address owner) public view virtual override returns (uint256) {
return _convertToAssets(balanceOf(owner), Math.Rounding.Down);
}
/** @dev See {IERC4626-maxRedeem}. */
function maxRedeem(address owner) public view virtual override returns (uint256) {
return balanceOf(owner);
}
/** @dev See {IERC4626-previewDeposit}. */
function previewDeposit(uint256 assets) public view virtual override returns (uint256) {
return _convertToShares(assets, Math.Rounding.Down);
}
/** @dev See {IERC4626-previewMint}. */
function previewMint(uint256 shares) public view virtual override returns (uint256) {
return _convertToAssets(shares, Math.Rounding.Up);
}
/** @dev See {IERC4626-previewWithdraw}. */
function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {
return _convertToShares(assets, Math.Rounding.Up);
}
/** @dev See {IERC4626-previewRedeem}. */
function previewRedeem(uint256 shares) public view virtual override returns (uint256) {
return _convertToAssets(shares, Math.Rounding.Down);
}
/** @dev See {IERC4626-deposit}. */
function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {
require(assets <= maxDeposit(receiver), "ERC4626: deposit more than max");
uint256 shares = previewDeposit(assets);
_deposit(_msgSender(), receiver, assets, shares);
return shares;
}
/** @dev See {IERC4626-mint}.
*
* As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero.
* In this case, the shares will be minted without requiring any assets to be deposited.
*/
function mint(uint256 shares, address receiver) public virtual override returns (uint256) {
require(shares <= maxMint(receiver), "ERC4626: mint more than max");
uint256 assets = previewMint(shares);
_deposit(_msgSender(), receiver, assets, shares);
return assets;
}
/** @dev See {IERC4626-withdraw}. */
function withdraw(
uint256 assets,
address receiver,
address owner
) public virtual override returns (uint256) {
require(assets <= maxWithdraw(owner), "ERC4626: withdraw more than max");
uint256 shares = previewWithdraw(assets);
_withdraw(_msgSender(), receiver, owner, assets, shares);
return shares;
}
/** @dev See {IERC4626-redeem}. */
function redeem(
uint256 shares,
address receiver,
address owner
) public virtual override returns (uint256) {
require(shares <= maxRedeem(owner), "ERC4626: redeem more than max");
uint256 assets = previewRedeem(shares);
_withdraw(_msgSender(), receiver, owner, assets, shares);
return assets;
}
/**
* @dev Internal conversion function (from assets to shares) with support for rounding direction.
*/
function _convertToShares(uint256 assets, Math.Rounding rounding)
internal
view
virtual
returns (uint256)
{
return assets.mulDiv(totalSupply() + 10**_decimalsOffset(), totalAssets() + 1, rounding);
}
/**
* @dev Internal conversion function (from shares to assets) with support for rounding direction.
*/
function _convertToAssets(uint256 shares, Math.Rounding rounding)
internal
view
virtual
returns (uint256)
{
return shares.mulDiv(totalAssets() + 1, totalSupply() + 10**_decimalsOffset(), rounding);
}
/**
* @dev Deposit/mint common workflow.
*/
function _deposit(
address caller,
address receiver,
uint256 assets,
uint256 shares
) internal virtual {
// If _asset is ERC777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the
// `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
// calls the vault, which is assumed not malicious.
//
// Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the
// assets are transferred and before the shares are minted, which is a valid state.
// slither-disable-next-line reentrancy-no-eth
// slither-disable-next-line arbitrary-send-erc20
SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);
_mint(receiver, shares);
emit Deposit(caller, receiver, assets, shares);
}
/**
* @dev Withdraw/redeem common workflow.
*/
function _withdraw(
address caller,
address receiver,
address owner,
uint256 assets,
uint256 shares
) internal virtual {
if (caller != owner) {
_spendAllowance(owner, caller, shares);
}
// If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the
// `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,
// calls the vault, which is assumed not malicious.
//
// Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the
// shares are burned and after the assets are transferred, which is a valid state.
_burn(owner, shares);
SafeERC20.safeTransfer(_asset, receiver, assets);
emit Withdraw(caller, receiver, owner, assets, shares);
}
function _decimalsOffset() internal view virtual returns (uint8) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (interfaces/IERC4626.sol)
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
* https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
*
* _Available since v4.7._
*/
//slither-disable-next-line name-reused
interface IERC4626 is IERC20, IERC20Metadata {
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed sender,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
/**
* @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
*
* - MUST be an ERC-20 token contract.
* - MUST NOT revert.
*/
function asset() external view returns (address assetTokenAddress);
/**
* @dev Returns the total amount of the underlying asset that is “managed” by Vault.
*
* - SHOULD include any compounding that occurs from yield.
* - MUST be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT revert.
*/
function totalAssets() external view returns (uint256 totalManagedAssets);
/**
* @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
* through a deposit call.
*
* - MUST return a limited value if receiver is subject to some deposit limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
* - MUST NOT revert.
*/
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
* call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
* in the same transaction.
* - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
* deposit would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/**
* @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* deposit execution, and are accounted for during deposit.
* - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
* - MUST return a limited value if receiver is subject to some mint limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
* - MUST NOT revert.
*/
function maxMint(address receiver) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
* in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
* same transaction.
* - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
* would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by minting.
*/
function previewMint(uint256 shares) external view returns (uint256 assets);
/**
* @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
* execution, and are accounted for during mint.
* - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function mint(uint256 shares, address receiver) external returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
* call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
* called
* in the same transaction.
* - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
* the withdrawal would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/**
* @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* withdraw execution, and are accounted for during withdraw.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function withdraw(
uint256 assets,
address receiver,
address owner
) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
* through a redeem call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxRedeem(address owner) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
* in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
* same transaction.
* - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
* redemption would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by redeeming.
*/
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/**
* @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* redeem execution, and are accounted for during redeem.
* - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function redeem(
uint256 shares,
address receiver,
address owner
) external returns (uint256 assets);
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"components":[{"internalType":"uint48","name":"priceTimeout","type":"uint48"},{"internalType":"contract AggregatorV3Interface","name":"chainlinkFeed","type":"address"},{"internalType":"uint192","name":"oracleError","type":"uint192"},{"internalType":"contract IERC20Metadata","name":"erc20","type":"address"},{"internalType":"uint192","name":"maxTradeVolume","type":"uint192"},{"internalType":"uint48","name":"oracleTimeout","type":"uint48"},{"internalType":"bytes32","name":"targetName","type":"bytes32"},{"internalType":"uint192","name":"defaultThreshold","type":"uint192"},{"internalType":"uint48","name":"delayUntilDefault","type":"uint48"}],"internalType":"struct CollateralConfig","name":"config","type":"tuple"},{"internalType":"uint192","name":"revenueHiding","type":"uint192"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidPrice","type":"error"},{"inputs":[],"name":"StalePrice","type":"error"},{"inputs":[],"name":"UIntOutOfBounds","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"enum CollateralStatus","name":"oldStatus","type":"uint8"},{"indexed":true,"internalType":"enum CollateralStatus","name":"newStatus","type":"uint8"}],"name":"CollateralStatusChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"erc20","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsClaimed","type":"event"},{"inputs":[],"name":"MAX_HIGH_PRICE_BUFFER","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"bal","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chainlinkFeed","outputs":[{"internalType":"contract AggregatorV3Interface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"delayUntilDefault","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"erc20","outputs":[{"internalType":"contract IERC20Metadata","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"erc20Decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isCollateral","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"lastSave","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lotPrice","outputs":[{"internalType":"uint192","name":"lotLow","type":"uint192"},{"internalType":"uint192","name":"lotHigh","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxOracleTimeout","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTradeVolume","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracleError","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracleTimeout","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pegBottom","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pegTop","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price","outputs":[{"internalType":"uint192","name":"_low","type":"uint192"},{"internalType":"uint192","name":"_high","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceTimeout","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"refPerTok","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"refresh","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revenueShowing","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"savedHighPrice","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"savedLowPrice","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"savedPegPrice","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"status","outputs":[{"internalType":"enum CollateralStatus","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"targetName","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"targetPerRef","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tryPrice","outputs":[{"internalType":"uint192","name":"low","type":"uint192"},{"internalType":"uint192","name":"high","type":"uint192"},{"internalType":"uint192","name":"pegPrice","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"underlyingRefPerTok","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"contract MorphoTokenisedDeposit","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"whenDefault","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6102806040526002805465ffffffffffff191665ffffffffffff17905534801561002857600080fd5b50604051612bf6380380612bf68339810160408190526100479161097e565b818181806000015181602001518260400151836060015184608001518560a001518565ffffffffffff166000036100ba5760405162461bcd60e51b815260206004820152601260248201527170726963652074696d656f7574207a65726f60701b60448201526064015b60405180910390fd5b6001600160a01b0385166101105760405162461bcd60e51b815260206004820152601660248201527f6d697373696e6720636861696e6c696e6b20666565640000000000000000000060448201526064016100b1565b6001600160c01b038416158015906101385750670de0b6b3a76400006001600160c01b038516105b6101845760405162461bcd60e51b815260206004820152601960248201527f6f7261636c65206572726f72206f7574206f662072616e67650000000000000060448201526064016100b1565b6001600160a01b0383166101ca5760405162461bcd60e51b815260206004820152600d60248201526c06d697373696e6720657263323609c1b60448201526064016100b1565b816001600160c01b03166000036102235760405162461bcd60e51b815260206004820152601860248201527f696e76616c6964206d617820747261646520766f6c756d65000000000000000060448201526064016100b1565b8065ffffffffffff166000036102705760405162461bcd60e51b81526020600482015260126024820152716f7261636c6554696d656f7574207a65726f60701b60448201526064016100b1565b65ffffffffffff8616610140526001600160a01b038086166080526001600160c01b03851661012052831660a08190526040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa1580156102db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102ff9190610a51565b60ff1660c09081526001600160c01b039290921660e05265ffffffffffff166101008190526001805465ffffffffffff60c01b1916600160c01b909202919091179055850151935061038c925050505760405162461bcd60e51b81526020600482015260126024820152717461726765744e616d65206d697373696e6760701b60448201526064016100b1565b60e08101516001600160c01b0316156103fc5780610100015165ffffffffffff166000036103fc5760405162461bcd60e51b815260206004820152601660248201527f64656c6179556e74696c44656661756c74207a65726f0000000000000000000060448201526064016100b1565b6212750081610100015165ffffffffffff16111561045c5760405162461bcd60e51b815260206004820152601a60248201527f64656c6179556e74696c44656661756c7420746f6f206c6f6e6700000000000060448201526064016100b1565b60c08101516101805261010081015165ffffffffffff166101605260e0810151670de0b6b3a7640000906000906104949083906107c0565b90506104a08183610a8a565b6001600160c01b03166101a0526104b78183610aa9565b6001600160c01b039081166101c052670de0b6b3a76400009085161092506105249150505760405162461bcd60e51b815260206004820152601a60248201527f726576656e7565486964696e67206f7574206f662072616e676500000000000060448201526064016100b1565b610536670de0b6b3a7640000826107d7565b6001600160c01b039081166101e05260e08501511615915061059c90505760405162461bcd60e51b815260206004820152601f60248201527f64656661756c74207468726573686f6c64206e6f7420737570706f727465640060448201526064016100b1565b60608201516001600160a01b03166105e65760405162461bcd60e51b815260206004820152600d60248201526c06d697373696e6720657263323609c1b60448201526064016100b1565b60608201516001600160a01b03166102008190526040805163f7c618c160e01b8152905163f7c618c1916004808201926020929091908290030181865afa158015610635573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106599190610ac8565b6001600160a01b0316610220816001600160a01b031681525050610200516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106d89190610a51565b6106e390600a610bcc565b6102408181525050610200516001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561072c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107509190610ac8565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561078d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b19190610a51565b60000b6102605250610c729050565b60006107ce838360016107e3565b90505b92915050565b60006107ce8284610a8a565b60006108156108106108016001600160c01b03808716908816610bdb565b670de0b6b3a76400008561081f565b6108c4565b90505b9392505050565b60008061082c8486610c08565b9050600083600281111561084257610842610c1c565b0361084e579050610818565b600183600281111561086257610862610c1c565b036108a1576002610874600186610c32565b61087e9190610c08565b6108888587610c45565b111561089c578061089881610c59565b9150505b610815565b6108ab8486610c45565b1561081557806108ba81610c59565b9695505050505050565b60006001600160c01b038211156108ee5760405163f44398f560e01b815260040160405180910390fd5b5090565b60405161012081016001600160401b038111828210171561092357634e487b7160e01b600052604160045260246000fd5b60405290565b805165ffffffffffff8116811461093f57600080fd5b919050565b6001600160a01b038116811461095957600080fd5b50565b805161093f81610944565b80516001600160c01b038116811461093f57600080fd5b60008082840361014081121561099357600080fd5b6101208112156109a257600080fd5b506109ab6108f2565b6109b484610929565b81526109c26020850161095c565b60208201526109d360408501610967565b60408201526109e46060850161095c565b60608201526109f560808501610967565b6080820152610a0660a08501610929565b60a082015260c08481015190820152610a2160e08501610967565b60e0820152610a336101008501610929565b6101008201529150610a486101208401610967565b90509250929050565b600060208284031215610a6357600080fd5b815160ff8116811461081857600080fd5b634e487b7160e01b600052601160045260246000fd5b6001600160c01b0382811682821603908111156107d1576107d1610a74565b6001600160c01b0381811683821601908111156107d1576107d1610a74565b600060208284031215610ada57600080fd5b815161081881610944565b6001815b6001841115610b2057808504811115610b0457610b04610a74565b6001841615610b1257908102905b60019390931c928002610ae9565b935093915050565b600082610b37575060016107d1565b81610b44575060006107d1565b8160018114610b5a5760028114610b6457610b80565b60019150506107d1565b60ff841115610b7557610b75610a74565b50506001821b6107d1565b5060208310610133831016604e8410600b8410161715610ba3575081810a6107d1565b610bb06000198484610ae5565b8060001904821115610bc457610bc4610a74565b029392505050565b60006107ce60ff841683610b28565b80820281158282048414176107d1576107d1610a74565b634e487b7160e01b600052601260045260246000fd5b600082610c1757610c17610bf2565b500490565b634e487b7160e01b600052602160045260246000fd5b818103818111156107d1576107d1610a74565b600082610c5457610c54610bf2565b500690565b600060018201610c6b57610c6b610a74565b5060010190565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051611e57610d9f6000396000610699015260006105f4015260008181610735015261083001526000818161059c01526106290152600081816103590152610de50152600081816104ab0152610ffd0152600081816104d20152610fc2015260006105c301526000818161056d015261161c01526000818161025c01528181610b9a01528181610c1001528181610c760152610cbf0152600081816104f90152610a2f01526000818161048401526109e9015260006104180152600081816102cd015261097f015260008181610380015281816107ac015261090f0152600081816103f101526109c70152611e576000f3fe608060405234801561001057600080fd5b50600436106101f05760003560e01c806396f80ae91161010f578063c59b3d63116100a2578063e6a1505311610071578063e6a1505314610568578063f8ac93e81461058f578063fbfa77cf14610597578063fdfd5917146105be57600080fd5b8063c59b3d631461051b578063d77381db1461052a578063d9e8e67014610547578063ddc0c7c81461055557600080fd5b8063abfeece5116100de578063abfeece51461047f578063ae4e1875146104a6578063b94d8739146104cd578063c55f29d1146104f457600080fd5b806396f80ae91461043a5780639ec0727214610453578063a035b1fe14610466578063a7fa0faf1461046e57600080fd5b806354fd4d5011610187578063785e9e8611610156578063785e9e861461037b5780637d1ea137146103ba5780637dbdf1f5146103ec57806395acc4ae1461041357600080fd5b806354fd4d50146103015780635580f72a14610328578063701619e71461033b57806374b629541461035457600080fd5b80632dc86624116101c35780632dc8662414610295578063300105da146102af578063372500ab146102be5780633cb5d379146102c857600080fd5b806305169ebe146101f5578063200d2ed21461021a578063271181ec1461022f5780632bcfaa8014610257575b600080fd5b6101fd6105e5565b6040516001600160c01b0390911681526020015b60405180910390f35b6102226106c9565b60405161021191906119b2565b61023761070a565b604080516001600160c01b03938416815292909116602083015201610211565b61027e7f000000000000000000000000000000000000000000000000000000000000000081565b60405165ffffffffffff9091168152602001610211565b60025465ffffffffffff165b604051908152602001610211565b6101fd670de0b6b3a764000081565b6102c661071d565b005b6102ef7f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610211565b60408051808201825260058152640342e322e360dc1b6020820152905161021191906119da565b6101fd610336366004611a3d565b6108e9565b60015461027e90600160c01b900465ffffffffffff1681565b6101fd7f000000000000000000000000000000000000000000000000000000000000000081565b6103a27f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610211565b6103c26109a9565b604080516001600160c01b0394851681529284166020840152921691810191909152606001610211565b6103a27f000000000000000000000000000000000000000000000000000000000000000081565b6101fd7f000000000000000000000000000000000000000000000000000000000000000081565b60005461027e90600160c01b900465ffffffffffff1681565b6001546101fd906001600160c01b031681565b610237610a80565b6003546001600160c01b03166101fd565b61027e7f000000000000000000000000000000000000000000000000000000000000000081565b6101fd7f000000000000000000000000000000000000000000000000000000000000000081565b6101fd7f000000000000000000000000000000000000000000000000000000000000000081565b6101fd7f000000000000000000000000000000000000000000000000000000000000000081565b60405160018152602001610211565b6002546101fd90660100000000000090046001600160c01b031681565b670de0b6b3a76400006101fd565b6000546101fd906001600160c01b031681565b61027e7f000000000000000000000000000000000000000000000000000000000000000081565b6102c6610d1c565b6103a27f000000000000000000000000000000000000000000000000000000000000000081565b6102a17f000000000000000000000000000000000000000000000000000000000000000081565b6040516303d1689d60e11b81527f000000000000000000000000000000000000000000000000000000000000000060048201526000906106c4906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906307a2d13a90602401602060405180830381865afa158015610670573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106949190611a5a565b6106bd7f0000000000000000000000000000000000000000000000000000000000000000611a89565b60006110ef565b905090565b60025460009065ffffffffffff1665fffffffffffe19016106ea5750600090565b6002544265ffffffffffff90911611156107045750600190565b50600290565b600080610715610a80565b915091509091565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610784573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a89190611a5a565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663372500ab6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561080557600080fd5b505af1158015610819573d6000803e3d6000fd5b50506040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031692507ffc30cddea38e2bf4d6ea7d3f9ed3b6ad7f176419f4963bd81318067a4aee73fe9150839083906370a0823190602401602060405180830381865afa1580156108a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108cb9190611a5a565b6108d59190611aa7565b60405190815260200160405180910390a250565b6040516370a0823160e01b81526001600160a01b0382811660048301526000916109a3917f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610956573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061097a9190611a5a565b6106bd7f0000000000000000000000000000000000000000000000000000000000000000611a89565b92915050565b600080600080610a1c6109ba6105e5565b610a0d6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000006111c4565b6001600160c01b031690611421565b90506000610a556001600160c01b0383167f0000000000000000000000000000000000000000000000000000000000000000600261142b565b9050610a618183611aba565b9450610a6d8183611ad9565b9495670de0b6b3a7640000945092505050565b600080306001600160a01b0316637d1ea1376040518163ffffffff1660e01b8152600401606060405180830381865afa925050508015610add575060408051601f3d908101601f19168201909252610ada91810190611b14565b60015b610cef573d808015610b0b576040519150601f19603f3d011682016040523d82523d6000602084013e610b10565b606091505b508051600003610b1f57600080fd5b60008054610b3c90600160c01b900465ffffffffffff1642611b57565b600154909150600090610b619061012c90600160c01b900465ffffffffffff16611b75565b90508065ffffffffffff168265ffffffffffff1611610b95576000546001546001600160c01b039182169650169350610ce7565b610bbf7f000000000000000000000000000000000000000000000000000000000000000082611b75565b65ffffffffffff168265ffffffffffff1610610be957506000946001600160c01b03945092505050565b610c5b610c35610bf98385611b57565b670de0b6b3a76400009065ffffffffffff908116907f000000000000000000000000000000000000000000000000000000000000000016611465565b610c4790670de0b6b3a7640000611ad9565b600180546001600160c01b03169190611474565b93506001600160c01b0380851614610ce757610ce482610c9b7f000000000000000000000000000000000000000000000000000000000000000084611b75565b610ca59190611b57565b6000546001600160c01b03169065ffffffffffff908116907f000000000000000000000000000000000000000000000000000000000000000016611465565b94505b505050610cf6565b5090925090505b806001600160c01b0316826001600160c01b03161115610d1857610d18611b93565b9091565b6000610d266106c9565b9050306001600160a01b03166305169ebe6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610d82575060408051601f3d908101601f19168201909252610d7f91810190611ba9565b60015b610dd4573d808015610db0576040519150601f19603f3d011682016040523d82523d6000602084013e610db5565b606091505b508051600003610dc457600080fd5b610dce60026115ac565b50611068565b6000610e096001600160c01b0383167f0000000000000000000000000000000000000000000000000000000000000000611421565b6003549091506001600160c01b039081169083161015610e4d57600380546001600160c01b0319166001600160c01b038416179055610e4860026115ac565b610e80565b6003546001600160c01b039081169082161115610e8057600380546001600160c01b0319166001600160c01b0383161790555b306001600160a01b0316637d1ea1376040518163ffffffff1660e01b8152600401606060405180830381865afa925050508015610eda575060408051601f3d908101601f19168201909252610ed791810190611b14565b60015b610f2c573d808015610f08576040519150601f19603f3d011682016040523d82523d6000602084013e610f0d565b606091505b508051600003610f1c57600080fd5b610f2660016115ac565b50611065565b6001600160c01b0382811614610fa95760008054600180546001600160c01b0319166001600160c01b0386811691909117909155600280546601000000000000600160f01b03191666010000000000008684160217905585166001600160f01b031990911617600160c01b4265ffffffffffff1602179055610fc0565b6001600160c01b03831615610fc057610fc0611b93565b7f00000000000000000000000000000000000000000000000000000000000000006001600160c01b0316816001600160c01b0316108061103157507f00000000000000000000000000000000000000000000000000000000000000006001600160c01b0316816001600160c01b0316115b8061104357506001600160c01b038316155b156110575761105260016115ac565b611061565b61106160006115ac565b5050505b50505b60006110726106c9565b90508060028111156110865761108661199c565b8260028111156110985761109861199c565b146110eb578060028111156110af576110af61199c565b8260028111156110c1576110c161199c565b6040517f99cada7141db4d51b602b2e469ec310c78ffbba0eb05bc3e3e633f30672dea0290600090a35b5050565b600083600003611101575060006111bd565b605f198360000b1361113a5760028260028111156111215761112161199c565b1461112d576000611130565b60015b60ff1690506111bd565b8260000b60281361115e5760405163f44398f560e01b815260040160405180910390fd5b611169601284611bc4565b925060006111798460000b6116d1565b61118490600a611ccc565b90506000808560000b12156111a35761119e8683866116e9565b6111ad565b6111ad8287611cd8565b90506111b88161178e565b925050505b9392505050565b6000826001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa925050508015611220575060408051601f3d908101601f1916820190925261121d91810190611d09565b60015b6112f9573d80801561124e576040519150601f19603f3d011682016040523d82523d6000602084013e611253565b606091505b5080516000036112f15760006001600160a01b0316846001600160a01b031663245a7bfc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ca9190611d5b565b6001600160a01b0316036101f057604051630cd5fa0760e11b815260040160405180910390fd5b805181602001fd5b81158061131d57508469ffffffffffffffffffff168169ffffffffffffffffffff16105b1561133b57604051630cd5fa0760e11b815260040160405180910390fd5b60006113478342611aa7565b905061135561012c89611b75565b65ffffffffffff168165ffffffffffff16111561138557604051630cd5fa0760e11b815260040160405180910390fd5b600085136113a55760405162bfc92160e01b815260040160405180910390fd5b611414858a6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061140b9190611d78565b6106bd90611a89565b9998505050505050505050565b60006111bd838360015b600061145d6114586114496001600160c01b03808716908816611cd8565b670de0b6b3a7640000856116e9565b61178e565b949350505050565b600061145d84848460006117bc565b60006001600160c01b038416158061149357506001600160c01b038316155b156114a0575060006111bd565b6001600160c01b0384811614806114bf57506001600160c01b03838116145b156114d257506001600160c01b036111bd565b6001600160c01b038381169085168181029182816114f2576114f2611d9b565b0414611508576001600160c01b039150506111bd565b80600184600281111561151d5761151d61199c565b03611531576706f05b59d3b2000001611555565b60028460028111156115455761154561199c565b0361155557670de0b6b3a763ffff015b8181101561156e576001600160c01b03925050506111bd565b6001600160c01b03670de0b6b3a764000082041115611598576001600160c01b03925050506111bd565b670de0b6b3a7640000900495945050505050565b6002544265ffffffffffff909116116115c25750565b60008160028111156115d6576115d661199c565b036115f5576002805465ffffffffffff191665ffffffffffff17905550565b60018160028111156116095761160961199c565b0361169b57600061164265ffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001642611db1565b905065ffffffffffff811061166c576002805465ffffffffffff191665ffffffffffff1790555050565b60025465ffffffffffff168110156110eb576002805465ffffffffffff191665ffffffffffff83161790555050565b60028160028111156116af576116af61199c565b036116ce576002805465ffffffffffff19164265ffffffffffff161790555b50565b60008082126116e057816109a3565b6109a382611dc4565b6000806116f68486611de0565b9050600083600281111561170c5761170c61199c565b036117185790506111bd565b600183600281111561172c5761172c61199c565b0361176b57600261173e600186611aa7565b6117489190611de0565b6117528587611df4565b1115611766578061176281611e08565b9150505b61145d565b6117758486611df4565b1561145d578061178481611e08565b9695505050505050565b60006001600160c01b038211156117b85760405163f44398f560e01b815260040160405180910390fd5b5090565b60006117d6611458866001600160c01b03168686866117df565b95945050505050565b6000806117ed86868661188c565b905060008360028111156118035761180361199c565b0361180f57905061145d565b6000848061181f5761181f611d9b565b868809905060028460028111156118385761183861199c565b036118565780156118515761184e600183611db1565b91505b611882565b6002611863600187611aa7565b61186d9190611de0565b8111156118825761187f600183611db1565b91505b5095945050505050565b600080600061189b868661196f565b915091508382106118bf5760405163f44398f560e01b815260040160405180910390fd5b600084806118cf576118cf611d9b565b8688099050818111156118e3576001830392505b9081900390600085900385168086816118fe576118fe611d9b565b04955080838161191057611910611d9b565b04925080816000038161192557611925611d9b565b046001019390930291909101600285810380870282030280870282030280870282030280870282030280870282030280870282030295860290039094029390930295945050505050565b6000808060001984860990508385029150818103925081811015611994576001830392505b509250929050565b634e487b7160e01b600052602160045260246000fd5b60208101600383106119d457634e487b7160e01b600052602160045260246000fd5b91905290565b602081526000825180602084015260005b81811015611a0857602081860181015160408684010152016119eb565b506000604082850101526040601f19601f83011684010191505092915050565b6001600160a01b03811681146116ce57600080fd5b600060208284031215611a4f57600080fd5b81356111bd81611a28565b600060208284031215611a6c57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b600081810b60808101611a9e57611a9e611a73565b60000392915050565b818103818111156109a3576109a3611a73565b6001600160c01b0382811682821603908111156109a3576109a3611a73565b6001600160c01b0381811683821601908111156109a3576109a3611a73565b80516001600160c01b0381168114611b0f57600080fd5b919050565b600080600060608486031215611b2957600080fd5b611b3284611af8565b9250611b4060208501611af8565b9150611b4e60408501611af8565b90509250925092565b65ffffffffffff82811682821603908111156109a3576109a3611a73565b65ffffffffffff81811683821601908111156109a3576109a3611a73565b634e487b7160e01b600052600160045260246000fd5b600060208284031215611bbb57600080fd5b6111bd82611af8565b600081810b9083900b01607f8113607f19821217156109a3576109a3611a73565b6001815b6001841115611c2057808504811115611c0457611c04611a73565b6001841615611c1257908102905b60019390931c928002611be9565b935093915050565b600082611c37575060016109a3565b81611c44575060006109a3565b8160018114611c5a5760028114611c6457611c80565b60019150506109a3565b60ff841115611c7557611c75611a73565b50506001821b6109a3565b5060208310610133831016604e8410600b8410161715611ca3575081810a6109a3565b611cb06000198484611be5565b8060001904821115611cc457611cc4611a73565b029392505050565b60006111bd8383611c28565b80820281158282048414176109a3576109a3611a73565b805169ffffffffffffffffffff81168114611b0f57600080fd5b600080600080600060a08688031215611d2157600080fd5b611d2a86611cef565b60208701516040880151606089015192975090955093509150611d4f60808701611cef565b90509295509295909350565b600060208284031215611d6d57600080fd5b81516111bd81611a28565b600060208284031215611d8a57600080fd5b815160ff811681146111bd57600080fd5b634e487b7160e01b600052601260045260246000fd5b808201808211156109a3576109a3611a73565b6000600160ff1b8201611dd957611dd9611a73565b5060000390565b600082611def57611def611d9b565b500490565b600082611e0357611e03611d9b565b500690565b600060018201611e1a57611e1a611a73565b506001019056fea26469706673582212207fefefe879fff17fec37ade333fb5e0477c6fb4953c8e6d3529ae162935ed62b64736f6c634300081c00330000000000000000000000000000000000000000000000000000000000093a800000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b84190000000000000000000000000000000000000000000000000011c37937e08000000000000000000000000000b7c4c4a2b7453e10d7e4e23fa8e8d2335d09afab00000000000000000000000000000000000000000000d3c21bcecceda10000000000000000000000000000000000000000000000000000000000000000000e10455448000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015180000000000000000000000000000000000000000000000000000000e8d4a51000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101f05760003560e01c806396f80ae91161010f578063c59b3d63116100a2578063e6a1505311610071578063e6a1505314610568578063f8ac93e81461058f578063fbfa77cf14610597578063fdfd5917146105be57600080fd5b8063c59b3d631461051b578063d77381db1461052a578063d9e8e67014610547578063ddc0c7c81461055557600080fd5b8063abfeece5116100de578063abfeece51461047f578063ae4e1875146104a6578063b94d8739146104cd578063c55f29d1146104f457600080fd5b806396f80ae91461043a5780639ec0727214610453578063a035b1fe14610466578063a7fa0faf1461046e57600080fd5b806354fd4d5011610187578063785e9e8611610156578063785e9e861461037b5780637d1ea137146103ba5780637dbdf1f5146103ec57806395acc4ae1461041357600080fd5b806354fd4d50146103015780635580f72a14610328578063701619e71461033b57806374b629541461035457600080fd5b80632dc86624116101c35780632dc8662414610295578063300105da146102af578063372500ab146102be5780633cb5d379146102c857600080fd5b806305169ebe146101f5578063200d2ed21461021a578063271181ec1461022f5780632bcfaa8014610257575b600080fd5b6101fd6105e5565b6040516001600160c01b0390911681526020015b60405180910390f35b6102226106c9565b60405161021191906119b2565b61023761070a565b604080516001600160c01b03938416815292909116602083015201610211565b61027e7f0000000000000000000000000000000000000000000000000000000000093a8081565b60405165ffffffffffff9091168152602001610211565b60025465ffffffffffff165b604051908152602001610211565b6101fd670de0b6b3a764000081565b6102c661071d565b005b6102ef7f000000000000000000000000000000000000000000000000000000000000001b81565b60405160ff9091168152602001610211565b60408051808201825260058152640342e322e360dc1b6020820152905161021191906119da565b6101fd610336366004611a3d565b6108e9565b60015461027e90600160c01b900465ffffffffffff1681565b6101fd7f0000000000000000000000000000000000000000000000000de0b5cad2bef00081565b6103a27f000000000000000000000000b7c4c4a2b7453e10d7e4e23fa8e8d2335d09afab81565b6040516001600160a01b039091168152602001610211565b6103c26109a9565b604080516001600160c01b0394851681529284166020840152921691810191909152606001610211565b6103a27f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841981565b6101fd7f00000000000000000000000000000000000000000000d3c21bcecceda100000081565b60005461027e90600160c01b900465ffffffffffff1681565b6001546101fd906001600160c01b031681565b610237610a80565b6003546001600160c01b03166101fd565b61027e7f0000000000000000000000000000000000000000000000000000000000000e1081565b6101fd7f0000000000000000000000000000000000000000000000000de0b6b3a764000081565b6101fd7f0000000000000000000000000000000000000000000000000de0b6b3a764000081565b6101fd7f0000000000000000000000000000000000000000000000000011c37937e0800081565b60405160018152602001610211565b6002546101fd90660100000000000090046001600160c01b031681565b670de0b6b3a76400006101fd565b6000546101fd906001600160c01b031681565b61027e7f000000000000000000000000000000000000000000000000000000000001518081565b6102c6610d1c565b6103a27f000000000000000000000000b7c4c4a2b7453e10d7e4e23fa8e8d2335d09afab81565b6102a17f455448000000000000000000000000000000000000000000000000000000000081565b6040516303d1689d60e11b81527f0000000000000000000000000000000000000000033b2e3c9fd0803ce800000060048201526000906106c4906001600160a01b037f000000000000000000000000b7c4c4a2b7453e10d7e4e23fa8e8d2335d09afab16906307a2d13a90602401602060405180830381865afa158015610670573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106949190611a5a565b6106bd7f0000000000000000000000000000000000000000000000000000000000000012611a89565b60006110ef565b905090565b60025460009065ffffffffffff1665fffffffffffe19016106ea5750600090565b6002544265ffffffffffff90911611156107045750600190565b50600290565b600080610715610a80565b915091509091565b6040516370a0823160e01b81523060048201526000907f0000000000000000000000009994e35db50125e0df82e4c2dde62496ce3309996001600160a01b0316906370a0823190602401602060405180830381865afa158015610784573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a89190611a5a565b90507f000000000000000000000000b7c4c4a2b7453e10d7e4e23fa8e8d2335d09afab6001600160a01b031663372500ab6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561080557600080fd5b505af1158015610819573d6000803e3d6000fd5b50506040516370a0823160e01b81523060048201527f0000000000000000000000009994e35db50125e0df82e4c2dde62496ce3309996001600160a01b031692507ffc30cddea38e2bf4d6ea7d3f9ed3b6ad7f176419f4963bd81318067a4aee73fe9150839083906370a0823190602401602060405180830381865afa1580156108a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108cb9190611a5a565b6108d59190611aa7565b60405190815260200160405180910390a250565b6040516370a0823160e01b81526001600160a01b0382811660048301526000916109a3917f000000000000000000000000b7c4c4a2b7453e10d7e4e23fa8e8d2335d09afab16906370a0823190602401602060405180830381865afa158015610956573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061097a9190611a5a565b6106bd7f000000000000000000000000000000000000000000000000000000000000001b611a89565b92915050565b600080600080610a1c6109ba6105e5565b610a0d6001600160a01b037f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b8419167f0000000000000000000000000000000000000000000000000000000000000e106111c4565b6001600160c01b031690611421565b90506000610a556001600160c01b0383167f0000000000000000000000000000000000000000000000000011c37937e08000600261142b565b9050610a618183611aba565b9450610a6d8183611ad9565b9495670de0b6b3a7640000945092505050565b600080306001600160a01b0316637d1ea1376040518163ffffffff1660e01b8152600401606060405180830381865afa925050508015610add575060408051601f3d908101601f19168201909252610ada91810190611b14565b60015b610cef573d808015610b0b576040519150601f19603f3d011682016040523d82523d6000602084013e610b10565b606091505b508051600003610b1f57600080fd5b60008054610b3c90600160c01b900465ffffffffffff1642611b57565b600154909150600090610b619061012c90600160c01b900465ffffffffffff16611b75565b90508065ffffffffffff168265ffffffffffff1611610b95576000546001546001600160c01b039182169650169350610ce7565b610bbf7f0000000000000000000000000000000000000000000000000000000000093a8082611b75565b65ffffffffffff168265ffffffffffff1610610be957506000946001600160c01b03945092505050565b610c5b610c35610bf98385611b57565b670de0b6b3a76400009065ffffffffffff908116907f0000000000000000000000000000000000000000000000000000000000093a8016611465565b610c4790670de0b6b3a7640000611ad9565b600180546001600160c01b03169190611474565b93506001600160c01b0380851614610ce757610ce482610c9b7f0000000000000000000000000000000000000000000000000000000000093a8084611b75565b610ca59190611b57565b6000546001600160c01b03169065ffffffffffff908116907f0000000000000000000000000000000000000000000000000000000000093a8016611465565b94505b505050610cf6565b5090925090505b806001600160c01b0316826001600160c01b03161115610d1857610d18611b93565b9091565b6000610d266106c9565b9050306001600160a01b03166305169ebe6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610d82575060408051601f3d908101601f19168201909252610d7f91810190611ba9565b60015b610dd4573d808015610db0576040519150601f19603f3d011682016040523d82523d6000602084013e610db5565b606091505b508051600003610dc457600080fd5b610dce60026115ac565b50611068565b6000610e096001600160c01b0383167f0000000000000000000000000000000000000000000000000de0b5cad2bef000611421565b6003549091506001600160c01b039081169083161015610e4d57600380546001600160c01b0319166001600160c01b038416179055610e4860026115ac565b610e80565b6003546001600160c01b039081169082161115610e8057600380546001600160c01b0319166001600160c01b0383161790555b306001600160a01b0316637d1ea1376040518163ffffffff1660e01b8152600401606060405180830381865afa925050508015610eda575060408051601f3d908101601f19168201909252610ed791810190611b14565b60015b610f2c573d808015610f08576040519150601f19603f3d011682016040523d82523d6000602084013e610f0d565b606091505b508051600003610f1c57600080fd5b610f2660016115ac565b50611065565b6001600160c01b0382811614610fa95760008054600180546001600160c01b0319166001600160c01b0386811691909117909155600280546601000000000000600160f01b03191666010000000000008684160217905585166001600160f01b031990911617600160c01b4265ffffffffffff1602179055610fc0565b6001600160c01b03831615610fc057610fc0611b93565b7f0000000000000000000000000000000000000000000000000de0b6b3a76400006001600160c01b0316816001600160c01b0316108061103157507f0000000000000000000000000000000000000000000000000de0b6b3a76400006001600160c01b0316816001600160c01b0316115b8061104357506001600160c01b038316155b156110575761105260016115ac565b611061565b61106160006115ac565b5050505b50505b60006110726106c9565b90508060028111156110865761108661199c565b8260028111156110985761109861199c565b146110eb578060028111156110af576110af61199c565b8260028111156110c1576110c161199c565b6040517f99cada7141db4d51b602b2e469ec310c78ffbba0eb05bc3e3e633f30672dea0290600090a35b5050565b600083600003611101575060006111bd565b605f198360000b1361113a5760028260028111156111215761112161199c565b1461112d576000611130565b60015b60ff1690506111bd565b8260000b60281361115e5760405163f44398f560e01b815260040160405180910390fd5b611169601284611bc4565b925060006111798460000b6116d1565b61118490600a611ccc565b90506000808560000b12156111a35761119e8683866116e9565b6111ad565b6111ad8287611cd8565b90506111b88161178e565b925050505b9392505050565b6000826001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa925050508015611220575060408051601f3d908101601f1916820190925261121d91810190611d09565b60015b6112f9573d80801561124e576040519150601f19603f3d011682016040523d82523d6000602084013e611253565b606091505b5080516000036112f15760006001600160a01b0316846001600160a01b031663245a7bfc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ca9190611d5b565b6001600160a01b0316036101f057604051630cd5fa0760e11b815260040160405180910390fd5b805181602001fd5b81158061131d57508469ffffffffffffffffffff168169ffffffffffffffffffff16105b1561133b57604051630cd5fa0760e11b815260040160405180910390fd5b60006113478342611aa7565b905061135561012c89611b75565b65ffffffffffff168165ffffffffffff16111561138557604051630cd5fa0760e11b815260040160405180910390fd5b600085136113a55760405162bfc92160e01b815260040160405180910390fd5b611414858a6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061140b9190611d78565b6106bd90611a89565b9998505050505050505050565b60006111bd838360015b600061145d6114586114496001600160c01b03808716908816611cd8565b670de0b6b3a7640000856116e9565b61178e565b949350505050565b600061145d84848460006117bc565b60006001600160c01b038416158061149357506001600160c01b038316155b156114a0575060006111bd565b6001600160c01b0384811614806114bf57506001600160c01b03838116145b156114d257506001600160c01b036111bd565b6001600160c01b038381169085168181029182816114f2576114f2611d9b565b0414611508576001600160c01b039150506111bd565b80600184600281111561151d5761151d61199c565b03611531576706f05b59d3b2000001611555565b60028460028111156115455761154561199c565b0361155557670de0b6b3a763ffff015b8181101561156e576001600160c01b03925050506111bd565b6001600160c01b03670de0b6b3a764000082041115611598576001600160c01b03925050506111bd565b670de0b6b3a7640000900495945050505050565b6002544265ffffffffffff909116116115c25750565b60008160028111156115d6576115d661199c565b036115f5576002805465ffffffffffff191665ffffffffffff17905550565b60018160028111156116095761160961199c565b0361169b57600061164265ffffffffffff7f00000000000000000000000000000000000000000000000000000000000151801642611db1565b905065ffffffffffff811061166c576002805465ffffffffffff191665ffffffffffff1790555050565b60025465ffffffffffff168110156110eb576002805465ffffffffffff191665ffffffffffff83161790555050565b60028160028111156116af576116af61199c565b036116ce576002805465ffffffffffff19164265ffffffffffff161790555b50565b60008082126116e057816109a3565b6109a382611dc4565b6000806116f68486611de0565b9050600083600281111561170c5761170c61199c565b036117185790506111bd565b600183600281111561172c5761172c61199c565b0361176b57600261173e600186611aa7565b6117489190611de0565b6117528587611df4565b1115611766578061176281611e08565b9150505b61145d565b6117758486611df4565b1561145d578061178481611e08565b9695505050505050565b60006001600160c01b038211156117b85760405163f44398f560e01b815260040160405180910390fd5b5090565b60006117d6611458866001600160c01b03168686866117df565b95945050505050565b6000806117ed86868661188c565b905060008360028111156118035761180361199c565b0361180f57905061145d565b6000848061181f5761181f611d9b565b868809905060028460028111156118385761183861199c565b036118565780156118515761184e600183611db1565b91505b611882565b6002611863600187611aa7565b61186d9190611de0565b8111156118825761187f600183611db1565b91505b5095945050505050565b600080600061189b868661196f565b915091508382106118bf5760405163f44398f560e01b815260040160405180910390fd5b600084806118cf576118cf611d9b565b8688099050818111156118e3576001830392505b9081900390600085900385168086816118fe576118fe611d9b565b04955080838161191057611910611d9b565b04925080816000038161192557611925611d9b565b046001019390930291909101600285810380870282030280870282030280870282030280870282030280870282030280870282030295860290039094029390930295945050505050565b6000808060001984860990508385029150818103925081811015611994576001830392505b509250929050565b634e487b7160e01b600052602160045260246000fd5b60208101600383106119d457634e487b7160e01b600052602160045260246000fd5b91905290565b602081526000825180602084015260005b81811015611a0857602081860181015160408684010152016119eb565b506000604082850101526040601f19601f83011684010191505092915050565b6001600160a01b03811681146116ce57600080fd5b600060208284031215611a4f57600080fd5b81356111bd81611a28565b600060208284031215611a6c57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b600081810b60808101611a9e57611a9e611a73565b60000392915050565b818103818111156109a3576109a3611a73565b6001600160c01b0382811682821603908111156109a3576109a3611a73565b6001600160c01b0381811683821601908111156109a3576109a3611a73565b80516001600160c01b0381168114611b0f57600080fd5b919050565b600080600060608486031215611b2957600080fd5b611b3284611af8565b9250611b4060208501611af8565b9150611b4e60408501611af8565b90509250925092565b65ffffffffffff82811682821603908111156109a3576109a3611a73565b65ffffffffffff81811683821601908111156109a3576109a3611a73565b634e487b7160e01b600052600160045260246000fd5b600060208284031215611bbb57600080fd5b6111bd82611af8565b600081810b9083900b01607f8113607f19821217156109a3576109a3611a73565b6001815b6001841115611c2057808504811115611c0457611c04611a73565b6001841615611c1257908102905b60019390931c928002611be9565b935093915050565b600082611c37575060016109a3565b81611c44575060006109a3565b8160018114611c5a5760028114611c6457611c80565b60019150506109a3565b60ff841115611c7557611c75611a73565b50506001821b6109a3565b5060208310610133831016604e8410600b8410161715611ca3575081810a6109a3565b611cb06000198484611be5565b8060001904821115611cc457611cc4611a73565b029392505050565b60006111bd8383611c28565b80820281158282048414176109a3576109a3611a73565b805169ffffffffffffffffffff81168114611b0f57600080fd5b600080600080600060a08688031215611d2157600080fd5b611d2a86611cef565b60208701516040880151606089015192975090955093509150611d4f60808701611cef565b90509295509295909350565b600060208284031215611d6d57600080fd5b81516111bd81611a28565b600060208284031215611d8a57600080fd5b815160ff811681146111bd57600080fd5b634e487b7160e01b600052601260045260246000fd5b808201808211156109a3576109a3611a73565b6000600160ff1b8201611dd957611dd9611a73565b5060000390565b600082611def57611def611d9b565b500490565b600082611e0357611e03611d9b565b500690565b600060018201611e1a57611e1a611a73565b506001019056fea26469706673582212207fefefe879fff17fec37ade333fb5e0477c6fb4953c8e6d3529ae162935ed62b64736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000000000000000093a800000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b84190000000000000000000000000000000000000000000000000011c37937e08000000000000000000000000000b7c4c4a2b7453e10d7e4e23fa8e8d2335d09afab00000000000000000000000000000000000000000000d3c21bcecceda10000000000000000000000000000000000000000000000000000000000000000000e10455448000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015180000000000000000000000000000000000000000000000000000000e8d4a51000
-----Decoded View---------------
Arg [0] : config (tuple):
Arg [1] : priceTimeout (uint48): 604800
Arg [2] : chainlinkFeed (address): 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419
Arg [3] : oracleError (uint192): 5000000000000000
Arg [4] : erc20 (address): 0xB7c4c4a2B7453E10d7e4e23Fa8E8D2335d09afab
Arg [5] : maxTradeVolume (uint192): 1000000000000000000000000
Arg [6] : oracleTimeout (uint48): 3600
Arg [7] : targetName (bytes32): 0x4554480000000000000000000000000000000000000000000000000000000000
Arg [8] : defaultThreshold (uint192): 0
Arg [9] : delayUntilDefault (uint48): 86400
Arg [1] : revenueHiding (uint192): 1000000000000
-----Encoded View---------------
10 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000093a80
Arg [1] : 0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b8419
Arg [2] : 0000000000000000000000000000000000000000000000000011c37937e08000
Arg [3] : 000000000000000000000000b7c4c4a2b7453e10d7e4e23fa8e8d2335d09afab
Arg [4] : 00000000000000000000000000000000000000000000d3c21bcecceda1000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000e10
Arg [6] : 4554480000000000000000000000000000000000000000000000000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [8] : 0000000000000000000000000000000000000000000000000000000000015180
Arg [9] : 000000000000000000000000000000000000000000000000000000e8d4a51000
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.