// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../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 account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
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 returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @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 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 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 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 `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @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 Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert Errors.FailedCall();
}
}
/**
* @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 or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {Errors.FailedCall} error.
*
* 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.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @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`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
* of an unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {Errors.FailedCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
*/
function _revert(bytes memory returndata) 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
assembly ("memory-safe") {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { UD2x18 } from "./ValueType.sol";
/// @notice Casts a UD2x18 number into SD59x18.
/// @dev There is no overflow check because UD2x18 ⊆ SD59x18.
function intoSD59x18(UD2x18 x) pure returns (SD59x18 result) {
result = SD59x18.wrap(int256(uint256(UD2x18.unwrap(x))));
}
/// @notice Casts a UD2x18 number into UD60x18.
/// @dev There is no overflow check because UD2x18 ⊆ UD60x18.
function intoUD60x18(UD2x18 x) pure returns (UD60x18 result) {
result = UD60x18.wrap(UD2x18.unwrap(x));
}
/// @notice Casts a UD2x18 number into uint128.
/// @dev There is no overflow check because UD2x18 ⊆ uint128.
function intoUint128(UD2x18 x) pure returns (uint128 result) {
result = uint128(UD2x18.unwrap(x));
}
/// @notice Casts a UD2x18 number into uint256.
/// @dev There is no overflow check because UD2x18 ⊆ uint256.
function intoUint256(UD2x18 x) pure returns (uint256 result) {
result = uint256(UD2x18.unwrap(x));
}
/// @notice Casts a UD2x18 number into uint40.
/// @dev Requirements:
/// - x ≤ MAX_UINT40
function intoUint40(UD2x18 x) pure returns (uint40 result) {
uint64 xUint = UD2x18.unwrap(x);
if (xUint > uint64(Common.MAX_UINT40)) {
revert Errors.PRBMath_UD2x18_IntoUint40_Overflow(x);
}
result = uint40(xUint);
}
/// @notice Alias for {wrap}.
function ud2x18(uint64 x) pure returns (UD2x18 result) {
result = UD2x18.wrap(x);
}
/// @notice Unwrap a UD2x18 number into uint64.
function unwrap(UD2x18 x) pure returns (uint64 result) {
result = UD2x18.unwrap(x);
}
/// @notice Wraps a uint64 number into UD2x18.
function wrap(uint64 x) pure returns (UD2x18 result) {
result = UD2x18.wrap(x);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/Checkpoints.sol)
// This file was procedurally generated from scripts/generate/templates/Checkpoints.js.
pragma solidity ^0.8.20;
import {Math} from "../math/Math.sol";
/**
* @dev This library defines the `Trace*` struct, for checkpointing values as they change at different points in
* time, and later looking up past values by block number. See {Votes} as an example.
*
* To create a history of checkpoints define a variable type `Checkpoints.Trace*` in your contract, and store a new
* checkpoint for the current transaction block using the {push} function.
*/
library Checkpoints {
/**
* @dev A value was attempted to be inserted on a past checkpoint.
*/
error CheckpointUnorderedInsertion();
struct Trace224 {
Checkpoint224[] _checkpoints;
}
struct Checkpoint224 {
uint32 _key;
uint224 _value;
}
/**
* @dev Pushes a (`key`, `value`) pair into a Trace224 so that it is stored as the checkpoint.
*
* Returns previous value and new value.
*
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint32).max` key set will disable the
* library.
*/
function push(
Trace224 storage self,
uint32 key,
uint224 value
) internal returns (uint224 oldValue, uint224 newValue) {
return _insert(self._checkpoints, key, value);
}
/**
* @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
* there is none.
*/
function lowerLookup(Trace224 storage self, uint32 key) internal view returns (uint224) {
uint256 len = self._checkpoints.length;
uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*/
function upperLookup(Trace224 storage self, uint32 key) internal view returns (uint224) {
uint256 len = self._checkpoints.length;
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*
* NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
* keys).
*/
function upperLookupRecent(Trace224 storage self, uint32 key) internal view returns (uint224) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
*/
function latest(Trace224 storage self) internal view returns (uint224) {
uint256 pos = self._checkpoints.length;
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
* in the most recent checkpoint.
*/
function latestCheckpoint(Trace224 storage self) internal view returns (bool exists, uint32 _key, uint224 _value) {
uint256 pos = self._checkpoints.length;
if (pos == 0) {
return (false, 0, 0);
} else {
Checkpoint224 storage ckpt = _unsafeAccess(self._checkpoints, pos - 1);
return (true, ckpt._key, ckpt._value);
}
}
/**
* @dev Returns the number of checkpoint.
*/
function length(Trace224 storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
/**
* @dev Returns checkpoint at given position.
*/
function at(Trace224 storage self, uint32 pos) internal view returns (Checkpoint224 memory) {
return self._checkpoints[pos];
}
/**
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
* or by updating the last one.
*/
function _insert(
Checkpoint224[] storage self,
uint32 key,
uint224 value
) private returns (uint224 oldValue, uint224 newValue) {
uint256 pos = self.length;
if (pos > 0) {
Checkpoint224 storage last = _unsafeAccess(self, pos - 1);
uint32 lastKey = last._key;
uint224 lastValue = last._value;
// Checkpoint keys must be non-decreasing.
if (lastKey > key) {
revert CheckpointUnorderedInsertion();
}
// Update or push new checkpoint
if (lastKey == key) {
last._value = value;
} else {
self.push(Checkpoint224({_key: key, _value: value}));
}
return (lastValue, value);
} else {
self.push(Checkpoint224({_key: key, _value: value}));
return (0, value);
}
}
/**
* @dev Return the index of the first (oldest) checkpoint with key strictly bigger than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _upperBinaryLookup(
Checkpoint224[] storage self,
uint32 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
/**
* @dev Return the index of the first (oldest) checkpoint with key greater or equal than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _lowerBinaryLookup(
Checkpoint224[] storage self,
uint32 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
/**
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
*/
function _unsafeAccess(
Checkpoint224[] storage self,
uint256 pos
) private pure returns (Checkpoint224 storage result) {
assembly {
mstore(0, self.slot)
result.slot := add(keccak256(0, 0x20), pos)
}
}
struct Trace208 {
Checkpoint208[] _checkpoints;
}
struct Checkpoint208 {
uint48 _key;
uint208 _value;
}
/**
* @dev Pushes a (`key`, `value`) pair into a Trace208 so that it is stored as the checkpoint.
*
* Returns previous value and new value.
*
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint48).max` key set will disable the
* library.
*/
function push(
Trace208 storage self,
uint48 key,
uint208 value
) internal returns (uint208 oldValue, uint208 newValue) {
return _insert(self._checkpoints, key, value);
}
/**
* @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
* there is none.
*/
function lowerLookup(Trace208 storage self, uint48 key) internal view returns (uint208) {
uint256 len = self._checkpoints.length;
uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*/
function upperLookup(Trace208 storage self, uint48 key) internal view returns (uint208) {
uint256 len = self._checkpoints.length;
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*
* NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
* keys).
*/
function upperLookupRecent(Trace208 storage self, uint48 key) internal view returns (uint208) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
*/
function latest(Trace208 storage self) internal view returns (uint208) {
uint256 pos = self._checkpoints.length;
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
* in the most recent checkpoint.
*/
function latestCheckpoint(Trace208 storage self) internal view returns (bool exists, uint48 _key, uint208 _value) {
uint256 pos = self._checkpoints.length;
if (pos == 0) {
return (false, 0, 0);
} else {
Checkpoint208 storage ckpt = _unsafeAccess(self._checkpoints, pos - 1);
return (true, ckpt._key, ckpt._value);
}
}
/**
* @dev Returns the number of checkpoint.
*/
function length(Trace208 storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
/**
* @dev Returns checkpoint at given position.
*/
function at(Trace208 storage self, uint32 pos) internal view returns (Checkpoint208 memory) {
return self._checkpoints[pos];
}
/**
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
* or by updating the last one.
*/
function _insert(
Checkpoint208[] storage self,
uint48 key,
uint208 value
) private returns (uint208 oldValue, uint208 newValue) {
uint256 pos = self.length;
if (pos > 0) {
Checkpoint208 storage last = _unsafeAccess(self, pos - 1);
uint48 lastKey = last._key;
uint208 lastValue = last._value;
// Checkpoint keys must be non-decreasing.
if (lastKey > key) {
revert CheckpointUnorderedInsertion();
}
// Update or push new checkpoint
if (lastKey == key) {
last._value = value;
} else {
self.push(Checkpoint208({_key: key, _value: value}));
}
return (lastValue, value);
} else {
self.push(Checkpoint208({_key: key, _value: value}));
return (0, value);
}
}
/**
* @dev Return the index of the first (oldest) checkpoint with key strictly bigger than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _upperBinaryLookup(
Checkpoint208[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
/**
* @dev Return the index of the first (oldest) checkpoint with key greater or equal than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _lowerBinaryLookup(
Checkpoint208[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
/**
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
*/
function _unsafeAccess(
Checkpoint208[] storage self,
uint256 pos
) private pure returns (Checkpoint208 storage result) {
assembly {
mstore(0, self.slot)
result.slot := add(keccak256(0, 0x20), pos)
}
}
struct Trace160 {
Checkpoint160[] _checkpoints;
}
struct Checkpoint160 {
uint96 _key;
uint160 _value;
}
/**
* @dev Pushes a (`key`, `value`) pair into a Trace160 so that it is stored as the checkpoint.
*
* Returns previous value and new value.
*
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint96).max` key set will disable the
* library.
*/
function push(
Trace160 storage self,
uint96 key,
uint160 value
) internal returns (uint160 oldValue, uint160 newValue) {
return _insert(self._checkpoints, key, value);
}
/**
* @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
* there is none.
*/
function lowerLookup(Trace160 storage self, uint96 key) internal view returns (uint160) {
uint256 len = self._checkpoints.length;
uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*/
function upperLookup(Trace160 storage self, uint96 key) internal view returns (uint160) {
uint256 len = self._checkpoints.length;
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*
* NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
* keys).
*/
function upperLookupRecent(Trace160 storage self, uint96 key) internal view returns (uint160) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
*/
function latest(Trace160 storage self) internal view returns (uint160) {
uint256 pos = self._checkpoints.length;
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
* in the most recent checkpoint.
*/
function latestCheckpoint(Trace160 storage self) internal view returns (bool exists, uint96 _key, uint160 _value) {
uint256 pos = self._checkpoints.length;
if (pos == 0) {
return (false, 0, 0);
} else {
Checkpoint160 storage ckpt = _unsafeAccess(self._checkpoints, pos - 1);
return (true, ckpt._key, ckpt._value);
}
}
/**
* @dev Returns the number of checkpoint.
*/
function length(Trace160 storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
/**
* @dev Returns checkpoint at given position.
*/
function at(Trace160 storage self, uint32 pos) internal view returns (Checkpoint160 memory) {
return self._checkpoints[pos];
}
/**
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
* or by updating the last one.
*/
function _insert(
Checkpoint160[] storage self,
uint96 key,
uint160 value
) private returns (uint160 oldValue, uint160 newValue) {
uint256 pos = self.length;
if (pos > 0) {
Checkpoint160 storage last = _unsafeAccess(self, pos - 1);
uint96 lastKey = last._key;
uint160 lastValue = last._value;
// Checkpoint keys must be non-decreasing.
if (lastKey > key) {
revert CheckpointUnorderedInsertion();
}
// Update or push new checkpoint
if (lastKey == key) {
last._value = value;
} else {
self.push(Checkpoint160({_key: key, _value: value}));
}
return (lastValue, value);
} else {
self.push(Checkpoint160({_key: key, _value: value}));
return (0, value);
}
}
/**
* @dev Return the index of the first (oldest) checkpoint with key strictly bigger than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _upperBinaryLookup(
Checkpoint160[] storage self,
uint96 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
/**
* @dev Return the index of the first (oldest) checkpoint with key greater or equal than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _lowerBinaryLookup(
Checkpoint160[] storage self,
uint96 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
/**
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
*/
function _unsafeAccess(
Checkpoint160[] storage self,
uint256 pos
) private pure returns (Checkpoint160 storage result) {
assembly {
mstore(0, self.slot)
result.slot := add(keccak256(0, 0x20), pos)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
// Common.sol
//
// Common mathematical functions used in both SD59x18 and UD60x18. Note that these global functions do not
// always operate with SD59x18 and UD60x18 numbers.
/*//////////////////////////////////////////////////////////////////////////
CUSTOM ERRORS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when the resultant value in {mulDiv} overflows uint256.
error PRBMath_MulDiv_Overflow(uint256 x, uint256 y, uint256 denominator);
/// @notice Thrown when the resultant value in {mulDiv18} overflows uint256.
error PRBMath_MulDiv18_Overflow(uint256 x, uint256 y);
/// @notice Thrown when one of the inputs passed to {mulDivSigned} is `type(int256).min`.
error PRBMath_MulDivSigned_InputTooSmall();
/// @notice Thrown when the resultant value in {mulDivSigned} overflows int256.
error PRBMath_MulDivSigned_Overflow(int256 x, int256 y);
/*//////////////////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////////////////*/
/// @dev The maximum value a uint128 number can have.
uint128 constant MAX_UINT128 = type(uint128).max;
/// @dev The maximum value a uint40 number can have.
uint40 constant MAX_UINT40 = type(uint40).max;
/// @dev The maximum value a uint64 number can have.
uint64 constant MAX_UINT64 = type(uint64).max;
/// @dev The unit number, which the decimal precision of the fixed-point types.
uint256 constant UNIT = 1e18;
/// @dev The unit number inverted mod 2^256.
uint256 constant UNIT_INVERSE = 78156646155174841979727994598816262306175212592076161876661_508869554232690281;
/// @dev The the largest power of two that divides the decimal value of `UNIT`. The logarithm of this value is the least significant
/// bit in the binary representation of `UNIT`.
uint256 constant UNIT_LPOTD = 262144;
/*//////////////////////////////////////////////////////////////////////////
FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Calculates the binary exponent of x using the binary fraction method.
/// @dev Has to use 192.64-bit fixed-point numbers. See https://ethereum.stackexchange.com/a/96594/24693.
/// @param x The exponent as an unsigned 192.64-bit fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function exp2(uint256 x) pure returns (uint256 result) {
unchecked {
// Start from 0.5 in the 192.64-bit fixed-point format.
result = 0x800000000000000000000000000000000000000000000000;
// The following logic multiplies the result by $\sqrt{2^{-i}}$ when the bit at position i is 1. Key points:
//
// 1. Intermediate results will not overflow, as the starting point is 2^191 and all magic factors are under 2^65.
// 2. The rationale for organizing the if statements into groups of 8 is gas savings. If the result of performing
// a bitwise AND operation between x and any value in the array [0x80; 0x40; 0x20; 0x10; 0x08; 0x04; 0x02; 0x01] is 1,
// we know that `x & 0xFF` is also 1.
if (x & 0xFF00000000000000 > 0) {
if (x & 0x8000000000000000 > 0) {
result = (result * 0x16A09E667F3BCC909) >> 64;
}
if (x & 0x4000000000000000 > 0) {
result = (result * 0x1306FE0A31B7152DF) >> 64;
}
if (x & 0x2000000000000000 > 0) {
result = (result * 0x1172B83C7D517ADCE) >> 64;
}
if (x & 0x1000000000000000 > 0) {
result = (result * 0x10B5586CF9890F62A) >> 64;
}
if (x & 0x800000000000000 > 0) {
result = (result * 0x1059B0D31585743AE) >> 64;
}
if (x & 0x400000000000000 > 0) {
result = (result * 0x102C9A3E778060EE7) >> 64;
}
if (x & 0x200000000000000 > 0) {
result = (result * 0x10163DA9FB33356D8) >> 64;
}
if (x & 0x100000000000000 > 0) {
result = (result * 0x100B1AFA5ABCBED61) >> 64;
}
}
if (x & 0xFF000000000000 > 0) {
if (x & 0x80000000000000 > 0) {
result = (result * 0x10058C86DA1C09EA2) >> 64;
}
if (x & 0x40000000000000 > 0) {
result = (result * 0x1002C605E2E8CEC50) >> 64;
}
if (x & 0x20000000000000 > 0) {
result = (result * 0x100162F3904051FA1) >> 64;
}
if (x & 0x10000000000000 > 0) {
result = (result * 0x1000B175EFFDC76BA) >> 64;
}
if (x & 0x8000000000000 > 0) {
result = (result * 0x100058BA01FB9F96D) >> 64;
}
if (x & 0x4000000000000 > 0) {
result = (result * 0x10002C5CC37DA9492) >> 64;
}
if (x & 0x2000000000000 > 0) {
result = (result * 0x1000162E525EE0547) >> 64;
}
if (x & 0x1000000000000 > 0) {
result = (result * 0x10000B17255775C04) >> 64;
}
}
if (x & 0xFF0000000000 > 0) {
if (x & 0x800000000000 > 0) {
result = (result * 0x1000058B91B5BC9AE) >> 64;
}
if (x & 0x400000000000 > 0) {
result = (result * 0x100002C5C89D5EC6D) >> 64;
}
if (x & 0x200000000000 > 0) {
result = (result * 0x10000162E43F4F831) >> 64;
}
if (x & 0x100000000000 > 0) {
result = (result * 0x100000B1721BCFC9A) >> 64;
}
if (x & 0x80000000000 > 0) {
result = (result * 0x10000058B90CF1E6E) >> 64;
}
if (x & 0x40000000000 > 0) {
result = (result * 0x1000002C5C863B73F) >> 64;
}
if (x & 0x20000000000 > 0) {
result = (result * 0x100000162E430E5A2) >> 64;
}
if (x & 0x10000000000 > 0) {
result = (result * 0x1000000B172183551) >> 64;
}
}
if (x & 0xFF00000000 > 0) {
if (x & 0x8000000000 > 0) {
result = (result * 0x100000058B90C0B49) >> 64;
}
if (x & 0x4000000000 > 0) {
result = (result * 0x10000002C5C8601CC) >> 64;
}
if (x & 0x2000000000 > 0) {
result = (result * 0x1000000162E42FFF0) >> 64;
}
if (x & 0x1000000000 > 0) {
result = (result * 0x10000000B17217FBB) >> 64;
}
if (x & 0x800000000 > 0) {
result = (result * 0x1000000058B90BFCE) >> 64;
}
if (x & 0x400000000 > 0) {
result = (result * 0x100000002C5C85FE3) >> 64;
}
if (x & 0x200000000 > 0) {
result = (result * 0x10000000162E42FF1) >> 64;
}
if (x & 0x100000000 > 0) {
result = (result * 0x100000000B17217F8) >> 64;
}
}
if (x & 0xFF000000 > 0) {
if (x & 0x80000000 > 0) {
result = (result * 0x10000000058B90BFC) >> 64;
}
if (x & 0x40000000 > 0) {
result = (result * 0x1000000002C5C85FE) >> 64;
}
if (x & 0x20000000 > 0) {
result = (result * 0x100000000162E42FF) >> 64;
}
if (x & 0x10000000 > 0) {
result = (result * 0x1000000000B17217F) >> 64;
}
if (x & 0x8000000 > 0) {
result = (result * 0x100000000058B90C0) >> 64;
}
if (x & 0x4000000 > 0) {
result = (result * 0x10000000002C5C860) >> 64;
}
if (x & 0x2000000 > 0) {
result = (result * 0x1000000000162E430) >> 64;
}
if (x & 0x1000000 > 0) {
result = (result * 0x10000000000B17218) >> 64;
}
}
if (x & 0xFF0000 > 0) {
if (x & 0x800000 > 0) {
result = (result * 0x1000000000058B90C) >> 64;
}
if (x & 0x400000 > 0) {
result = (result * 0x100000000002C5C86) >> 64;
}
if (x & 0x200000 > 0) {
result = (result * 0x10000000000162E43) >> 64;
}
if (x & 0x100000 > 0) {
result = (result * 0x100000000000B1721) >> 64;
}
if (x & 0x80000 > 0) {
result = (result * 0x10000000000058B91) >> 64;
}
if (x & 0x40000 > 0) {
result = (result * 0x1000000000002C5C8) >> 64;
}
if (x & 0x20000 > 0) {
result = (result * 0x100000000000162E4) >> 64;
}
if (x & 0x10000 > 0) {
result = (result * 0x1000000000000B172) >> 64;
}
}
if (x & 0xFF00 > 0) {
if (x & 0x8000 > 0) {
result = (result * 0x100000000000058B9) >> 64;
}
if (x & 0x4000 > 0) {
result = (result * 0x10000000000002C5D) >> 64;
}
if (x & 0x2000 > 0) {
result = (result * 0x1000000000000162E) >> 64;
}
if (x & 0x1000 > 0) {
result = (result * 0x10000000000000B17) >> 64;
}
if (x & 0x800 > 0) {
result = (result * 0x1000000000000058C) >> 64;
}
if (x & 0x400 > 0) {
result = (result * 0x100000000000002C6) >> 64;
}
if (x & 0x200 > 0) {
result = (result * 0x10000000000000163) >> 64;
}
if (x & 0x100 > 0) {
result = (result * 0x100000000000000B1) >> 64;
}
}
if (x & 0xFF > 0) {
if (x & 0x80 > 0) {
result = (result * 0x10000000000000059) >> 64;
}
if (x & 0x40 > 0) {
result = (result * 0x1000000000000002C) >> 64;
}
if (x & 0x20 > 0) {
result = (result * 0x10000000000000016) >> 64;
}
if (x & 0x10 > 0) {
result = (result * 0x1000000000000000B) >> 64;
}
if (x & 0x8 > 0) {
result = (result * 0x10000000000000006) >> 64;
}
if (x & 0x4 > 0) {
result = (result * 0x10000000000000003) >> 64;
}
if (x & 0x2 > 0) {
result = (result * 0x10000000000000001) >> 64;
}
if (x & 0x1 > 0) {
result = (result * 0x10000000000000001) >> 64;
}
}
// In the code snippet below, two operations are executed simultaneously:
//
// 1. The result is multiplied by $(2^n + 1)$, where $2^n$ represents the integer part, and the additional 1
// accounts for the initial guess of 0.5. This is achieved by subtracting from 191 instead of 192.
// 2. The result is then converted to an unsigned 60.18-decimal fixed-point format.
//
// The underlying logic is based on the relationship $2^{191-ip} = 2^{ip} / 2^{191}$, where $ip$ denotes the,
// integer part, $2^n$.
result *= UNIT;
result >>= (191 - (x >> 64));
}
}
/// @notice Finds the zero-based index of the first 1 in the binary representation of x.
///
/// @dev See the note on "msb" in this Wikipedia article: https://en.wikipedia.org/wiki/Find_first_set
///
/// Each step in this implementation is equivalent to this high-level code:
///
/// ```solidity
/// if (x >= 2 ** 128) {
/// x >>= 128;
/// result += 128;
/// }
/// ```
///
/// Where 128 is replaced with each respective power of two factor. See the full high-level implementation here:
/// https://gist.github.com/PaulRBerg/f932f8693f2733e30c4d479e8e980948
///
/// The Yul instructions used below are:
///
/// - "gt" is "greater than"
/// - "or" is the OR bitwise operator
/// - "shl" is "shift left"
/// - "shr" is "shift right"
///
/// @param x The uint256 number for which to find the index of the most significant bit.
/// @return result The index of the most significant bit as a uint256.
/// @custom:smtchecker abstract-function-nondet
function msb(uint256 x) pure returns (uint256 result) {
// 2^128
assembly ("memory-safe") {
let factor := shl(7, gt(x, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^64
assembly ("memory-safe") {
let factor := shl(6, gt(x, 0xFFFFFFFFFFFFFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^32
assembly ("memory-safe") {
let factor := shl(5, gt(x, 0xFFFFFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^16
assembly ("memory-safe") {
let factor := shl(4, gt(x, 0xFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^8
assembly ("memory-safe") {
let factor := shl(3, gt(x, 0xFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^4
assembly ("memory-safe") {
let factor := shl(2, gt(x, 0xF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^2
assembly ("memory-safe") {
let factor := shl(1, gt(x, 0x3))
x := shr(factor, x)
result := or(result, factor)
}
// 2^1
// No need to shift x any more.
assembly ("memory-safe") {
let factor := gt(x, 0x1)
result := or(result, factor)
}
}
/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev Credits to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - The denominator must not be zero.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as a uint256.
/// @param y The multiplier as a uint256.
/// @param denominator The divisor as a uint256.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function mulDiv(uint256 x, uint256 y, uint256 denominator) pure returns (uint256 result) {
// 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 ("memory-safe") {
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) {
unchecked {
return prod0 / denominator;
}
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (prod1 >= denominator) {
revert PRBMath_MulDiv_Overflow(x, y, denominator);
}
////////////////////////////////////////////////////////////////////////////
// 512 by 256 division
////////////////////////////////////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly ("memory-safe") {
// Compute remainder using the mulmod Yul instruction.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512-bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
unchecked {
// Calculate the largest power of two divisor of the denominator using the unary operator ~. This operation cannot overflow
// because the denominator cannot be zero at this point in the function execution. The result is always >= 1.
// For more detail, see https://cs.stackexchange.com/q/138556/92363.
uint256 lpotdod = denominator & (~denominator + 1);
uint256 flippedLpotdod;
assembly ("memory-safe") {
// Factor powers of two out of denominator.
denominator := div(denominator, lpotdod)
// Divide [prod1 prod0] by lpotdod.
prod0 := div(prod0, lpotdod)
// Get the flipped value `2^256 / lpotdod`. If the `lpotdod` is zero, the flipped value is one.
// `sub(0, lpotdod)` produces the two's complement version of `lpotdod`, which is equivalent to flipping all the bits.
// However, `div` interprets this value as an unsigned value: https://ethereum.stackexchange.com/q/147168/24693
flippedLpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * flippedLpotdod;
// 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;
}
}
/// @notice Calculates x*y÷1e18 with 512-bit precision.
///
/// @dev A variant of {mulDiv} with constant folding, i.e. in which the denominator is hard coded to 1e18.
///
/// Notes:
/// - The body is purposely left uncommented; to understand how this works, see the documentation in {mulDiv}.
/// - The result is rounded toward zero.
/// - We take as an axiom that the result cannot be `MAX_UINT256` when x and y solve the following system of equations:
///
/// $$
/// \begin{cases}
/// x * y = MAX\_UINT256 * UNIT \\
/// (x * y) \% UNIT \geq \frac{UNIT}{2}
/// \end{cases}
/// $$
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
/// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function mulDiv18(uint256 x, uint256 y) pure returns (uint256 result) {
uint256 prod0;
uint256 prod1;
assembly ("memory-safe") {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
unchecked {
return prod0 / UNIT;
}
}
if (prod1 >= UNIT) {
revert PRBMath_MulDiv18_Overflow(x, y);
}
uint256 remainder;
assembly ("memory-safe") {
remainder := mulmod(x, y, UNIT)
result :=
mul(
or(
div(sub(prod0, remainder), UNIT_LPOTD),
mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, UNIT_LPOTD), UNIT_LPOTD), 1))
),
UNIT_INVERSE
)
}
}
/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev This is an extension of {mulDiv} for signed numbers, which works by computing the signs and the absolute values separately.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - None of the inputs can be `type(int256).min`.
/// - The result must fit in int256.
///
/// @param x The multiplicand as an int256.
/// @param y The multiplier as an int256.
/// @param denominator The divisor as an int256.
/// @return result The result as an int256.
/// @custom:smtchecker abstract-function-nondet
function mulDivSigned(int256 x, int256 y, int256 denominator) pure returns (int256 result) {
if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
revert PRBMath_MulDivSigned_InputTooSmall();
}
// Get hold of the absolute values of x, y and the denominator.
uint256 xAbs;
uint256 yAbs;
uint256 dAbs;
unchecked {
xAbs = x < 0 ? uint256(-x) : uint256(x);
yAbs = y < 0 ? uint256(-y) : uint256(y);
dAbs = denominator < 0 ? uint256(-denominator) : uint256(denominator);
}
// Compute the absolute value of x*y÷denominator. The result must fit in int256.
uint256 resultAbs = mulDiv(xAbs, yAbs, dAbs);
if (resultAbs > uint256(type(int256).max)) {
revert PRBMath_MulDivSigned_Overflow(x, y);
}
// Get the signs of x, y and the denominator.
uint256 sx;
uint256 sy;
uint256 sd;
assembly ("memory-safe") {
// "sgt" is the "signed greater than" assembly instruction and "sub(0,1)" is -1 in two's complement.
sx := sgt(x, sub(0, 1))
sy := sgt(y, sub(0, 1))
sd := sgt(denominator, sub(0, 1))
}
// XOR over sx, sy and sd. What this does is to check whether there are 1 or 3 negative signs in the inputs.
// If there are, the result should be negative. Otherwise, it should be positive.
unchecked {
result = sx ^ sy ^ sd == 0 ? -int256(resultAbs) : int256(resultAbs);
}
}
/// @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.
/// @custom:smtchecker abstract-function-nondet
function sqrt(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;
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { UD21x18 } from "./ValueType.sol";
/// @dev Euler's number as a UD21x18 number.
UD21x18 constant E = UD21x18.wrap(2_718281828459045235);
/// @dev The maximum value a UD21x18 number can have.
uint128 constant uMAX_UD21x18 = 340282366920938463463_374607431768211455;
UD21x18 constant MAX_UD21x18 = UD21x18.wrap(uMAX_UD21x18);
/// @dev PI as a UD21x18 number.
UD21x18 constant PI = UD21x18.wrap(3_141592653589793238);
/// @dev The unit number, which gives the decimal precision of UD21x18.
uint256 constant uUNIT = 1e18;
UD21x18 constant UNIT = UD21x18.wrap(1e18);
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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
pragma solidity >=0.8.19;
import { uMAX_UD60x18, uUNIT } from "./Constants.sol";
import { PRBMath_UD60x18_Convert_Overflow } from "./Errors.sol";
import { UD60x18 } from "./ValueType.sol";
/// @notice Converts a UD60x18 number to a simple integer by dividing it by `UNIT`.
/// @dev The result is rounded toward zero.
/// @param x The UD60x18 number to convert.
/// @return result The same number in basic integer form.
function convert(UD60x18 x) pure returns (uint256 result) {
result = UD60x18.unwrap(x) / uUNIT;
}
/// @notice Converts a simple integer to UD60x18 by multiplying it by `UNIT`.
///
/// @dev Requirements:
/// - x ≤ MAX_UD60x18 / UNIT
///
/// @param x The basic integer to convert.
/// @return result The same number converted to UD60x18.
function convert(uint256 x) pure returns (UD60x18 result) {
if (x > uMAX_UD60x18 / uUNIT) {
revert PRBMath_UD60x18_Convert_Overflow(x);
}
unchecked {
result = UD60x18.wrap(x * uUNIT);
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {ISummerToken} from "../interfaces/ISummerToken.sol";
import {IDecayController} from "../interfaces/IDecayController.sol";
import {IGovernanceRewardsManager} from "../interfaces/IGovernanceRewardsManager.sol";
/**
* @title DecayController
* @notice Manages decay updates for governance rewards and voting power
*/
abstract contract DecayController is IDecayController {
ISummerToken private immutable _summerToken;
IGovernanceRewardsManager private _rewardsManager;
constructor(address summerToken_) {
if (summerToken_ == address(0)) {
revert DecayController__ZeroAddress();
}
_summerToken = ISummerToken(summerToken_);
}
/**
* @notice Internal function to set the rewards manager address
* @dev This function must be called by the inheriting contract after deployment
* to avoid circular dependencies, as both DecayController and GovernanceRewardsManager
* need to reference each other. The pattern used is:
* 1. Deploy DecayController (with rewardsManager unset)
* 2. Deploy GovernanceRewardsManager (which can reference DecayController)
* 3. Call this function to set rewardsManager address
* @param rewardsManager_ Address of the GovernanceRewardsManager contract
*/
function _setRewardsManager(address rewardsManager_) internal {
if (rewardsManager_ == address(0)) {
revert DecayController__ZeroAddress();
}
if (address(_rewardsManager) != address(0)) {
revert DecayController__RewardsManagerAlreadySet();
}
_rewardsManager = IGovernanceRewardsManager(rewardsManager_);
}
function _updateDecay(address account) internal {
if (account != address(0)) {
_summerToken.updateDecayFactor(account);
_rewardsManager.updateSmoothedDecayFactor(account);
}
}
/**
* @notice Modifier to update decay before executing a function
* @param account Address to update decay for
* @dev Updates both base decay and smoothed decay factors
*/
modifier updateDecay(address account) {
_updateDecay(account);
_;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/DoubleEndedQueue.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
/**
* @dev A sequence of items with the ability to efficiently push and pop items (i.e. insert and remove) on both ends of
* the sequence (called front and back). Among other access patterns, it can be used to implement efficient LIFO and
* FIFO queues. Storage use is optimized, and all operations are O(1) constant time. This includes {clear}, given that
* the existing queue contents are left in storage.
*
* The struct is called `Bytes32Deque`. Other types can be cast to and from `bytes32`. This data structure can only be
* used in storage, and not in memory.
* ```solidity
* DoubleEndedQueue.Bytes32Deque queue;
* ```
*/
library DoubleEndedQueue {
/**
* @dev Indices are 128 bits so begin and end are packed in a single storage slot for efficient access.
*
* Struct members have an underscore prefix indicating that they are "private" and should not be read or written to
* directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
* lead to unexpected behavior.
*
* The first item is at data[begin] and the last item is at data[end - 1]. This range can wrap around.
*/
struct Bytes32Deque {
uint128 _begin;
uint128 _end;
mapping(uint128 index => bytes32) _data;
}
/**
* @dev Inserts an item at the end of the queue.
*
* Reverts with {Panic-RESOURCE_ERROR} if the queue is full.
*/
function pushBack(Bytes32Deque storage deque, bytes32 value) internal {
unchecked {
uint128 backIndex = deque._end;
if (backIndex + 1 == deque._begin) Panic.panic(Panic.RESOURCE_ERROR);
deque._data[backIndex] = value;
deque._end = backIndex + 1;
}
}
/**
* @dev Removes the item at the end of the queue and returns it.
*
* Reverts with {Panic-EMPTY_ARRAY_POP} if the queue is empty.
*/
function popBack(Bytes32Deque storage deque) internal returns (bytes32 value) {
unchecked {
uint128 backIndex = deque._end;
if (backIndex == deque._begin) Panic.panic(Panic.EMPTY_ARRAY_POP);
--backIndex;
value = deque._data[backIndex];
delete deque._data[backIndex];
deque._end = backIndex;
}
}
/**
* @dev Inserts an item at the beginning of the queue.
*
* Reverts with {Panic-RESOURCE_ERROR} if the queue is full.
*/
function pushFront(Bytes32Deque storage deque, bytes32 value) internal {
unchecked {
uint128 frontIndex = deque._begin - 1;
if (frontIndex == deque._end) Panic.panic(Panic.RESOURCE_ERROR);
deque._data[frontIndex] = value;
deque._begin = frontIndex;
}
}
/**
* @dev Removes the item at the beginning of the queue and returns it.
*
* Reverts with {Panic-EMPTY_ARRAY_POP} if the queue is empty.
*/
function popFront(Bytes32Deque storage deque) internal returns (bytes32 value) {
unchecked {
uint128 frontIndex = deque._begin;
if (frontIndex == deque._end) Panic.panic(Panic.EMPTY_ARRAY_POP);
value = deque._data[frontIndex];
delete deque._data[frontIndex];
deque._begin = frontIndex + 1;
}
}
/**
* @dev Returns the item at the beginning of the queue.
*
* Reverts with {Panic-ARRAY_OUT_OF_BOUNDS} if the queue is empty.
*/
function front(Bytes32Deque storage deque) internal view returns (bytes32 value) {
if (empty(deque)) Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
return deque._data[deque._begin];
}
/**
* @dev Returns the item at the end of the queue.
*
* Reverts with {Panic-ARRAY_OUT_OF_BOUNDS} if the queue is empty.
*/
function back(Bytes32Deque storage deque) internal view returns (bytes32 value) {
if (empty(deque)) Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
unchecked {
return deque._data[deque._end - 1];
}
}
/**
* @dev Return the item at a position in the queue given by `index`, with the first item at 0 and last item at
* `length(deque) - 1`.
*
* Reverts with {Panic-ARRAY_OUT_OF_BOUNDS} if the index is out of bounds.
*/
function at(Bytes32Deque storage deque, uint256 index) internal view returns (bytes32 value) {
if (index >= length(deque)) Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
// By construction, length is a uint128, so the check above ensures that index can be safely downcast to uint128
unchecked {
return deque._data[deque._begin + uint128(index)];
}
}
/**
* @dev Resets the queue back to being empty.
*
* NOTE: The current items are left behind in storage. This does not affect the functioning of the queue, but misses
* out on potential gas refunds.
*/
function clear(Bytes32Deque storage deque) internal {
deque._begin = 0;
deque._end = 0;
}
/**
* @dev Returns the number of items in the queue.
*/
function length(Bytes32Deque storage deque) internal view returns (uint256) {
unchecked {
return uint256(deque._end - deque._begin);
}
}
/**
* @dev Returns true if the queue is empty.
*/
function empty(Bytes32Deque storage deque) internal view returns (bool) {
return deque._end == deque._begin;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(
bytes32 hash,
bytes memory signature
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly ("memory-safe") {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[ERC-2098 short signatures]
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.20;
import {MessageHashUtils} from "./MessageHashUtils.sol";
import {ShortStrings, ShortString} from "../ShortStrings.sol";
import {IERC5267} from "../../interfaces/IERC5267.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP-712] is a standard for hashing and signing of typed structured data.
*
* The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
* encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
* does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
* produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP-712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
* separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
* separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
*
* @custom:oz-upgrades-unsafe-allow state-variable-immutable
*/
abstract contract EIP712 is IERC5267 {
using ShortStrings for *;
bytes32 private constant TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _cachedDomainSeparator;
uint256 private immutable _cachedChainId;
address private immutable _cachedThis;
bytes32 private immutable _hashedName;
bytes32 private immutable _hashedVersion;
ShortString private immutable _name;
ShortString private immutable _version;
string private _nameFallback;
string private _versionFallback;
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP-712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
_name = name.toShortStringWithFallback(_nameFallback);
_version = version.toShortStringWithFallback(_versionFallback);
_hashedName = keccak256(bytes(name));
_hashedVersion = keccak256(bytes(version));
_cachedChainId = block.chainid;
_cachedDomainSeparator = _buildDomainSeparator();
_cachedThis = address(this);
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
return _cachedDomainSeparator;
} else {
return _buildDomainSeparator();
}
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/**
* @dev See {IERC-5267}.
*/
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
return (
hex"0f", // 01111
_EIP712Name(),
_EIP712Version(),
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
/**
* @dev The name parameter for the EIP712 domain.
*
* NOTE: By default this function reads _name which is an immutable value.
* It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
*/
// solhint-disable-next-line func-name-mixedcase
function _EIP712Name() internal view returns (string memory) {
return _name.toStringWithFallback(_nameFallback);
}
/**
* @dev The version parameter for the EIP712 domain.
*
* NOTE: By default this function reads _version which is an immutable value.
* It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
*/
// solhint-disable-next-line func-name-mixedcase
function _EIP712Version() internal view returns (string memory) {
return _version.toStringWithFallback(_versionFallback);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 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);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.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}.
*
* 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 ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => 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 returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual 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 returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual 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 `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` 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 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* 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 `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` 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.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` 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.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC20Wrapper.sol)
pragma solidity ^0.8.20;
import {IERC20, IERC20Metadata, ERC20} from "../ERC20.sol";
import {SafeERC20} from "../utils/SafeERC20.sol";
/**
* @dev Extension of the ERC-20 token contract to support token wrapping.
*
* Users can deposit and withdraw "underlying tokens" and receive a matching number of "wrapped tokens". This is useful
* in conjunction with other modules. For example, combining this wrapping mechanism with {ERC20Votes} will allow the
* wrapping of an existing "basic" ERC-20 into a governance token.
*
* WARNING: Any mechanism in which the underlying token changes the {balanceOf} of an account without an explicit transfer
* may desynchronize this contract's supply and its underlying balance. Please exercise caution when wrapping tokens that
* may undercollateralize the wrapper (i.e. wrapper's total supply is higher than its underlying balance). See {_recover}
* for recovering value accrued to the wrapper.
*/
abstract contract ERC20Wrapper is ERC20 {
IERC20 private immutable _underlying;
/**
* @dev The underlying token couldn't be wrapped.
*/
error ERC20InvalidUnderlying(address token);
constructor(IERC20 underlyingToken) {
if (underlyingToken == this) {
revert ERC20InvalidUnderlying(address(this));
}
_underlying = underlyingToken;
}
/**
* @dev See {ERC20-decimals}.
*/
function decimals() public view virtual override returns (uint8) {
try IERC20Metadata(address(_underlying)).decimals() returns (uint8 value) {
return value;
} catch {
return super.decimals();
}
}
/**
* @dev Returns the address of the underlying ERC-20 token that is being wrapped.
*/
function underlying() public view returns (IERC20) {
return _underlying;
}
/**
* @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens.
*/
function depositFor(address account, uint256 value) public virtual returns (bool) {
address sender = _msgSender();
if (sender == address(this)) {
revert ERC20InvalidSender(address(this));
}
if (account == address(this)) {
revert ERC20InvalidReceiver(account);
}
SafeERC20.safeTransferFrom(_underlying, sender, address(this), value);
_mint(account, value);
return true;
}
/**
* @dev Allow a user to burn a number of wrapped tokens and withdraw the corresponding number of underlying tokens.
*/
function withdrawTo(address account, uint256 value) public virtual returns (bool) {
if (account == address(this)) {
revert ERC20InvalidReceiver(account);
}
_burn(_msgSender(), value);
SafeERC20.safeTransfer(_underlying, account, value);
return true;
}
/**
* @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake or acquired from
* rebasing mechanisms. Internal function that can be exposed with access control if desired.
*/
function _recover(address account) internal virtual returns (uint256) {
uint256 value = _underlying.balanceOf(address(this)) - totalSupply();
_mint(account, value);
return value;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @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 is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @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._positions[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 cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 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 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[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._positions[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;
assembly ("memory-safe") {
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;
assembly ("memory-safe") {
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;
assembly ("memory-safe") {
result := store
}
return result;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*
* _Available since v5.1._
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
/**
* @dev A necessary precompile is missing.
*/
error MissingPrecompile(address);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {ReentrancyGuardTransient} from "@summerfi/dependencies/openzeppelin-next/ReentrancyGuardTransient.sol";
import {StakingRewardsManagerBase} from "@summerfi/rewards-contracts/contracts/StakingRewardsManagerBase.sol";
import {IStakingRewardsManagerBase} from "@summerfi/rewards-contracts/interfaces/IStakingRewardsManagerBase.sol";
import {ProtocolAccessManaged} from "@summerfi/access-contracts/contracts/ProtocolAccessManaged.sol";
import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IGovernanceRewardsManager} from "../interfaces/IGovernanceRewardsManager.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {Constants} from "@summerfi/constants/Constants.sol";
import {ISummerToken} from "../interfaces/ISummerToken.sol";
import {DecayController} from "./DecayController.sol";
import {WrappedStakingToken} from "./WrappedStakingToken.sol";
/**
* @title GovernanceRewardsManager
* @notice Contract for managing governance rewards with multiple reward tokens in the Summer protocol
* @dev Implements IGovernanceRewardsManager interface and inherits from StakingRewardsManagerBase
*/
contract GovernanceRewardsManager is
IGovernanceRewardsManager,
StakingRewardsManagerBase,
DecayController
{
using SafeERC20 for IERC20;
using EnumerableSet for EnumerableSet.AddressSet;
/*//////////////////////////////////////////////////////////////
STATE VARIABLES
//////////////////////////////////////////////////////////////*/
/**
* @notice Smoothing factor base for decay calculations (1e18)
*/
uint256 public constant DECAY_SMOOTHING_FACTOR_BASE = Constants.WAD;
/**
* @notice Smoothing factor for decay calculations (0.2 * 1e18)
*/
uint256 public constant DECAY_SMOOTHING_FACTOR =
DECAY_SMOOTHING_FACTOR_BASE / 5; // represents 0.2
/**
* @notice Mapping of user addresses to their smoothed decay factors
*/
mapping(address account => uint256 smoothedDecayFactor)
public userSmoothedDecayFactor;
/**
* @notice Wrapped version of staking token for rewards
*/
address public immutable wrappedStakingToken;
/**
* @notice Updates rewards for an account before executing a function
* @param account The address of the account to update rewards for
* @dev Updates reward data for all reward tokens
*/
modifier updateReward(address account) override {
_updateReward(account);
_;
}
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
/**
* @notice Initializes the contract with the protocol access manager
* @param _stakingToken Address of the staking token
* @param accessManager Address of the ProtocolAccessManager contract
*/
constructor(
address _stakingToken,
address accessManager
) StakingRewardsManagerBase(accessManager) DecayController(_stakingToken) {
stakingToken = _stakingToken;
wrappedStakingToken = address(new WrappedStakingToken(stakingToken));
_setRewardsManager(address(this));
}
/*//////////////////////////////////////////////////////////////
MUTATIVE FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @inheritdoc IStakingRewardsManagerBase
function stakeOnBehalfOf(address, uint256) external pure override {
revert StakeOnBehalfOfNotSupported();
}
/**
* @notice No op function to satisfy interface requirements. Emits an event but performs no state changes.
* @dev This operation is not supported and will only emit an event
*/
function unstakeAndWithdrawOnBehalfOf(
address,
uint256,
bool
) external pure override {
revert UnstakeOnBehalfOfNotSupported();
}
/// @inheritdoc IStakingRewardsManagerBase
function stake(
uint256 amount
)
external
override(IStakingRewardsManagerBase, StakingRewardsManagerBase)
updateDecay(_msgSender())
updateReward(_msgSender())
{
_stake(_msgSender(), _msgSender(), amount);
}
/// @inheritdoc IStakingRewardsManagerBase
function unstake(
uint256 amount
)
external
override(IStakingRewardsManagerBase, StakingRewardsManagerBase)
updateReward(_msgSender())
updateDecay(_msgSender())
{
_unstake(_msgSender(), _msgSender(), amount);
}
/**
* @notice External function to update smoothed decay factor
* @param account The address to update
* @dev Only callable by the SummerToken or this contract
*/
function updateSmoothedDecayFactor(
address account
) external onlyDecayController {
_updateSmoothedDecayFactor(account);
}
/*//////////////////////////////////////////////////////////////
VIEWS
//////////////////////////////////////////////////////////////*/
/// @inheritdoc IGovernanceRewardsManager
function balanceOf(
address account
)
public
view
override(IGovernanceRewardsManager, StakingRewardsManagerBase)
returns (uint256)
{
return super.balanceOf(account);
}
/// @inheritdoc IStakingRewardsManagerBase
function earned(
address account,
address rewardToken
)
public
view
override(IStakingRewardsManagerBase, StakingRewardsManagerBase)
returns (uint256)
{
uint256 rawEarned = _earned(account, rewardToken);
uint256 latestSmoothedDecayFactor = _calculateSmoothedDecayFactor(
account
);
return (rawEarned * latestSmoothedDecayFactor) / Constants.WAD;
}
/// @inheritdoc IGovernanceRewardsManager
function calculateSmoothedDecayFactor(
address account
) external view returns (uint256) {
return _calculateSmoothedDecayFactor(account);
}
/*//////////////////////////////////////////////////////////////
INTERNAL
//////////////////////////////////////////////////////////////*/
/**
* @notice Updates the smoothed decay factor for a given account
* @param account The address of the account to update
*/
function _updateSmoothedDecayFactor(address account) internal {
if (account != address(0)) {
userSmoothedDecayFactor[account] = _calculateSmoothedDecayFactor(
account
);
}
}
/**
* @notice Calculates the smoothed decay factor for a given account without modifying state
* @param account The address of the account to calculate for
* @return The calculated smoothed decay factor
*/
function _calculateSmoothedDecayFactor(
address account
) internal view returns (uint256) {
uint256 currentDecayFactor = ISummerToken(address(stakingToken))
.getDecayFactor(account);
// If there's no existing smoothed factor, return the current factor
if (userSmoothedDecayFactor[account] == 0) {
return currentDecayFactor;
}
// Apply exponential moving average (EMA) smoothing
// Formula: EMA = α * currentValue + (1 - α) * previousEMA
// Where α is the smoothing factor (DECAY_SMOOTHING_FACTOR / DECAY_SMOOTHING_FACTOR_BASE)
return
((currentDecayFactor * DECAY_SMOOTHING_FACTOR) +
(userSmoothedDecayFactor[account] *
(DECAY_SMOOTHING_FACTOR_BASE - DECAY_SMOOTHING_FACTOR))) /
DECAY_SMOOTHING_FACTOR_BASE;
}
/**
* @notice Override _stake to wrap tokens
* @param from The address to transfer tokens from
* @param receiver The address to receive tokens
* @param amount The amount of tokens to transfer
*/
function _stake(
address from,
address receiver,
uint256 amount
) internal override {
if (receiver == address(0)) revert CannotStakeToZeroAddress();
if (amount == 0) revert CannotStakeZero();
if (address(stakingToken) == address(0)) {
revert StakingTokenNotInitialized();
}
address delegate = ISummerToken(address(stakingToken)).delegates(
receiver
);
if (delegate == address(0)) {
revert NotDelegated();
}
totalSupply += amount;
_balances[receiver] += amount;
IERC20(stakingToken).safeTransferFrom(from, address(this), amount);
IERC20(stakingToken).forceApprove(wrappedStakingToken, amount);
WrappedStakingToken(wrappedStakingToken).depositFor(
address(this),
amount
);
emit Staked(from, receiver, amount);
}
/**
* @notice Override _unstake to unwrap tokens
* @param from The address to transfer tokens from
* @param receiver The address to receive tokens
* @param amount The amount of tokens to transfer
*/
function _unstake(
address from,
address receiver,
uint256 amount
) internal virtual override {
if (amount == 0) revert CannotUnstakeZero();
address delegate = ISummerToken(address(stakingToken)).delegates(
receiver
);
if (delegate == address(0)) {
revert NotDelegated();
}
totalSupply -= amount;
_balances[from] -= amount;
// Send direct to receiver to avoid any interim state where voting units might be incorrectly calculated
WrappedStakingToken(wrappedStakingToken).withdrawTo(receiver, amount);
emit Unstaked(from, receiver, amount);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (governance/Governor.sol)
pragma solidity ^0.8.20;
import {IERC721Receiver} from "../token/ERC721/IERC721Receiver.sol";
import {IERC1155Receiver} from "../token/ERC1155/IERC1155Receiver.sol";
import {EIP712} from "../utils/cryptography/EIP712.sol";
import {SignatureChecker} from "../utils/cryptography/SignatureChecker.sol";
import {IERC165, ERC165} from "../utils/introspection/ERC165.sol";
import {SafeCast} from "../utils/math/SafeCast.sol";
import {DoubleEndedQueue} from "../utils/structs/DoubleEndedQueue.sol";
import {Address} from "../utils/Address.sol";
import {Context} from "../utils/Context.sol";
import {Nonces} from "../utils/Nonces.sol";
import {IGovernor, IERC6372} from "./IGovernor.sol";
/**
* @dev Core of the governance system, designed to be extended through various modules.
*
* This contract is abstract and requires several functions to be implemented in various modules:
*
* - A counting module must implement {quorum}, {_quorumReached}, {_voteSucceeded} and {_countVote}
* - A voting module must implement {_getVotes}
* - Additionally, {votingPeriod} must also be implemented
*/
abstract contract Governor is Context, ERC165, EIP712, Nonces, IGovernor, IERC721Receiver, IERC1155Receiver {
using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque;
bytes32 public constant BALLOT_TYPEHASH =
keccak256("Ballot(uint256 proposalId,uint8 support,address voter,uint256 nonce)");
bytes32 public constant EXTENDED_BALLOT_TYPEHASH =
keccak256(
"ExtendedBallot(uint256 proposalId,uint8 support,address voter,uint256 nonce,string reason,bytes params)"
);
struct ProposalCore {
address proposer;
uint48 voteStart;
uint32 voteDuration;
bool executed;
bool canceled;
uint48 etaSeconds;
}
bytes32 private constant ALL_PROPOSAL_STATES_BITMAP = bytes32((2 ** (uint8(type(ProposalState).max) + 1)) - 1);
string private _name;
mapping(uint256 proposalId => ProposalCore) private _proposals;
// This queue keeps track of the governor operating on itself. Calls to functions protected by the {onlyGovernance}
// modifier needs to be whitelisted in this queue. Whitelisting is set in {execute}, consumed by the
// {onlyGovernance} modifier and eventually reset after {_executeOperations} completes. This ensures that the
// execution of {onlyGovernance} protected calls can only be achieved through successful proposals.
DoubleEndedQueue.Bytes32Deque private _governanceCall;
/**
* @dev Restricts a function so it can only be executed through governance proposals. For example, governance
* parameter setters in {GovernorSettings} are protected using this modifier.
*
* The governance executing address may be different from the Governor's own address, for example it could be a
* timelock. This can be customized by modules by overriding {_executor}. The executor is only able to invoke these
* functions during the execution of the governor's {execute} function, and not under any other circumstances. Thus,
* for example, additional timelock proposers are not able to change governance parameters without going through the
* governance protocol (since v4.6).
*/
modifier onlyGovernance() {
_checkGovernance();
_;
}
/**
* @dev Sets the value for {name} and {version}
*/
constructor(string memory name_) EIP712(name_, version()) {
_name = name_;
}
/**
* @dev Function to receive ETH that will be handled by the governor (disabled if executor is a third party contract)
*/
receive() external payable virtual {
if (_executor() != address(this)) {
revert GovernorDisabledDeposit();
}
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
return
interfaceId == type(IGovernor).interfaceId ||
interfaceId == type(IERC1155Receiver).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IGovernor-name}.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev See {IGovernor-version}.
*/
function version() public view virtual returns (string memory) {
return "1";
}
/**
* @dev See {IGovernor-hashProposal}.
*
* The proposal id is produced by hashing the ABI encoded `targets` array, the `values` array, the `calldatas` array
* and the descriptionHash (bytes32 which itself is the keccak256 hash of the description string). This proposal id
* can be produced from the proposal data which is part of the {ProposalCreated} event. It can even be computed in
* advance, before the proposal is submitted.
*
* Note that the chainId and the governor address are not part of the proposal id computation. Consequently, the
* same proposal (with same operation and same description) will have the same id if submitted on multiple governors
* across multiple networks. This also means that in order to execute the same operation twice (on the same
* governor) the proposer will have to change the description in order to avoid proposal id conflicts.
*/
function hashProposal(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public pure virtual returns (uint256) {
return uint256(keccak256(abi.encode(targets, values, calldatas, descriptionHash)));
}
/**
* @dev See {IGovernor-state}.
*/
function state(uint256 proposalId) public view virtual returns (ProposalState) {
// We read the struct fields into the stack at once so Solidity emits a single SLOAD
ProposalCore storage proposal = _proposals[proposalId];
bool proposalExecuted = proposal.executed;
bool proposalCanceled = proposal.canceled;
if (proposalExecuted) {
return ProposalState.Executed;
}
if (proposalCanceled) {
return ProposalState.Canceled;
}
uint256 snapshot = proposalSnapshot(proposalId);
if (snapshot == 0) {
revert GovernorNonexistentProposal(proposalId);
}
uint256 currentTimepoint = clock();
if (snapshot >= currentTimepoint) {
return ProposalState.Pending;
}
uint256 deadline = proposalDeadline(proposalId);
if (deadline >= currentTimepoint) {
return ProposalState.Active;
} else if (!_quorumReached(proposalId) || !_voteSucceeded(proposalId)) {
return ProposalState.Defeated;
} else if (proposalEta(proposalId) == 0) {
return ProposalState.Succeeded;
} else {
return ProposalState.Queued;
}
}
/**
* @dev See {IGovernor-proposalThreshold}.
*/
function proposalThreshold() public view virtual returns (uint256) {
return 0;
}
/**
* @dev See {IGovernor-proposalSnapshot}.
*/
function proposalSnapshot(uint256 proposalId) public view virtual returns (uint256) {
return _proposals[proposalId].voteStart;
}
/**
* @dev See {IGovernor-proposalDeadline}.
*/
function proposalDeadline(uint256 proposalId) public view virtual returns (uint256) {
return _proposals[proposalId].voteStart + _proposals[proposalId].voteDuration;
}
/**
* @dev See {IGovernor-proposalProposer}.
*/
function proposalProposer(uint256 proposalId) public view virtual returns (address) {
return _proposals[proposalId].proposer;
}
/**
* @dev See {IGovernor-proposalEta}.
*/
function proposalEta(uint256 proposalId) public view virtual returns (uint256) {
return _proposals[proposalId].etaSeconds;
}
/**
* @dev See {IGovernor-proposalNeedsQueuing}.
*/
function proposalNeedsQueuing(uint256) public view virtual returns (bool) {
return false;
}
/**
* @dev Reverts if the `msg.sender` is not the executor. In case the executor is not this contract
* itself, the function reverts if `msg.data` is not whitelisted as a result of an {execute}
* operation. See {onlyGovernance}.
*/
function _checkGovernance() internal virtual {
if (_executor() != _msgSender()) {
revert GovernorOnlyExecutor(_msgSender());
}
if (_executor() != address(this)) {
bytes32 msgDataHash = keccak256(_msgData());
// loop until popping the expected operation - throw if deque is empty (operation not authorized)
while (_governanceCall.popFront() != msgDataHash) {}
}
}
/**
* @dev Amount of votes already cast passes the threshold limit.
*/
function _quorumReached(uint256 proposalId) internal view virtual returns (bool);
/**
* @dev Is the proposal successful or not.
*/
function _voteSucceeded(uint256 proposalId) internal view virtual returns (bool);
/**
* @dev Get the voting weight of `account` at a specific `timepoint`, for a vote as described by `params`.
*/
function _getVotes(address account, uint256 timepoint, bytes memory params) internal view virtual returns (uint256);
/**
* @dev Register a vote for `proposalId` by `account` with a given `support`, voting `weight` and voting `params`.
*
* Note: Support is generic and can represent various things depending on the voting system used.
*/
function _countVote(
uint256 proposalId,
address account,
uint8 support,
uint256 totalWeight,
bytes memory params
) internal virtual returns (uint256);
/**
* @dev Default additional encoded parameters used by castVote methods that don't include them
*
* Note: Should be overridden by specific implementations to use an appropriate value, the
* meaning of the additional params, in the context of that implementation
*/
function _defaultParams() internal view virtual returns (bytes memory) {
return "";
}
/**
* @dev See {IGovernor-propose}. This function has opt-in frontrunning protection, described in {_isValidDescriptionForProposer}.
*/
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public virtual returns (uint256) {
address proposer = _msgSender();
// check description restriction
if (!_isValidDescriptionForProposer(proposer, description)) {
revert GovernorRestrictedProposer(proposer);
}
// check proposal threshold
uint256 votesThreshold = proposalThreshold();
if (votesThreshold > 0) {
uint256 proposerVotes = getVotes(proposer, clock() - 1);
if (proposerVotes < votesThreshold) {
revert GovernorInsufficientProposerVotes(proposer, proposerVotes, votesThreshold);
}
}
return _propose(targets, values, calldatas, description, proposer);
}
/**
* @dev Internal propose mechanism. Can be overridden to add more logic on proposal creation.
*
* Emits a {IGovernor-ProposalCreated} event.
*/
function _propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description,
address proposer
) internal virtual returns (uint256 proposalId) {
proposalId = hashProposal(targets, values, calldatas, keccak256(bytes(description)));
if (targets.length != values.length || targets.length != calldatas.length || targets.length == 0) {
revert GovernorInvalidProposalLength(targets.length, calldatas.length, values.length);
}
if (_proposals[proposalId].voteStart != 0) {
revert GovernorUnexpectedProposalState(proposalId, state(proposalId), bytes32(0));
}
uint256 snapshot = clock() + votingDelay();
uint256 duration = votingPeriod();
ProposalCore storage proposal = _proposals[proposalId];
proposal.proposer = proposer;
proposal.voteStart = SafeCast.toUint48(snapshot);
proposal.voteDuration = SafeCast.toUint32(duration);
emit ProposalCreated(
proposalId,
proposer,
targets,
values,
new string[](targets.length),
calldatas,
snapshot,
snapshot + duration,
description
);
// Using a named return variable to avoid stack too deep errors
}
/**
* @dev See {IGovernor-queue}.
*/
function queue(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public virtual returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
_validateStateBitmap(proposalId, _encodeStateBitmap(ProposalState.Succeeded));
uint48 etaSeconds = _queueOperations(proposalId, targets, values, calldatas, descriptionHash);
if (etaSeconds != 0) {
_proposals[proposalId].etaSeconds = etaSeconds;
emit ProposalQueued(proposalId, etaSeconds);
} else {
revert GovernorQueueNotImplemented();
}
return proposalId;
}
/**
* @dev Internal queuing mechanism. Can be overridden (without a super call) to modify the way queuing is
* performed (for example adding a vault/timelock).
*
* This is empty by default, and must be overridden to implement queuing.
*
* This function returns a timestamp that describes the expected ETA for execution. If the returned value is 0
* (which is the default value), the core will consider queueing did not succeed, and the public {queue} function
* will revert.
*
* NOTE: Calling this function directly will NOT check the current state of the proposal, or emit the
* `ProposalQueued` event. Queuing a proposal should be done using {queue}.
*/
function _queueOperations(
uint256 /*proposalId*/,
address[] memory /*targets*/,
uint256[] memory /*values*/,
bytes[] memory /*calldatas*/,
bytes32 /*descriptionHash*/
) internal virtual returns (uint48) {
return 0;
}
/**
* @dev See {IGovernor-execute}.
*/
function execute(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public payable virtual returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
_validateStateBitmap(
proposalId,
_encodeStateBitmap(ProposalState.Succeeded) | _encodeStateBitmap(ProposalState.Queued)
);
// mark as executed before calls to avoid reentrancy
_proposals[proposalId].executed = true;
// before execute: register governance call in queue.
if (_executor() != address(this)) {
for (uint256 i = 0; i < targets.length; ++i) {
if (targets[i] == address(this)) {
_governanceCall.pushBack(keccak256(calldatas[i]));
}
}
}
_executeOperations(proposalId, targets, values, calldatas, descriptionHash);
// after execute: cleanup governance call queue.
if (_executor() != address(this) && !_governanceCall.empty()) {
_governanceCall.clear();
}
emit ProposalExecuted(proposalId);
return proposalId;
}
/**
* @dev Internal execution mechanism. Can be overridden (without a super call) to modify the way execution is
* performed (for example adding a vault/timelock).
*
* NOTE: Calling this function directly will NOT check the current state of the proposal, set the executed flag to
* true or emit the `ProposalExecuted` event. Executing a proposal should be done using {execute} or {_execute}.
*/
function _executeOperations(
uint256 /* proposalId */,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 /*descriptionHash*/
) internal virtual {
for (uint256 i = 0; i < targets.length; ++i) {
(bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]);
Address.verifyCallResult(success, returndata);
}
}
/**
* @dev See {IGovernor-cancel}.
*/
function cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public virtual returns (uint256) {
// The proposalId will be recomputed in the `_cancel` call further down. However we need the value before we
// do the internal call, because we need to check the proposal state BEFORE the internal `_cancel` call
// changes it. The `hashProposal` duplication has a cost that is limited, and that we accept.
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
// public cancel restrictions (on top of existing _cancel restrictions).
_validateStateBitmap(proposalId, _encodeStateBitmap(ProposalState.Pending));
if (_msgSender() != proposalProposer(proposalId)) {
revert GovernorOnlyProposer(_msgSender());
}
return _cancel(targets, values, calldatas, descriptionHash);
}
/**
* @dev Internal cancel mechanism with minimal restrictions. A proposal can be cancelled in any state other than
* Canceled, Expired, or Executed. Once cancelled a proposal can't be re-submitted.
*
* Emits a {IGovernor-ProposalCanceled} event.
*/
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
_validateStateBitmap(
proposalId,
ALL_PROPOSAL_STATES_BITMAP ^
_encodeStateBitmap(ProposalState.Canceled) ^
_encodeStateBitmap(ProposalState.Expired) ^
_encodeStateBitmap(ProposalState.Executed)
);
_proposals[proposalId].canceled = true;
emit ProposalCanceled(proposalId);
return proposalId;
}
/**
* @dev See {IGovernor-getVotes}.
*/
function getVotes(address account, uint256 timepoint) public view virtual returns (uint256) {
return _getVotes(account, timepoint, _defaultParams());
}
/**
* @dev See {IGovernor-getVotesWithParams}.
*/
function getVotesWithParams(
address account,
uint256 timepoint,
bytes memory params
) public view virtual returns (uint256) {
return _getVotes(account, timepoint, params);
}
/**
* @dev See {IGovernor-castVote}.
*/
function castVote(uint256 proposalId, uint8 support) public virtual returns (uint256) {
address voter = _msgSender();
return _castVote(proposalId, voter, support, "");
}
/**
* @dev See {IGovernor-castVoteWithReason}.
*/
function castVoteWithReason(
uint256 proposalId,
uint8 support,
string calldata reason
) public virtual returns (uint256) {
address voter = _msgSender();
return _castVote(proposalId, voter, support, reason);
}
/**
* @dev See {IGovernor-castVoteWithReasonAndParams}.
*/
function castVoteWithReasonAndParams(
uint256 proposalId,
uint8 support,
string calldata reason,
bytes memory params
) public virtual returns (uint256) {
address voter = _msgSender();
return _castVote(proposalId, voter, support, reason, params);
}
/**
* @dev See {IGovernor-castVoteBySig}.
*/
function castVoteBySig(
uint256 proposalId,
uint8 support,
address voter,
bytes memory signature
) public virtual returns (uint256) {
bool valid = SignatureChecker.isValidSignatureNow(
voter,
_hashTypedDataV4(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support, voter, _useNonce(voter)))),
signature
);
if (!valid) {
revert GovernorInvalidSignature(voter);
}
return _castVote(proposalId, voter, support, "");
}
/**
* @dev See {IGovernor-castVoteWithReasonAndParamsBySig}.
*/
function castVoteWithReasonAndParamsBySig(
uint256 proposalId,
uint8 support,
address voter,
string calldata reason,
bytes memory params,
bytes memory signature
) public virtual returns (uint256) {
bool valid = SignatureChecker.isValidSignatureNow(
voter,
_hashTypedDataV4(
keccak256(
abi.encode(
EXTENDED_BALLOT_TYPEHASH,
proposalId,
support,
voter,
_useNonce(voter),
keccak256(bytes(reason)),
keccak256(params)
)
)
),
signature
);
if (!valid) {
revert GovernorInvalidSignature(voter);
}
return _castVote(proposalId, voter, support, reason, params);
}
/**
* @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been cast yet, retrieve
* voting weight using {IGovernor-getVotes} and call the {_countVote} internal function. Uses the _defaultParams().
*
* Emits a {IGovernor-VoteCast} event.
*/
function _castVote(
uint256 proposalId,
address account,
uint8 support,
string memory reason
) internal virtual returns (uint256) {
return _castVote(proposalId, account, support, reason, _defaultParams());
}
/**
* @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been cast yet, retrieve
* voting weight using {IGovernor-getVotes} and call the {_countVote} internal function.
*
* Emits a {IGovernor-VoteCast} event.
*/
function _castVote(
uint256 proposalId,
address account,
uint8 support,
string memory reason,
bytes memory params
) internal virtual returns (uint256) {
_validateStateBitmap(proposalId, _encodeStateBitmap(ProposalState.Active));
uint256 totalWeight = _getVotes(account, proposalSnapshot(proposalId), params);
uint256 votedWeight = _countVote(proposalId, account, support, totalWeight, params);
if (params.length == 0) {
emit VoteCast(account, proposalId, support, votedWeight, reason);
} else {
emit VoteCastWithParams(account, proposalId, support, votedWeight, reason, params);
}
return votedWeight;
}
/**
* @dev Relays a transaction or function call to an arbitrary target. In cases where the governance executor
* is some contract other than the governor itself, like when using a timelock, this function can be invoked
* in a governance proposal to recover tokens or Ether that was sent to the governor contract by mistake.
* Note that if the executor is simply the governor itself, use of `relay` is redundant.
*/
function relay(address target, uint256 value, bytes calldata data) external payable virtual onlyGovernance {
(bool success, bytes memory returndata) = target.call{value: value}(data);
Address.verifyCallResult(success, returndata);
}
/**
* @dev Address through which the governor executes action. Will be overloaded by module that execute actions
* through another contract such as a timelock.
*/
function _executor() internal view virtual returns (address) {
return address(this);
}
/**
* @dev See {IERC721Receiver-onERC721Received}.
* Receiving tokens is disabled if the governance executor is other than the governor itself (eg. when using with a timelock).
*/
function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
if (_executor() != address(this)) {
revert GovernorDisabledDeposit();
}
return this.onERC721Received.selector;
}
/**
* @dev See {IERC1155Receiver-onERC1155Received}.
* Receiving tokens is disabled if the governance executor is other than the governor itself (eg. when using with a timelock).
*/
function onERC1155Received(address, address, uint256, uint256, bytes memory) public virtual returns (bytes4) {
if (_executor() != address(this)) {
revert GovernorDisabledDeposit();
}
return this.onERC1155Received.selector;
}
/**
* @dev See {IERC1155Receiver-onERC1155BatchReceived}.
* Receiving tokens is disabled if the governance executor is other than the governor itself (eg. when using with a timelock).
*/
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) public virtual returns (bytes4) {
if (_executor() != address(this)) {
revert GovernorDisabledDeposit();
}
return this.onERC1155BatchReceived.selector;
}
/**
* @dev Encodes a `ProposalState` into a `bytes32` representation where each bit enabled corresponds to
* the underlying position in the `ProposalState` enum. For example:
*
* 0x000...10000
* ^^^^^^------ ...
* ^----- Succeeded
* ^---- Defeated
* ^--- Canceled
* ^-- Active
* ^- Pending
*/
function _encodeStateBitmap(ProposalState proposalState) internal pure returns (bytes32) {
return bytes32(1 << uint8(proposalState));
}
/**
* @dev Check that the current state of a proposal matches the requirements described by the `allowedStates` bitmap.
* This bitmap should be built using `_encodeStateBitmap`.
*
* If requirements are not met, reverts with a {GovernorUnexpectedProposalState} error.
*/
function _validateStateBitmap(uint256 proposalId, bytes32 allowedStates) private view returns (ProposalState) {
ProposalState currentState = state(proposalId);
if (_encodeStateBitmap(currentState) & allowedStates == bytes32(0)) {
revert GovernorUnexpectedProposalState(proposalId, currentState, allowedStates);
}
return currentState;
}
/*
* @dev Check if the proposer is authorized to submit a proposal with the given description.
*
* If the proposal description ends with `#proposer=0x???`, where `0x???` is an address written as a hex string
* (case insensitive), then the submission of this proposal will only be authorized to said address.
*
* This is used for frontrunning protection. By adding this pattern at the end of their proposal, one can ensure
* that no other address can submit the same proposal. An attacker would have to either remove or change that part,
* which would result in a different proposal id.
*
* If the description does not match this pattern, it is unrestricted and anyone can submit it. This includes:
* - If the `0x???` part is not a valid hex string.
* - If the `0x???` part is a valid hex string, but does not contain exactly 40 hex digits.
* - If it ends with the expected suffix followed by newlines or other whitespace.
* - If it ends with some other similar suffix, e.g. `#other=abc`.
* - If it does not end with any such suffix.
*/
function _isValidDescriptionForProposer(
address proposer,
string memory description
) internal view virtual returns (bool) {
uint256 len = bytes(description).length;
// Length is too short to contain a valid proposer suffix
if (len < 52) {
return true;
}
// Extract what would be the `#proposer=0x` marker beginning the suffix
bytes12 marker;
assembly ("memory-safe") {
// - Start of the string contents in memory = description + 32
// - First character of the marker = len - 52
// - Length of "#proposer=0x0000000000000000000000000000000000000000" = 52
// - We read the memory word starting at the first character of the marker:
// - (description + 32) + (len - 52) = description + (len - 20)
// - Note: Solidity will ignore anything past the first 12 bytes
marker := mload(add(description, sub(len, 20)))
}
// If the marker is not found, there is no proposer suffix to check
if (marker != bytes12("#proposer=0x")) {
return true;
}
// Parse the 40 characters following the marker as uint160
uint160 recovered = 0;
for (uint256 i = len - 40; i < len; ++i) {
(bool isHex, uint8 value) = _tryHexToUint(bytes(description)[i]);
// If any of the characters is not a hex digit, ignore the suffix entirely
if (!isHex) {
return true;
}
recovered = (recovered << 4) | value;
}
return recovered == uint160(proposer);
}
/**
* @dev Try to parse a character from a string as a hex value. Returns `(true, value)` if the char is in
* `[0-9a-fA-F]` and `(false, 0)` otherwise. Value is guaranteed to be in the range `0 <= value < 16`
*/
function _tryHexToUint(bytes1 char) private pure returns (bool isHex, uint8 value) {
uint8 c = uint8(char);
unchecked {
// Case 0-9
if (47 < c && c < 58) {
return (true, c - 48);
}
// Case A-F
else if (64 < c && c < 71) {
return (true, c - 55);
}
// Case a-f
else if (96 < c && c < 103) {
return (true, c - 87);
}
// Else: not a hex char
else {
return (false, 0);
}
}
}
/**
* @inheritdoc IERC6372
*/
function clock() public view virtual returns (uint48);
/**
* @inheritdoc IERC6372
*/
// solhint-disable-next-line func-name-mixedcase
function CLOCK_MODE() public view virtual returns (string memory);
/**
* @inheritdoc IGovernor
*/
function votingDelay() public view virtual returns (uint256);
/**
* @inheritdoc IGovernor
*/
function votingPeriod() public view virtual returns (uint256);
/**
* @inheritdoc IGovernor
*/
function quorum(uint256 timepoint) public view virtual returns (uint256);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (governance/extensions/GovernorVotes.sol)
pragma solidity ^0.8.20;
import {Governor} from "../Governor.sol";
import {IVotes} from "../utils/IVotes.sol";
import {IERC5805} from "../../interfaces/IERC5805.sol";
import {SafeCast} from "../../utils/math/SafeCast.sol";
import {Time} from "../../utils/types/Time.sol";
/**
* @dev Extension of {Governor} for voting weight extraction from an {ERC20Votes} token, or since v4.5 an {ERC721Votes}
* token.
*/
abstract contract GovernorVotes is Governor {
IERC5805 private immutable _token;
constructor(IVotes tokenAddress) {
_token = IERC5805(address(tokenAddress));
}
/**
* @dev The token that voting power is sourced from.
*/
function token() public view virtual returns (IERC5805) {
return _token;
}
/**
* @dev Clock (as specified in ERC-6372) is set to match the token's clock. Fallback to block numbers if the token
* does not implement ERC-6372.
*/
function clock() public view virtual override returns (uint48) {
try token().clock() returns (uint48 timepoint) {
return timepoint;
} catch {
return Time.blockNumber();
}
}
/**
* @dev Machine-readable description of the clock as specified in ERC-6372.
*/
// solhint-disable-next-line func-name-mixedcase
function CLOCK_MODE() public view virtual override returns (string memory) {
try token().CLOCK_MODE() returns (string memory clockmode) {
return clockmode;
} catch {
return "mode=blocknumber&from=default";
}
}
/**
* Read the voting weight from the token's built in snapshot mechanism (see {Governor-_getVotes}).
*/
function _getVotes(
address account,
uint256 timepoint,
bytes memory /*params*/
) internal view virtual override returns (uint256) {
return token().getPastVotes(account, timepoint);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { wrap } from "./Casting.sol";
import { SD59x18 } from "./ValueType.sol";
/// @notice Implements the checked addition operation (+) in the SD59x18 type.
function add(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
return wrap(x.unwrap() + y.unwrap());
}
/// @notice Implements the AND (&) bitwise operation in the SD59x18 type.
function and(SD59x18 x, int256 bits) pure returns (SD59x18 result) {
return wrap(x.unwrap() & bits);
}
/// @notice Implements the AND (&) bitwise operation in the SD59x18 type.
function and2(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
return wrap(x.unwrap() & y.unwrap());
}
/// @notice Implements the equal (=) operation in the SD59x18 type.
function eq(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() == y.unwrap();
}
/// @notice Implements the greater than operation (>) in the SD59x18 type.
function gt(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() > y.unwrap();
}
/// @notice Implements the greater than or equal to operation (>=) in the SD59x18 type.
function gte(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() >= y.unwrap();
}
/// @notice Implements a zero comparison check function in the SD59x18 type.
function isZero(SD59x18 x) pure returns (bool result) {
result = x.unwrap() == 0;
}
/// @notice Implements the left shift operation (<<) in the SD59x18 type.
function lshift(SD59x18 x, uint256 bits) pure returns (SD59x18 result) {
result = wrap(x.unwrap() << bits);
}
/// @notice Implements the lower than operation (<) in the SD59x18 type.
function lt(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() < y.unwrap();
}
/// @notice Implements the lower than or equal to operation (<=) in the SD59x18 type.
function lte(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() <= y.unwrap();
}
/// @notice Implements the unchecked modulo operation (%) in the SD59x18 type.
function mod(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
result = wrap(x.unwrap() % y.unwrap());
}
/// @notice Implements the not equal operation (!=) in the SD59x18 type.
function neq(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() != y.unwrap();
}
/// @notice Implements the NOT (~) bitwise operation in the SD59x18 type.
function not(SD59x18 x) pure returns (SD59x18 result) {
result = wrap(~x.unwrap());
}
/// @notice Implements the OR (|) bitwise operation in the SD59x18 type.
function or(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
result = wrap(x.unwrap() | y.unwrap());
}
/// @notice Implements the right shift operation (>>) in the SD59x18 type.
function rshift(SD59x18 x, uint256 bits) pure returns (SD59x18 result) {
result = wrap(x.unwrap() >> bits);
}
/// @notice Implements the checked subtraction operation (-) in the SD59x18 type.
function sub(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
result = wrap(x.unwrap() - y.unwrap());
}
/// @notice Implements the checked unary minus operation (-) in the SD59x18 type.
function unary(SD59x18 x) pure returns (SD59x18 result) {
result = wrap(-x.unwrap());
}
/// @notice Implements the unchecked addition operation (+) in the SD59x18 type.
function uncheckedAdd(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
unchecked {
result = wrap(x.unwrap() + y.unwrap());
}
}
/// @notice Implements the unchecked subtraction operation (-) in the SD59x18 type.
function uncheckedSub(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
unchecked {
result = wrap(x.unwrap() - y.unwrap());
}
}
/// @notice Implements the unchecked unary minus operation (-) in the SD59x18 type.
function uncheckedUnary(SD59x18 x) pure returns (SD59x18 result) {
unchecked {
result = wrap(-x.unwrap());
}
}
/// @notice Implements the XOR (^) bitwise operation in the SD59x18 type.
function xor(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
result = wrap(x.unwrap() ^ y.unwrap());
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC-165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @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.
*/
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. This account bears the admin role (for the granted role).
* Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
*/
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 `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
/**
* @title IAccessControlErrors
* @dev This file contains custom error definitions for access control in the system.
* @notice These custom errors provide more gas-efficient and informative error handling
* compared to traditional require statements with string messages.
*/
interface IAccessControlErrors {
/**
* @notice Thrown when a caller does not have the required role.
*/
error CallerIsNotContractSpecificRole(address caller, bytes32 role);
/**
* @notice Thrown when a caller is not the curator.
*/
error CallerIsNotCurator(address caller);
/**
* @notice Thrown when a caller is not the governor.
*/
error CallerIsNotGovernor(address caller);
/**
* @notice Thrown when a caller is not a keeper.
*/
error CallerIsNotKeeper(address caller);
/**
* @notice Thrown when a caller is not a super keeper.
*/
error CallerIsNotSuperKeeper(address caller);
/**
* @notice Thrown when a caller is not the commander.
*/
error CallerIsNotCommander(address caller);
/**
* @notice Thrown when a caller is neither the Raft nor the commander.
*/
error CallerIsNotRaftOrCommander(address caller);
/**
* @notice Thrown when a caller is not the Raft.
*/
error CallerIsNotRaft(address caller);
/**
* @notice Thrown when a caller is not an admin.
*/
error CallerIsNotAdmin(address caller);
/**
* @notice Thrown when a caller is not the guardian.
*/
error CallerIsNotGuardian(address caller);
/**
* @notice Thrown when a caller is not the guardian or governor.
*/
error CallerIsNotGuardianOrGovernor(address caller);
/**
* @notice Thrown when a caller is not the decay controller.
*/
error CallerIsNotDecayController(address caller);
/**
* @notice Thrown when a caller is not authorized to board.
*/
error CallerIsNotAuthorizedToBoard(address caller);
/**
* @notice Thrown when direct grant is disabled.
*/
error DirectGrantIsDisabled(address caller);
/**
* @notice Thrown when direct revoke is disabled.
*/
error DirectRevokeIsDisabled(address caller);
/**
* @notice Thrown when an invalid access manager address is provided.
*/
error InvalidAccessManagerAddress(address invalidAddress);
/**
* @notice Error thrown when a caller is not the Foundation
* @param caller The address that attempted the operation
*/
error CallerIsNotFoundation(address caller);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
/**
* @title IDecayController
* @notice Interface for the DecayController contract that manages decay updates
*/
interface IDecayController {
/**
* @notice Error thrown when a zero address is provided for the summer token
*/
error DecayController__ZeroAddress();
/**
* @notice Error thrown when the rewards manager is already set
*/
error DecayController__RewardsManagerAlreadySet();
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Interface that must be implemented by smart contracts in order to receive
* ERC-1155 token transfers.
*/
interface IERC1155Receiver is IERC165 {
/**
* @dev Handles the receipt of a single ERC-1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC-1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*/
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 (last updated v5.0.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* 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[ERC 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 v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
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 value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` 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 value) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC-20 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 v5.0.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.20;
interface IERC5267 {
/**
* @dev MAY be emitted to signal that the domain could have changed.
*/
event EIP712DomainChanged();
/**
* @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
* signature.
*/
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5805.sol)
pragma solidity ^0.8.20;
import {IVotes} from "../governance/utils/IVotes.sol";
import {IERC6372} from "./IERC6372.sol";
interface IERC5805 is IERC6372, IVotes {}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC6372.sol)
pragma solidity ^0.8.20;
interface IERC6372 {
/**
* @dev Clock used for flagging checkpoints. Can be overridden to implement timestamp based checkpoints (and voting).
*/
function clock() external view returns (uint48);
/**
* @dev Description of the clock
*/
// solhint-disable-next-line func-name-mixedcase
function CLOCK_MODE() external view returns (string memory);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC-721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC-721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IGovernanceRewardsManagerErrors} from "../errors/IGovernanceRewardsManagerErrors.sol";
import {IStakingRewardsManagerBase} from "@summerfi/rewards-contracts/interfaces/IStakingRewardsManagerBase.sol";
/**
* @title IGovernanceRewardsManager
* @notice Interface for the GovernanceRewardsManager contract
* @dev Manages staking and distribution of multiple reward tokens
*/
interface IGovernanceRewardsManager is
IStakingRewardsManagerBase,
IGovernanceRewardsManagerErrors
{
/**
* @notice Returns the wrapped staking token
* @return The wrapped staking token
*/
function wrappedStakingToken() external view returns (address);
/**
* @notice Emitted when unstakeAndWithdrawOnBehalfOf is called (operation not supported)
* @param owner The address that owns the staked tokens
* @param receiver The address that would have received the unstaked tokens
* @param amount The amount of tokens that was attempted to be unstaked
*/
event UnstakeOnBehalfOfIgnored(
address indexed owner,
address indexed receiver,
uint256 amount
);
/**
* @notice Returns the balance of staked tokens for an account
* @param account The address of the staker
* @return The amount of tokens staked by the account
*/
function balanceOf(address account) external view returns (uint256);
/**
* @notice Updates the smoothed decay factor for an account
* @param account The address to update the smoothed decay factor for
*/
function updateSmoothedDecayFactor(address account) external;
/**
* @notice Calculates the smoothed decay factor for a given account without modifying state
* @param account The address of the account to calculate for
* @return The calculated smoothed decay factor
*/
function calculateSmoothedDecayFactor(
address account
) external view returns (uint256);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
/**
* @title IGovernanceRewardsManagerErrors
* @notice Interface defining custom errors for the Governance Rewards Manager
*/
interface IGovernanceRewardsManagerErrors {
/**
* @notice Thrown when the caller is not the staking token
* @dev Used to restrict certain functions to only be callable by the staking token contract
*/
error InvalidCaller();
/**
* @notice Thrown when the stakeOnBehalfOf function is called (operation not supported)
*/
error StakeOnBehalfOfNotSupported();
/**
* @notice Thrown when the UnstakeOnBehalfOfNotSupported function is called (operation not supported)
*/
error UnstakeOnBehalfOfNotSupported();
/**
* @notice Thrown when the caller is not delegated
*/
error NotDelegated();
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (governance/IGovernor.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../interfaces/IERC165.sol";
import {IERC6372} from "../interfaces/IERC6372.sol";
/**
* @dev Interface of the {Governor} core.
*
* NOTE: Event parameters lack the `indexed` keyword for compatibility with GovernorBravo events.
* Making event parameters `indexed` affects how events are decoded, potentially breaking existing indexers.
*/
interface IGovernor is IERC165, IERC6372 {
enum ProposalState {
Pending,
Active,
Canceled,
Defeated,
Succeeded,
Queued,
Expired,
Executed
}
/**
* @dev Empty proposal or a mismatch between the parameters length for a proposal call.
*/
error GovernorInvalidProposalLength(uint256 targets, uint256 calldatas, uint256 values);
/**
* @dev The vote was already cast.
*/
error GovernorAlreadyCastVote(address voter);
/**
* @dev Token deposits are disabled in this contract.
*/
error GovernorDisabledDeposit();
/**
* @dev The `account` is not a proposer.
*/
error GovernorOnlyProposer(address account);
/**
* @dev The `account` is not the governance executor.
*/
error GovernorOnlyExecutor(address account);
/**
* @dev The `proposalId` doesn't exist.
*/
error GovernorNonexistentProposal(uint256 proposalId);
/**
* @dev The current state of a proposal is not the required for performing an operation.
* The `expectedStates` is a bitmap with the bits enabled for each ProposalState enum position
* counting from right to left.
*
* NOTE: If `expectedState` is `bytes32(0)`, the proposal is expected to not be in any state (i.e. not exist).
* This is the case when a proposal that is expected to be unset is already initiated (the proposal is duplicated).
*
* See {Governor-_encodeStateBitmap}.
*/
error GovernorUnexpectedProposalState(uint256 proposalId, ProposalState current, bytes32 expectedStates);
/**
* @dev The voting period set is not a valid period.
*/
error GovernorInvalidVotingPeriod(uint256 votingPeriod);
/**
* @dev The `proposer` does not have the required votes to create a proposal.
*/
error GovernorInsufficientProposerVotes(address proposer, uint256 votes, uint256 threshold);
/**
* @dev The `proposer` is not allowed to create a proposal.
*/
error GovernorRestrictedProposer(address proposer);
/**
* @dev The vote type used is not valid for the corresponding counting module.
*/
error GovernorInvalidVoteType();
/**
* @dev The provided params buffer is not supported by the counting module.
*/
error GovernorInvalidVoteParams();
/**
* @dev Queue operation is not implemented for this governor. Execute should be called directly.
*/
error GovernorQueueNotImplemented();
/**
* @dev The proposal hasn't been queued yet.
*/
error GovernorNotQueuedProposal(uint256 proposalId);
/**
* @dev The proposal has already been queued.
*/
error GovernorAlreadyQueuedProposal(uint256 proposalId);
/**
* @dev The provided signature is not valid for the expected `voter`.
* If the `voter` is a contract, the signature is not valid using {IERC1271-isValidSignature}.
*/
error GovernorInvalidSignature(address voter);
/**
* @dev Emitted when a proposal is created.
*/
event ProposalCreated(
uint256 proposalId,
address proposer,
address[] targets,
uint256[] values,
string[] signatures,
bytes[] calldatas,
uint256 voteStart,
uint256 voteEnd,
string description
);
/**
* @dev Emitted when a proposal is queued.
*/
event ProposalQueued(uint256 proposalId, uint256 etaSeconds);
/**
* @dev Emitted when a proposal is executed.
*/
event ProposalExecuted(uint256 proposalId);
/**
* @dev Emitted when a proposal is canceled.
*/
event ProposalCanceled(uint256 proposalId);
/**
* @dev Emitted when a vote is cast without params.
*
* Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used.
*/
event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason);
/**
* @dev Emitted when a vote is cast with params.
*
* Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used.
* `params` are additional encoded parameters. Their interpretation also depends on the voting module used.
*/
event VoteCastWithParams(
address indexed voter,
uint256 proposalId,
uint8 support,
uint256 weight,
string reason,
bytes params
);
/**
* @notice module:core
* @dev Name of the governor instance (used in building the EIP-712 domain separator).
*/
function name() external view returns (string memory);
/**
* @notice module:core
* @dev Version of the governor instance (used in building the EIP-712 domain separator). Default: "1"
*/
function version() external view returns (string memory);
/**
* @notice module:voting
* @dev A description of the possible `support` values for {castVote} and the way these votes are counted, meant to
* be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of
* key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`.
*
* There are 2 standard keys: `support` and `quorum`.
*
* - `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`.
* - `quorum=bravo` means that only For votes are counted towards quorum.
* - `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum.
*
* If a counting module makes use of encoded `params`, it should include this under a `params` key with a unique
* name that describes the behavior. For example:
*
* - `params=fractional` might refer to a scheme where votes are divided fractionally between for/against/abstain.
* - `params=erc721` might refer to a scheme where specific NFTs are delegated to vote.
*
* NOTE: The string can be decoded by the standard
* https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams[`URLSearchParams`]
* JavaScript class.
*/
// solhint-disable-next-line func-name-mixedcase
function COUNTING_MODE() external view returns (string memory);
/**
* @notice module:core
* @dev Hashing function used to (re)build the proposal id from the proposal details..
*/
function hashProposal(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external pure returns (uint256);
/**
* @notice module:core
* @dev Current state of a proposal, following Compound's convention
*/
function state(uint256 proposalId) external view returns (ProposalState);
/**
* @notice module:core
* @dev The number of votes required in order for a voter to become a proposer.
*/
function proposalThreshold() external view returns (uint256);
/**
* @notice module:core
* @dev Timepoint used to retrieve user's votes and quorum. If using block number (as per Compound's Comp), the
* snapshot is performed at the end of this block. Hence, voting for this proposal starts at the beginning of the
* following block.
*/
function proposalSnapshot(uint256 proposalId) external view returns (uint256);
/**
* @notice module:core
* @dev Timepoint at which votes close. If using block number, votes close at the end of this block, so it is
* possible to cast a vote during this block.
*/
function proposalDeadline(uint256 proposalId) external view returns (uint256);
/**
* @notice module:core
* @dev The account that created a proposal.
*/
function proposalProposer(uint256 proposalId) external view returns (address);
/**
* @notice module:core
* @dev The time when a queued proposal becomes executable ("ETA"). Unlike {proposalSnapshot} and
* {proposalDeadline}, this doesn't use the governor clock, and instead relies on the executor's clock which may be
* different. In most cases this will be a timestamp.
*/
function proposalEta(uint256 proposalId) external view returns (uint256);
/**
* @notice module:core
* @dev Whether a proposal needs to be queued before execution.
*/
function proposalNeedsQueuing(uint256 proposalId) external view returns (bool);
/**
* @notice module:user-config
* @dev Delay, between the proposal is created and the vote starts. The unit this duration is expressed in depends
* on the clock (see ERC-6372) this contract uses.
*
* This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a
* proposal starts.
*
* NOTE: While this interface returns a uint256, timepoints are stored as uint48 following the ERC-6372 clock type.
* Consequently this value must fit in a uint48 (when added to the current clock). See {IERC6372-clock}.
*/
function votingDelay() external view returns (uint256);
/**
* @notice module:user-config
* @dev Delay between the vote start and vote end. The unit this duration is expressed in depends on the clock
* (see ERC-6372) this contract uses.
*
* NOTE: The {votingDelay} can delay the start of the vote. This must be considered when setting the voting
* duration compared to the voting delay.
*
* NOTE: This value is stored when the proposal is submitted so that possible changes to the value do not affect
* proposals that have already been submitted. The type used to save it is a uint32. Consequently, while this
* interface returns a uint256, the value it returns should fit in a uint32.
*/
function votingPeriod() external view returns (uint256);
/**
* @notice module:user-config
* @dev Minimum number of cast voted required for a proposal to be successful.
*
* NOTE: The `timepoint` parameter corresponds to the snapshot used for counting vote. This allows to scale the
* quorum depending on values such as the totalSupply of a token at this timepoint (see {ERC20Votes}).
*/
function quorum(uint256 timepoint) external view returns (uint256);
/**
* @notice module:reputation
* @dev Voting power of an `account` at a specific `timepoint`.
*
* Note: this can be implemented in a number of ways, for example by reading the delegated balance from one (or
* multiple), {ERC20Votes} tokens.
*/
function getVotes(address account, uint256 timepoint) external view returns (uint256);
/**
* @notice module:reputation
* @dev Voting power of an `account` at a specific `timepoint` given additional encoded parameters.
*/
function getVotesWithParams(
address account,
uint256 timepoint,
bytes memory params
) external view returns (uint256);
/**
* @notice module:voting
* @dev Returns whether `account` has cast a vote on `proposalId`.
*/
function hasVoted(uint256 proposalId, address account) external view returns (bool);
/**
* @dev Create a new proposal. Vote start after a delay specified by {IGovernor-votingDelay} and lasts for a
* duration specified by {IGovernor-votingPeriod}.
*
* Emits a {ProposalCreated} event.
*
* NOTE: The state of the Governor and `targets` may change between the proposal creation and its execution.
* This may be the result of third party actions on the targeted contracts, or other governor proposals.
* For example, the balance of this contract could be updated or its access control permissions may be modified,
* possibly compromising the proposal's ability to execute successfully (e.g. the governor doesn't have enough
* value to cover a proposal with multiple transfers).
*/
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) external returns (uint256 proposalId);
/**
* @dev Queue a proposal. Some governors require this step to be performed before execution can happen. If queuing
* is not necessary, this function may revert.
* Queuing a proposal requires the quorum to be reached, the vote to be successful, and the deadline to be reached.
*
* Emits a {ProposalQueued} event.
*/
function queue(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external returns (uint256 proposalId);
/**
* @dev Execute a successful proposal. This requires the quorum to be reached, the vote to be successful, and the
* deadline to be reached. Depending on the governor it might also be required that the proposal was queued and
* that some delay passed.
*
* Emits a {ProposalExecuted} event.
*
* NOTE: Some modules can modify the requirements for execution, for example by adding an additional timelock.
*/
function execute(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external payable returns (uint256 proposalId);
/**
* @dev Cancel a proposal. A proposal is cancellable by the proposer, but only while it is Pending state, i.e.
* before the vote starts.
*
* Emits a {ProposalCanceled} event.
*/
function cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) external returns (uint256 proposalId);
/**
* @dev Cast a vote
*
* Emits a {VoteCast} event.
*/
function castVote(uint256 proposalId, uint8 support) external returns (uint256 balance);
/**
* @dev Cast a vote with a reason
*
* Emits a {VoteCast} event.
*/
function castVoteWithReason(
uint256 proposalId,
uint8 support,
string calldata reason
) external returns (uint256 balance);
/**
* @dev Cast a vote with a reason and additional encoded parameters
*
* Emits a {VoteCast} or {VoteCastWithParams} event depending on the length of params.
*/
function castVoteWithReasonAndParams(
uint256 proposalId,
uint8 support,
string calldata reason,
bytes memory params
) external returns (uint256 balance);
/**
* @dev Cast a vote using the voter's signature, including ERC-1271 signature support.
*
* Emits a {VoteCast} event.
*/
function castVoteBySig(
uint256 proposalId,
uint8 support,
address voter,
bytes memory signature
) external returns (uint256 balance);
/**
* @dev Cast a vote with a reason and additional encoded parameters using the voter's signature,
* including ERC-1271 signature support.
*
* Emits a {VoteCast} or {VoteCastWithParams} event depending on the length of params.
*/
function castVoteWithReasonAndParamsBySig(
uint256 proposalId,
uint8 support,
address voter,
string calldata reason,
bytes memory params,
bytes memory signature
) external returns (uint256 balance);
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IMessageLibManager } from "./IMessageLibManager.sol";
import { IMessagingComposer } from "./IMessagingComposer.sol";
import { IMessagingChannel } from "./IMessagingChannel.sol";
import { IMessagingContext } from "./IMessagingContext.sol";
struct MessagingParams {
uint32 dstEid;
bytes32 receiver;
bytes message;
bytes options;
bool payInLzToken;
}
struct MessagingReceipt {
bytes32 guid;
uint64 nonce;
MessagingFee fee;
}
struct MessagingFee {
uint256 nativeFee;
uint256 lzTokenFee;
}
struct Origin {
uint32 srcEid;
bytes32 sender;
uint64 nonce;
}
interface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {
event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);
event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);
event PacketDelivered(Origin origin, address receiver);
event LzReceiveAlert(
address indexed receiver,
address indexed executor,
Origin origin,
bytes32 guid,
uint256 gas,
uint256 value,
bytes message,
bytes extraData,
bytes reason
);
event LzTokenSet(address token);
event DelegateSet(address sender, address delegate);
function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);
function send(
MessagingParams calldata _params,
address _refundAddress
) external payable returns (MessagingReceipt memory);
function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;
function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);
function initializable(Origin calldata _origin, address _receiver) external view returns (bool);
function lzReceive(
Origin calldata _origin,
address _receiver,
bytes32 _guid,
bytes calldata _message,
bytes calldata _extraData
) external payable;
// oapp can burn messages partially by calling this function with its own business logic if messages are verified in order
function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;
function setLzToken(address _lzToken) external;
function lzToken() external view returns (address);
function nativeToken() external view returns (address);
function setDelegate(address _delegate) external;
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
struct SetConfigParam {
uint32 eid;
uint32 configType;
bytes config;
}
interface IMessageLibManager {
struct Timeout {
address lib;
uint256 expiry;
}
event LibraryRegistered(address newLib);
event DefaultSendLibrarySet(uint32 eid, address newLib);
event DefaultReceiveLibrarySet(uint32 eid, address newLib);
event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);
event SendLibrarySet(address sender, uint32 eid, address newLib);
event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);
event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);
function registerLibrary(address _lib) external;
function isRegisteredLibrary(address _lib) external view returns (bool);
function getRegisteredLibraries() external view returns (address[] memory);
function setDefaultSendLibrary(uint32 _eid, address _newLib) external;
function defaultSendLibrary(uint32 _eid) external view returns (address);
function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _gracePeriod) external;
function defaultReceiveLibrary(uint32 _eid) external view returns (address);
function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;
function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);
function isSupportedEid(uint32 _eid) external view returns (bool);
function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);
/// ------------------- OApp interfaces -------------------
function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;
function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);
function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);
function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;
function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);
function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _expiry) external;
function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);
function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;
function getConfig(
address _oapp,
address _lib,
uint32 _eid,
uint32 _configType
) external view returns (bytes memory config);
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessagingChannel {
event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);
event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
function eid() external view returns (uint32);
// this is an emergency function if a message cannot be verified for some reasons
// required to provide _nextNonce to avoid race condition
function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;
function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;
function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;
function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);
function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);
function inboundPayloadHash(
address _receiver,
uint32 _srcEid,
bytes32 _sender,
uint64 _nonce
) external view returns (bytes32);
function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessagingComposer {
event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);
event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);
event LzComposeAlert(
address indexed from,
address indexed to,
address indexed executor,
bytes32 guid,
uint16 index,
uint256 gas,
uint256 value,
bytes message,
bytes extraData,
bytes reason
);
function composeQueue(
address _from,
address _to,
bytes32 _guid,
uint16 _index
) external view returns (bytes32 messageHash);
function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;
function lzCompose(
address _from,
address _to,
bytes32 _guid,
uint16 _index,
bytes calldata _message,
bytes calldata _extraData
) external payable;
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessagingContext {
function isSendingMessage() external view returns (bool);
function getSendContext() external view returns (uint32 dstEid, address sender);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { ILayerZeroEndpointV2 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
/**
* @title IOAppCore
*/
interface IOAppCore {
// Custom error messages
error OnlyPeer(uint32 eid, bytes32 sender);
error NoPeer(uint32 eid);
error InvalidEndpointCall();
error InvalidDelegate();
// Event emitted when a peer (OApp) is set for a corresponding endpoint
event PeerSet(uint32 eid, bytes32 peer);
/**
* @notice Retrieves the OApp version information.
* @return senderVersion The version of the OAppSender.sol contract.
* @return receiverVersion The version of the OAppReceiver.sol contract.
*/
function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);
/**
* @notice Retrieves the LayerZero endpoint associated with the OApp.
* @return iEndpoint The LayerZero endpoint as an interface.
*/
function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);
/**
* @notice Retrieves the peer (OApp) associated with a corresponding endpoint.
* @param _eid The endpoint ID.
* @return peer The peer address (OApp instance) associated with the corresponding endpoint.
*/
function peers(uint32 _eid) external view returns (bytes32 peer);
/**
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
* @param _eid The endpoint ID.
* @param _peer The address of the peer to be associated with the corresponding endpoint.
*/
function setPeer(uint32 _eid, bytes32 _peer) external;
/**
* @notice Sets the delegate address for the OApp Core.
* @param _delegate The address of the delegate to be set.
*/
function setDelegate(address _delegate) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { MessagingReceipt, MessagingFee } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol";
/**
* @dev Struct representing token parameters for the OFT send() operation.
*/
struct SendParam {
uint32 dstEid; // Destination endpoint ID.
bytes32 to; // Recipient address.
uint256 amountLD; // Amount to send in local decimals.
uint256 minAmountLD; // Minimum amount to send in local decimals.
bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.
bytes composeMsg; // The composed message for the send() operation.
bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.
}
/**
* @dev Struct representing OFT limit information.
* @dev These amounts can change dynamically and are up the specific oft implementation.
*/
struct OFTLimit {
uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.
uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.
}
/**
* @dev Struct representing OFT receipt information.
*/
struct OFTReceipt {
uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.
// @dev In non-default implementations, the amountReceivedLD COULD differ from this value.
uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.
}
/**
* @dev Struct representing OFT fee details.
* @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.
*/
struct OFTFeeDetail {
int256 feeAmountLD; // Amount of the fee in local decimals.
string description; // Description of the fee.
}
/**
* @title IOFT
* @dev Interface for the OftChain (OFT) token.
* @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.
* @dev This specific interface ID is '0x02e49c2c'.
*/
interface IOFT {
// Custom error messages
error InvalidLocalDecimals();
error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);
// Events
event OFTSent(
bytes32 indexed guid, // GUID of the OFT message.
uint32 dstEid, // Destination Endpoint ID.
address indexed fromAddress, // Address of the sender on the src chain.
uint256 amountSentLD, // Amount of tokens sent in local decimals.
uint256 amountReceivedLD // Amount of tokens received in local decimals.
);
event OFTReceived(
bytes32 indexed guid, // GUID of the OFT message.
uint32 srcEid, // Source Endpoint ID.
address indexed toAddress, // Address of the recipient on the dst chain.
uint256 amountReceivedLD // Amount of tokens received in local decimals.
);
/**
* @notice Retrieves interfaceID and the version of the OFT.
* @return interfaceId The interface ID.
* @return version The version.
*
* @dev interfaceId: This specific interface ID is '0x02e49c2c'.
* @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.
* @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.
* ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)
*/
function oftVersion() external view returns (bytes4 interfaceId, uint64 version);
/**
* @notice Retrieves the address of the token associated with the OFT.
* @return token The address of the ERC20 token implementation.
*/
function token() external view returns (address);
/**
* @notice Indicates whether the OFT contract requires approval of the 'token()' to send.
* @return requiresApproval Needs approval of the underlying token implementation.
*
* @dev Allows things like wallet implementers to determine integration requirements,
* without understanding the underlying token implementation.
*/
function approvalRequired() external view returns (bool);
/**
* @notice Retrieves the shared decimals of the OFT.
* @return sharedDecimals The shared decimals of the OFT.
*/
function sharedDecimals() external view returns (uint8);
/**
* @notice Provides a quote for OFT-related operations.
* @param _sendParam The parameters for the send operation.
* @return limit The OFT limit information.
* @return oftFeeDetails The details of OFT fees.
* @return receipt The OFT receipt information.
*/
function quoteOFT(
SendParam calldata _sendParam
) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);
/**
* @notice Provides a quote for the send() operation.
* @param _sendParam The parameters for the send() operation.
* @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.
* @return fee The calculated LayerZero messaging fee from the send() operation.
*
* @dev MessagingFee: LayerZero msg fee
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
*/
function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);
/**
* @notice Executes the send() operation.
* @param _sendParam The parameters for the send operation.
* @param _fee The fee information supplied by the caller.
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
* @param _refundAddress The address to receive any excess funds from fees etc. on the src.
* @return receipt The LayerZero messaging receipt from the send() operation.
* @return oftReceipt The OFT receipt information.
*
* @dev MessagingReceipt: LayerZero msg receipt
* - guid: The unique identifier for the sent message.
* - nonce: The nonce of the sent message.
* - fee: The LayerZero fee incurred for the message.
*/
function send(
SendParam calldata _sendParam,
MessagingFee calldata _fee,
address _refundAddress
) external payable returns (MessagingReceipt memory, OFTReceipt memory);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
/**
* @dev Dynamic roles are roles that are not hardcoded in the contract but are defined by the protocol
* Members of this enum are treated as prefixes to the role generated using prefix and target contract address
* e.g generateRole(ContractSpecificRoles.CURATOR_ROLE, address(this)) for FleetCommander, to generate the CURATOR_ROLE
* for the curator of the FleetCommander contract
*/
enum ContractSpecificRoles {
CURATOR_ROLE,
KEEPER_ROLE,
COMMANDER_ROLE
}
/**
* @title IProtocolAccessManager
* @notice Defines system roles and provides role based remote-access control for
* contracts that inherit from ProtocolAccessManaged contract
*/
interface IProtocolAccessManager {
/**
* @notice Grants the Governor role to a given account
*
* @param account The account to which the Governor role will be granted
*/
function grantGovernorRole(address account) external;
/**
* @notice Revokes the Governor role from a given account
*
* @param account The account from which the Governor role will be revoked
*/
function revokeGovernorRole(address account) external;
/**
* @notice Grants the Super Keeper role to a given account
*
* @param account The account to which the Super Keeper role will be granted
*/
function grantSuperKeeperRole(address account) external;
/**
* @notice Revokes the Super Keeper role from a given account
*
* @param account The account from which the Super Keeper role will be revoked
*/
function revokeSuperKeeperRole(address account) external;
/**
* @dev Generates a unique role identifier based on the role name and target contract address
* @param roleName The name of the role (from ContractSpecificRoles enum)
* @param roleTargetContract The address of the contract the role is for
* @return bytes32 The generated role identifier
* @custom:internal-logic
* - Combines the roleName and roleTargetContract using abi.encodePacked
* - Applies keccak256 hash function to generate a unique bytes32 identifier
* @custom:effects
* - Does not modify any state, pure function
* @custom:security-considerations
* - Ensures unique role identifiers for different contracts
* - Relies on the uniqueness of contract addresses and role names
*/
function generateRole(
ContractSpecificRoles roleName,
address roleTargetContract
) external pure returns (bytes32);
/**
* @notice Grants a contract specific role to a given account
* @param roleName The name of the role to grant
* @param roleTargetContract The address of the contract to grant the role for
* @param account The account to which the role will be granted
*/
function grantContractSpecificRole(
ContractSpecificRoles roleName,
address roleTargetContract,
address account
) external;
/**
* @notice Revokes a contract specific role from a given account
* @param roleName The name of the role to revoke
* @param roleTargetContract The address of the contract to revoke the role for
* @param account The account from which the role will be revoked
*/
function revokeContractSpecificRole(
ContractSpecificRoles roleName,
address roleTargetContract,
address account
) external;
/**
* @notice Grants the Curator role to a given account
* @param fleetCommanderAddress The address of the fleet commander to grant the role for
* @param account The account to which the role will be granted
*/
function grantCuratorRole(
address fleetCommanderAddress,
address account
) external;
/**
* @notice Revokes the Curator role from a given account
* @param fleetCommanderAddress The address of the fleet commander to revoke the role for
* @param account The account from which the role will be revoked
*/
function revokeCuratorRole(
address fleetCommanderAddress,
address account
) external;
/**
* @notice Grants the Keeper role to a given account
* @param fleetCommanderAddress The address of the fleet commander to grant the role for
* @param account The account to which the role will be granted
*/
function grantKeeperRole(
address fleetCommanderAddress,
address account
) external;
/**
* @notice Revokes the Keeper role from a given account
* @param fleetCommanderAddress The address of the fleet commander to revoke the role for
* @param account The account from which the role will be revoked
*/
function revokeKeeperRole(
address fleetCommanderAddress,
address account
) external;
/**
* @notice Grants the Commander role for a specific Ark
* @param arkAddress Address of the Ark contract
* @param account Address to grant the Commander role to
*/
function grantCommanderRole(address arkAddress, address account) external;
/**
* @notice Revokes the Commander role for a specific Ark
* @param arkAddress Address of the Ark contract
* @param account Address to revoke the Commander role from
*/
function revokeCommanderRole(address arkAddress, address account) external;
/**
* @notice Revokes a contract specific role from the caller
* @param roleName The name of the role to revoke
* @param roleTargetContract The address of the contract to revoke the role for
*/
function selfRevokeContractSpecificRole(
ContractSpecificRoles roleName,
address roleTargetContract
) external;
/**
* @notice Grants the Guardian role to a given account
*
* @param account The account to which the Guardian role will be granted
*/
function grantGuardianRole(address account) external;
/**
* @notice Revokes the Guardian role from a given account
*
* @param account The account from which the Guardian role will be revoked
*/
function revokeGuardianRole(address account) external;
/**
* @notice Grants the Decay Controller role to a given account
* @param account The account to which the Decay Controller role will be granted
*/
function grantDecayControllerRole(address account) external;
/**
* @notice Revokes the Decay Controller role from a given account
* @param account The account from which the Decay Controller role will be revoked
*/
function revokeDecayControllerRole(address account) external;
/**
* @notice Grants the ADMIRALS_QUARTERS_ROLE to an address
* @param account The address to grant the role to
*/
function grantAdmiralsQuartersRole(address account) external;
/**
* @notice Revokes the ADMIRALS_QUARTERS_ROLE from an address
* @param account The address to revoke the role from
*/
function revokeAdmiralsQuartersRole(address account) external;
/*//////////////////////////////////////////////////////////////
ROLE CONSTANTS
//////////////////////////////////////////////////////////////*/
/// @notice Role identifier for the Governor role
function GOVERNOR_ROLE() external pure returns (bytes32);
/// @notice Role identifier for the Guardian role
function GUARDIAN_ROLE() external pure returns (bytes32);
/// @notice Role identifier for the Super Keeper role
function SUPER_KEEPER_ROLE() external pure returns (bytes32);
/// @notice Role identifier for the Decay Controller role
function DECAY_CONTROLLER_ROLE() external pure returns (bytes32);
/// @notice Role identifier for the Admirals Quarters role
function ADMIRALS_QUARTERS_ROLE() external pure returns (bytes32);
/// @notice Role identifier for the Foundation, responsible for managing vesting wallets and related operations
function FOUNDATION_ROLE() external pure returns (bytes32);
/**
* @notice Checks if an account has a specific role
* @param role The role identifier to check
* @param account The account to check the role for
* @return bool True if the account has the role, false otherwise
*/
function hasRole(
bytes32 role,
address account
) external view returns (bool);
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
/**
* @notice Emitted when a guardian's expiration is set
* @param account The address of the guardian
* @param expiration The timestamp until which the guardian powers are valid
*/
event GuardianExpirationSet(address indexed account, uint256 expiration);
/*//////////////////////////////////////////////////////////////
GUARDIAN FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Checks if an account is an active guardian (has role and not expired)
* @param account Address to check
* @return bool True if account is an active guardian
*/
function isActiveGuardian(address account) external view returns (bool);
/**
* @notice Sets the expiration timestamp for a guardian
* @param account Guardian address
* @param expiration Timestamp when guardian powers expire
*/
function setGuardianExpiration(
address account,
uint256 expiration
) external;
/**
* @notice Gets the expiration timestamp for a guardian
* @param account Guardian address
* @return uint256 Timestamp when guardian powers expire
*/
function guardianExpirations(
address account
) external view returns (uint256);
/**
* @notice Gets the expiration timestamp for a guardian
* @param account Guardian address
* @return expiration Timestamp when guardian powers expire
*/
function getGuardianExpiration(
address account
) external view returns (uint256 expiration);
/**
* @notice Emitted when an invalid guardian expiry period is set
* @param expiryPeriod The expiry period that was set
* @param minExpiryPeriod The minimum allowed expiry period
* @param maxExpiryPeriod The maximum allowed expiry period
*/
error InvalidGuardianExpiryPeriod(
uint256 expiryPeriod,
uint256 minExpiryPeriod,
uint256 maxExpiryPeriod
);
/**
* @notice Grants the Foundation role to a given account. The Foundation is responsible for
* managing vesting wallets and related operations.
* @param account The account to which the Foundation role will be granted
*/
function grantFoundationRole(address account) external;
/**
* @notice Revokes the Foundation role from a given account
* @param account The account from which the Foundation role will be revoked
*/
function revokeFoundationRole(address account) external;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IStakingRewardsManagerBaseErrors} from "./IStakingRewardsManagerBaseErrors.sol";
/* @title IStakingRewardsManagerBase
* @notice Interface for the Staking Rewards Manager contract
* @dev Manages staking and distribution of multiple reward tokens
*/
interface IStakingRewardsManagerBase is IStakingRewardsManagerBaseErrors {
// Views
/* @notice Get the total amount of staked tokens
* @return The total supply of staked tokens
*/
function totalSupply() external view returns (uint256);
/* @notice Get the staked balance of a specific account
* @param account The address of the account to check
* @return The staked balance of the account
*/
function balanceOf(address account) external view returns (uint256);
/* @notice Get the last time the reward was applicable for a specific reward token
* @param rewardToken The address of the reward token
* @return The timestamp of the last applicable reward time
*/
function lastTimeRewardApplicable(
address rewardToken
) external view returns (uint256);
/* @notice Get the reward per token for a specific reward token
* @param rewardToken The address of the reward token
* @return The reward amount per staked token (WAD-scaled)
* @dev Returns a WAD-scaled value (1e18) to maintain precision in calculations
* @dev This value represents: (rewardRate * timeElapsed * WAD) / totalSupply
*/
function rewardPerToken(
address rewardToken
) external view returns (uint256);
/* @notice Calculate the earned reward for an account and a specific reward token
* @param account The address of the account
* @param rewardToken The address of the reward token
* @return The amount of reward tokens earned (not WAD-scaled)
* @dev Calculated as: (balance * (rewardPerToken - userRewardPerTokenPaid)) / WAD + rewards
*/
function earned(
address account,
address rewardToken
) external view returns (uint256);
/* @notice Get the reward for the entire duration for a specific reward token
* @param rewardToken The address of the reward token
* @return The total reward amount for the duration (not WAD-scaled)
* @dev Calculated as: (rewardRate * rewardsDuration) / WAD
*/
function getRewardForDuration(
address rewardToken
) external view returns (uint256);
/* @notice Get the address of the staking token
* @return The address of the staking token
*/
function stakingToken() external view returns (address);
/* @notice Get the reward token at a specific index
* @param index The index of the reward token
* @return The address of the reward token
* @dev Reverts with IndexOutOfBounds if index >= rewardTokensLength()
*/
function rewardTokens(uint256 index) external view returns (address);
/* @notice Get the total number of reward tokens
* @return The length of the reward tokens list
*/
function rewardTokensLength() external view returns (uint256);
/* @notice Check if a token is in the list of reward tokens
* @param rewardToken The address to check
* @return bool True if the token is a reward token, false otherwise
*/
function isRewardToken(address rewardToken) external view returns (bool);
// Mutative functions
/* @notice Stake tokens for an account
* @param amount The amount of tokens to stake
*/
function stake(uint256 amount) external;
/* @notice Stake tokens for an account on behalf of another account
* @param receiver The address of the account to stake for
* @param amount The amount of tokens to stake
*/
function stakeOnBehalfOf(address receiver, uint256 amount) external;
/* @notice Unstake staked tokens on behalf of another account
* @param owner The address of the account to unstake from
* @param amount The amount of tokens to unstake
* @param claimRewards Whether to claim rewards before unstaking
*/
function unstakeAndWithdrawOnBehalfOf(
address owner,
uint256 amount,
bool claimRewards
) external;
/* @notice Unstake staked tokens
* @param amount The amount of tokens to unstake
*/
function unstake(uint256 amount) external;
/* @notice Claim accumulated rewards for all reward tokens */
function getReward() external;
/* @notice Claim accumulated rewards for a specific reward token
* @param rewardToken The address of the reward token to claim
*/
function getReward(address rewardToken) external;
/* @notice Withdraw all staked tokens and claim rewards */
function exit() external;
// Admin functions
/* @notice Notify the contract about new reward amount
* @param rewardToken The address of the reward token
* @param reward The amount of new reward (not WAD-scaled)
* @param newRewardsDuration The duration for rewards distribution (only used when adding a new reward token)
* @dev Internally sets rewardRate as (reward * WAD) / duration to maintain precision
*/
function notifyRewardAmount(
address rewardToken,
uint256 reward,
uint256 newRewardsDuration
) external;
/* @notice Set the duration for rewards distribution
* @param rewardToken The address of the reward token
* @param _rewardsDuration The new duration for rewards
*/
function setRewardsDuration(
address rewardToken,
uint256 _rewardsDuration
) external;
/* @notice Removes a reward token from the list of reward tokens
* @dev Can only be called by governor
* @dev Can only be called after reward period is complete
* @dev Can only be called if remaining balance is below dust threshold
* @param rewardToken The address of the reward token to remove
*/
function removeRewardToken(address rewardToken) external;
// Events
/* @notice Emitted when a new reward is added
* @param rewardToken The address of the reward token
* @param reward The amount of reward added
*/
event RewardAdded(address indexed rewardToken, uint256 reward);
/* @notice Emitted when tokens are staked
* @param staker The address that provided the tokens for staking
* @param receiver The address whose staking balance was updated
* @param amount The amount of tokens added to the staking position
*/
event Staked(
address indexed staker,
address indexed receiver,
uint256 amount
);
/* @notice Emitted when tokens are unstaked
* @param staker The address whose tokens were unstaked
* @param receiver The address receiving the unstaked tokens
* @param amount The amount of tokens unstaked
*/
event Unstaked(
address indexed staker,
address indexed receiver,
uint256 amount
);
/* @notice Emitted when tokens are withdrawn
* @param user The address of the user that withdrew
* @param amount The amount of tokens withdrawn
*/
event Withdrawn(address indexed user, uint256 amount);
/* @notice Emitted when rewards are paid out
* @param user The address of the user receiving the reward
* @param rewardToken The address of the reward token
* @param reward The amount of reward paid
*/
event RewardPaid(
address indexed user,
address indexed rewardToken,
uint256 reward
);
/* @notice Emitted when the rewards duration is updated
* @param rewardToken The address of the reward token
* @param newDuration The new duration for rewards
*/
event RewardsDurationUpdated(
address indexed rewardToken,
uint256 newDuration
);
/* @notice Emitted when a new reward token is added
* @param rewardToken The address of the new reward token
* @param rewardsDuration The duration for the new reward token
*/
event RewardTokenAdded(address rewardToken, uint256 rewardsDuration);
/* @notice Emitted when a reward token is removed
* @param rewardToken The address of the reward token
*/
event RewardTokenRemoved(address rewardToken);
/* @notice Claims rewards for a specific account
* @param account The address to claim rewards for
*/
function getRewardFor(address account) external;
/* @notice Claims rewards for a specific account and specific reward token
* @param account The address to claim rewards for
* @param rewardToken The address of the reward token to claim
*/
function getRewardFor(address account, address rewardToken) external;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
/* @title IStakingRewardsManagerBaseErrors
* @notice Interface defining custom errors for the Staking Rewards Manager
*/
interface IStakingRewardsManagerBaseErrors {
/* @notice Thrown when attempting to stake zero tokens */
error CannotStakeZero();
/* @notice Thrown when attempting to withdraw zero tokens */
error CannotWithdrawZero();
/* @notice Thrown when the provided reward amount is too high */
error ProvidedRewardTooHigh();
/* @notice Thrown when trying to set rewards before the current period is complete */
error RewardPeriodNotComplete();
/* @notice Thrown when there are no reward tokens set */
error NoRewardTokens();
/* @notice Thrown when trying to add a reward token that already exists */
error RewardTokenAlreadyExists();
/* @notice Thrown when setting an invalid rewards duration */
error InvalidRewardsDuration();
/* @notice Thrown when trying to interact with a reward token that hasn't been initialized */
error RewardTokenNotInitialized();
/* @notice Thrown when the reward amount is invalid for the given duration
* @param rewardToken The address of the reward token
* @param rewardsDuration The duration for which the reward is invalid
*/
error InvalidRewardAmount(address rewardToken, uint256 rewardsDuration);
/* @notice Thrown when trying to interact with the staking token before it's initialized */
error StakingTokenNotInitialized();
/* @notice Thrown when trying to remove a reward token that doesn't exist */
error RewardTokenDoesNotExist();
/* @notice Thrown when trying to change the rewards duration of a reward token */
error CannotChangeRewardsDuration();
/* @notice Thrown when a reward token still has a balance */
error RewardTokenStillHasBalance(uint256 balance);
/* @notice Thrown when the index is out of bounds */
error IndexOutOfBounds();
/* @notice Thrown when the rewards duration is zero */
error RewardsDurationCannotBeZero();
/* @notice Thrown when attempting to unstake zero tokens */
error CannotUnstakeZero();
/* @notice Thrown when the rewards duration is too long */
error RewardsDurationTooLong();
/**
* @notice Thrown when the receiver is the zero address
*/
error CannotStakeToZeroAddress();
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import {ISummerTokenErrors} from "../errors/ISummerTokenErrors.sol";
import {VotingDecayLibrary} from "@summerfi/voting-decay/VotingDecayLibrary.sol";
import {IGovernanceRewardsManager} from "./IGovernanceRewardsManager.sol";
import {IVotes} from "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
import {Percentage} from "@summerfi/percentage-solidity/contracts/Percentage.sol";
import {IOFT} from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol";
/**
* @title ISummerToken
* @dev Interface for the Summer governance token, combining ERC20, permit functionality,
* and voting decay mechanisms
*/
interface ISummerToken is
IOFT,
IERC20,
IERC20Permit,
ISummerTokenErrors,
IVotes
{
/*//////////////////////////////////////////////////////////////
STRUCTS
//////////////////////////////////////////////////////////////*/
/**
* @dev Parameters required for contract construction
* @param name The name of the token
* @param symbol The symbol of the token
* @param lzEndpoint The LayerZero endpoint address
* @param initialOwner The initial owner of the contract
* @param accessManager The access manager contract address
* @param maxSupply The maximum token supply
* @param transferEnableDate The timestamp when transfers can be enabled
* @param hubChainId The chain ID of the hub chain
*/
struct ConstructorParams {
string name;
string symbol;
address lzEndpoint;
address initialOwner;
address accessManager;
uint256 maxSupply;
uint256 transferEnableDate;
uint32 hubChainId;
}
/**
* @dev Parameters required for contract initialization
* @param initialSupply The initial token supply to mint
* @param initialDecayFreeWindow The initial decay-free window duration in seconds
* @param initialYearlyDecayRate The initial yearly decay rate as a percentage
* @param initialDecayFunction The initial decay function type
* @param vestingWalletFactory The address of the vesting wallet factory contract
*/
struct InitializeParams {
uint256 initialSupply;
uint40 initialDecayFreeWindow;
Percentage initialYearlyDecayRate;
VotingDecayLibrary.DecayFunction initialDecayFunction;
address vestingWalletFactory;
}
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
/*
* @dev Error thrown when the chain is not the hub chain
* @param chainId The chain ID
* @param hubChainId The hub chain ID
*/
error NotHubChain(uint256 chainId, uint256 hubChainId);
/**
* @notice Error thrown when transfers are not allowed
*/
error TransferNotAllowed();
/**
* @notice Error thrown when transfers cannot be enabled yet
*/
error TransfersCannotBeEnabledYet();
/**
* @notice Error thrown when transfers are already enabled
*/
error TransfersAlreadyEnabled();
/**
* @notice Error thrown when the address length is invalid
*/
error InvalidAddressLength();
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
/**
* @notice Emitted when transfers are enabled
*/
event TransfersEnabled();
/**
* @notice Error thrown when invalid peer arrays are provided
*/
error SummerTokenInvalidPeerArrays();
/**
* @notice Emitted when an address is whitelisted
* @param account The address of the whitelisted account
*/
event AddressWhitelisted(address indexed account);
/**
* @notice Emitted when an address is removed from the whitelist
* @param account The address of the removed account
*/
event AddressRemovedFromWhitelist(address indexed account);
/*//////////////////////////////////////////////////////////////
EXTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Returns the decay free window
* @return The decay free window in seconds
*/
function getDecayFreeWindow() external view returns (uint40);
/**
* @notice Returns the yearly decay rate as a percentage
* @return The yearly decay rate as a Percentage type
* @dev This returns the annualized rate using simple multiplication rather than
* compound interest calculation for clarity and predictability
*/
function getDecayRatePerYear() external view returns (Percentage);
/**
* @notice Returns the decay factor for an account
* @param account The address to get the decay factor for
* @return The decay factor for the account
*/
function getDecayFactor(address account) external view returns (uint256);
/**
* @notice Returns the decay factor for an account at a specific timepoint
* @param account The address to get the decay factor for
* @param timepoint The timestamp to get the decay factor at
* @return The decay factor for the account at the specified timepoint
*/
function getPastDecayFactor(
address account,
uint256 timepoint
) external view returns (uint256);
/**
* @notice Returns the current votes for an account with decay factor applied
* @param account The address to get votes for
* @return The current voting power after applying the decay factor
* @dev This function:
* 1. Gets the raw votes using ERC20Votes' _getVotes
* 2. Applies the decay factor from VotingDecayManager
* @custom:relationship-to-votingdecay
* - Uses VotingDecayManager.getVotingPower() to apply decay
* - Decay factor is determined by:
* - Time since last update
* - Delegation chain (up to MAX_DELEGATION_DEPTH)
* - Current decayRatePerSecond and decayFreeWindow
*/
function getVotes(address account) external view returns (uint256);
/**
* @notice Updates the decay factor for a specific account
* @param account The address of the account to update
* @dev Can only be called by the governor
*/
function updateDecayFactor(address account) external;
/**
* @notice Sets the yearly decay rate for voting power decay
* @param newYearlyRate The new decay rate per year as a Percentage
* @dev Can only be called by the governor
* @dev The rate is converted internally to a per-second rate using simple division
*/
function setDecayRatePerYear(Percentage newYearlyRate) external;
/**
* @notice Sets the decay-free window duration
* @param newWindow The new decay-free window duration in seconds
* @dev Can only be called by the governor
*/
function setDecayFreeWindow(uint40 newWindow) external;
/**
* @notice Sets the decay function type
* @param newFunction The new decay function to use
* @dev Can only be called by the governor
*/
function setDecayFunction(
VotingDecayLibrary.DecayFunction newFunction
) external;
/**
* @notice Enables transfers
*/
function enableTransfers() external;
/**
* @notice Adds an address to the whitelist
* @param account The address to add to the whitelist
*/
function addToWhitelist(address account) external;
/**
* @notice Removes an address from the whitelist
* @param account The address to remove from the whitelist
*/
function removeFromWhitelist(address account) external;
/**
* @notice Returns the address of the rewards manager contract
* @return The address of the rewards manager
*/
function rewardsManager() external view returns (address);
/**
* @notice Gets the length of the delegation chain for an account
* @param account The address to check delegation chain for
* @return The length of the delegation chain (0 for self-delegated or invalid chains)
*/
function getDelegationChainLength(
address account
) external view returns (uint256);
/**
* @notice Returns the raw votes (before decay) for an account at a specific timepoint
* @param account The address to get raw votes for
* @param timestamp The timestamp to get raw votes at
* @return The current voting power before applying any decay factor
* @dev This returns the total voting units including direct balance, staked tokens,
* and vesting wallet balances, but without applying the decay factor
*/
function getRawVotesAt(
address account,
uint256 timestamp
) external view returns (uint256);
/**
* @notice Returns the votes for an account at a specific past block, with decay factor applied
* @param account The address to get votes for
* @param timepoint The block number to get votes at
* @return The historical voting power after applying the decay factor
* @dev This function:
* 1. Gets the historical raw votes using ERC20Votes' _getPastVotes
* 2. Applies the current decay factor from VotingDecayManager
* @custom:relationship-to-votingdecay
* - Uses VotingDecayManager.getVotingPower() to apply decay
* - Note: The decay factor is current, not historical
* - This means voting power can decrease over time even for past checkpoints
*/
function getPastVotes(
address account,
uint256 timepoint
) external view returns (uint256);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {ISummerVestingWallet} from "../interfaces/ISummerVestingWallet.sol";
/**
* @title ISummerTokenErrors
* @notice Interface defining custom errors for the SummerToken contract
*/
interface ISummerTokenErrors {
/**
* @dev Error thrown when an invalid vesting type is provided
* @param invalidType The invalid vesting type that was provided
*/
error InvalidVestingType(ISummerVestingWallet.VestingType invalidType);
/**
* @dev Error thrown when the caller is not the decay manager or governor
* @param caller The address of the caller
*/
error CallerIsNotAuthorized(address caller);
/**
* @dev Error thrown when the caller is not the decay manager
* @param caller The address of the caller
*/
error CallerIsNotDecayManager(address caller);
/**
* @dev Error thrown when the decay rate is too high
*/
error DecayRateTooHigh(uint256 rate);
/**
* @dev Error thrown when the decay free window is invalid (less than 30 days or more than 365.25 days)
* @param window The invalid window duration that was provided
*/
error InvalidDecayFreeWindow(uint40 window);
/**
* @dev Error thrown when attempting to initialize the contract after it has already been initialized
*/
error AlreadyInitialized();
/**
* @dev Error thrown when attempting to undelegate while staked
*/
error CannotUndelegateWhileStaked();
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title ISummerVestingWallet
* @dev Interface for SummerVestingWallet, an extension of OpenZeppelin's VestingWallet with custom vesting schedules
* and separate admin role.
* Supports two types of vesting: Team vesting and Investor/Ex-Team vesting, both with a 6-month cliff.
*
* Vesting Schedules:
* 1. Team Vesting:
* - Time-based: Monthly releases over 2 years, starting after the 6-month cliff.
* - Performance-based: arbitrary amount of additional milestone-based releases, triggered by the guardian.
* 2. Investor/Ex-Team Vesting:
* - Time-based only: Monthly releases over 2 years, starting after the 6-month cliff.
*
* The guardian role can mark performance goals as reached for team vesting and recall unvested
* performance-based tokens if necessary.
*/
interface ISummerVestingWallet {
/// @dev Enum representing the types of vesting schedules
enum VestingType {
TeamVesting,
InvestorExTeamVesting
}
//////////////////////////////////////////////
/// VIEW FUNCTIONS ///
//////////////////////////////////////////////
/// @dev The token being vested
function token() external view returns (address);
/// @dev Performance-based vesting amounts
function goalAmounts(uint256 index) external view returns (uint256);
/// @dev Performance milestone flags
function goalsReached(uint256 index) external view returns (bool);
/// @dev Time-based vesting amount
function timeBasedVestingAmount() external view returns (uint256);
/**
* @dev Returns the vesting type of this wallet
* @return The VestingType enum value representing the vesting type (TeamVesting or InvestorExTeamVesting)
*/
function getVestingType() external view returns (VestingType);
//////////////////////////////////////////////
/// MUTATIVE FUNCTIONS ///
//////////////////////////////////////////////
/**
* @notice Adds a new performance-based vesting goal to the contract
* @dev This function can only be called by an address with the GUARDIAN_ROLE
* @dev The new goal is appended to the existing goalAmounts array
* @dev A corresponding false value is added to the goalsReached array
* @dev This function allows for dynamic expansion of performance-based vesting goals
* @dev The caller must transfer the goalAmount of tokens to this contract after calling this function
* @param goalAmount The amount of tokens associated with the new performance goal
*/
function addNewGoal(uint256 goalAmount) external;
/**
* @notice Marks a specific performance goal as reached
* @dev This function can only be called by an address with the GUARDIAN_ROLE
* @param goalNumber The number of the goal to mark as reached (1-indexed)
*/
function markGoalReached(uint256 goalNumber) external;
/**
* @notice Recalls unvested performance-based tokens
* @dev This function can only be called by an address with the GUARDIAN_ROLE
* @dev It's only applicable for TeamVesting type
*/
function recallUnvestedTokens() external;
//////////////////////////////////////////////
/// ERRORS ///
//////////////////////////////////////////////
/// @dev Thrown when an invalid goal number is provided
error InvalidGoalNumber();
/// @dev Thrown when a function is called that's only applicable to TeamVesting
error OnlyTeamVesting();
/// @dev Thrown when the goal array length is invalid
error InvalidGoalArrayLength();
/// @dev Thrown when the token address is invalid
error InvalidToken(address token);
/// @dev Emitted when a new goal is added
event NewGoalAdded(uint256 goalAmount, uint256 goalNumber);
/// @dev Emitted when a goal is reached
event GoalReached(uint256 goalNumber);
/// @dev Emitted when unvested tokens are recalled
event UnvestedTokensRecalled(uint256 unvestedTokens);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (governance/utils/IVotes.sol)
pragma solidity ^0.8.20;
/**
* @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
*/
interface IVotes {
/**
* @dev The signature used has expired.
*/
error VotesExpiredSignature(uint256 expiry);
/**
* @dev Emitted when an account changes their delegate.
*/
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
/**
* @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of voting units.
*/
event DelegateVotesChanged(address indexed delegate, uint256 previousVotes, uint256 newVotes);
/**
* @dev Returns the current amount of votes that `account` has.
*/
function getVotes(address account) external view returns (uint256);
/**
* @dev Returns the amount of votes that `account` had at a specific moment in the past. If the `clock()` is
* configured to use block numbers, this will return the value at the end of the corresponding block.
*/
function getPastVotes(address account, uint256 timepoint) external view returns (uint256);
/**
* @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is
* configured to use block numbers, this will return the value at the end of the corresponding block.
*
* NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
* Votes that have not been delegated are still part of total supply, even though they would not participate in a
* vote.
*/
function getPastTotalSupply(uint256 timepoint) external view returns (uint256);
/**
* @dev Returns the delegate that `account` has chosen.
*/
function delegates(address account) external view returns (address);
/**
* @dev Delegates votes from the sender to `delegatee`.
*/
function delegate(address delegatee) external;
/**
* @dev Delegates votes from signer to `delegatee`.
*/
function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {IAccessControlErrors} from "../interfaces/IAccessControlErrors.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
/**
* @title LimitedAccessControl
* @dev This contract extends OpenZeppelin's AccessControl, disabling direct role granting and revoking.
* It's designed to be used as a base contract for more specific access control implementations.
* @dev This contract overrides the grantRole and revokeRole functions from AccessControl to disable direct role
* granting and revoking.
* @dev It doesn't override the renounceRole function, so it can be used to renounce roles for compromised accounts.
*/
abstract contract LimitedAccessControl is AccessControl, IAccessControlErrors {
/**
* @dev Overrides the grantRole function from AccessControl to disable direct role granting.
* @notice This function always reverts with a DirectGrantIsDisabled error.
*/
function grantRole(bytes32, address) public view override {
revert DirectGrantIsDisabled(msg.sender);
}
/**
* @dev Overrides the revokeRole function from AccessControl to disable direct role revoking.
* @notice This function always reverts with a DirectRevokeIsDisabled error.
*/
function revokeRole(bytes32, address) public view override {
revert DirectRevokeIsDisabled(msg.sender);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import { wrap } from "./Casting.sol";
import {
uEXP_MAX_INPUT,
uEXP2_MAX_INPUT,
uHALF_UNIT,
uLOG2_10,
uLOG2_E,
uMAX_UD60x18,
uMAX_WHOLE_UD60x18,
UNIT,
uUNIT,
uUNIT_SQUARED,
ZERO
} from "./Constants.sol";
import { UD60x18 } from "./ValueType.sol";
/*//////////////////////////////////////////////////////////////////////////
MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Calculates the arithmetic average of x and y using the following formula:
///
/// $$
/// avg(x, y) = (x & y) + ((xUint ^ yUint) / 2)
/// $$
///
/// In English, this is what this formula does:
///
/// 1. AND x and y.
/// 2. Calculate half of XOR x and y.
/// 3. Add the two results together.
///
/// This technique is known as SWAR, which stands for "SIMD within a register". You can read more about it here:
/// https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The arithmetic average as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function avg(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
uint256 yUint = y.unwrap();
unchecked {
result = wrap((xUint & yUint) + ((xUint ^ yUint) >> 1));
}
}
/// @notice Yields the smallest whole number greater than or equal to x.
///
/// @dev This is optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional
/// counterparts. See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x ≤ MAX_WHOLE_UD60x18
///
/// @param x The UD60x18 number to ceil.
/// @return result The smallest whole number greater than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ceil(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
if (xUint > uMAX_WHOLE_UD60x18) {
revert Errors.PRBMath_UD60x18_Ceil_Overflow(x);
}
assembly ("memory-safe") {
// Equivalent to `x % UNIT`.
let remainder := mod(x, uUNIT)
// Equivalent to `UNIT - remainder`.
let delta := sub(uUNIT, remainder)
// Equivalent to `x + remainder > 0 ? delta : 0`.
result := add(x, mul(delta, gt(remainder, 0)))
}
}
/// @notice Divides two UD60x18 numbers, returning a new UD60x18 number.
///
/// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
///
/// @param x The numerator as a UD60x18 number.
/// @param y The denominator as a UD60x18 number.
/// @return result The quotient as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function div(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(Common.mulDiv(x.unwrap(), uUNIT, y.unwrap()));
}
/// @notice Calculates the natural exponent of x using the following formula:
///
/// $$
/// e^x = 2^{x * log_2{e}}
/// $$
///
/// @dev Requirements:
/// - x ≤ 133_084258667509499440
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
// This check prevents values greater than 192e18 from being passed to {exp2}.
if (xUint > uEXP_MAX_INPUT) {
revert Errors.PRBMath_UD60x18_Exp_InputTooBig(x);
}
unchecked {
// Inline the fixed-point multiplication to save gas.
uint256 doubleUnitProduct = xUint * uLOG2_E;
result = exp2(wrap(doubleUnitProduct / uUNIT));
}
}
/// @notice Calculates the binary exponent of x using the binary fraction method.
///
/// @dev See https://ethereum.stackexchange.com/q/79903/24693
///
/// Requirements:
/// - x < 192e18
/// - The result must fit in UD60x18.
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp2(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
// Numbers greater than or equal to 192e18 don't fit in the 192.64-bit format.
if (xUint > uEXP2_MAX_INPUT) {
revert Errors.PRBMath_UD60x18_Exp2_InputTooBig(x);
}
// Convert x to the 192.64-bit fixed-point format.
uint256 x_192x64 = (xUint << 64) / uUNIT;
// Pass x to the {Common.exp2} function, which uses the 192.64-bit fixed-point number representation.
result = wrap(Common.exp2(x_192x64));
}
/// @notice Yields the greatest whole number less than or equal to x.
/// @dev Optimized for fractional value inputs, because every whole value has (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
/// @param x The UD60x18 number to floor.
/// @return result The greatest whole number less than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function floor(UD60x18 x) pure returns (UD60x18 result) {
assembly ("memory-safe") {
// Equivalent to `x % UNIT`.
let remainder := mod(x, uUNIT)
// Equivalent to `x - remainder > 0 ? remainder : 0)`.
result := sub(x, mul(remainder, gt(remainder, 0)))
}
}
/// @notice Yields the excess beyond the floor of x using the odd function definition.
/// @dev See https://en.wikipedia.org/wiki/Fractional_part.
/// @param x The UD60x18 number to get the fractional part of.
/// @return result The fractional part of x as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function frac(UD60x18 x) pure returns (UD60x18 result) {
assembly ("memory-safe") {
result := mod(x, uUNIT)
}
}
/// @notice Calculates the geometric mean of x and y, i.e. $\sqrt{x * y}$, rounding down.
///
/// @dev Requirements:
/// - x * y must fit in UD60x18.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function gm(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
uint256 yUint = y.unwrap();
if (xUint == 0 || yUint == 0) {
return ZERO;
}
unchecked {
// Checking for overflow this way is faster than letting Solidity do it.
uint256 xyUint = xUint * yUint;
if (xyUint / xUint != yUint) {
revert Errors.PRBMath_UD60x18_Gm_Overflow(x, y);
}
// We don't need to multiply the result by `UNIT` here because the x*y product picked up a factor of `UNIT`
// during multiplication. See the comments in {Common.sqrt}.
result = wrap(Common.sqrt(xyUint));
}
}
/// @notice Calculates the inverse of x.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must not be zero.
///
/// @param x The UD60x18 number for which to calculate the inverse.
/// @return result The inverse as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function inv(UD60x18 x) pure returns (UD60x18 result) {
unchecked {
result = wrap(uUNIT_SQUARED / x.unwrap());
}
}
/// @notice Calculates the natural logarithm of x using the following formula:
///
/// $$
/// ln{x} = log_2{x} / log_2{e}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
/// - The precision isn't sufficiently fine-grained to return exactly `UNIT` when the input is `E`.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the natural logarithm.
/// @return result The natural logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ln(UD60x18 x) pure returns (UD60x18 result) {
unchecked {
// Inline the fixed-point multiplication to save gas. This is overflow-safe because the maximum value that
// {log2} can return is ~196_205294292027477728.
result = wrap(log2(x).unwrap() * uUNIT / uLOG2_E);
}
}
/// @notice Calculates the common logarithm of x using the following formula:
///
/// $$
/// log_{10}{x} = log_2{x} / log_2{10}
/// $$
///
/// However, if x is an exact power of ten, a hard coded value is returned.
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the common logarithm.
/// @return result The common logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function log10(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
if (xUint < uUNIT) {
revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
}
// Note that the `mul` in this assembly block is the standard multiplication operation, not {UD60x18.mul}.
// prettier-ignore
assembly ("memory-safe") {
switch x
case 1 { result := mul(uUNIT, sub(0, 18)) }
case 10 { result := mul(uUNIT, sub(1, 18)) }
case 100 { result := mul(uUNIT, sub(2, 18)) }
case 1000 { result := mul(uUNIT, sub(3, 18)) }
case 10000 { result := mul(uUNIT, sub(4, 18)) }
case 100000 { result := mul(uUNIT, sub(5, 18)) }
case 1000000 { result := mul(uUNIT, sub(6, 18)) }
case 10000000 { result := mul(uUNIT, sub(7, 18)) }
case 100000000 { result := mul(uUNIT, sub(8, 18)) }
case 1000000000 { result := mul(uUNIT, sub(9, 18)) }
case 10000000000 { result := mul(uUNIT, sub(10, 18)) }
case 100000000000 { result := mul(uUNIT, sub(11, 18)) }
case 1000000000000 { result := mul(uUNIT, sub(12, 18)) }
case 10000000000000 { result := mul(uUNIT, sub(13, 18)) }
case 100000000000000 { result := mul(uUNIT, sub(14, 18)) }
case 1000000000000000 { result := mul(uUNIT, sub(15, 18)) }
case 10000000000000000 { result := mul(uUNIT, sub(16, 18)) }
case 100000000000000000 { result := mul(uUNIT, sub(17, 18)) }
case 1000000000000000000 { result := 0 }
case 10000000000000000000 { result := uUNIT }
case 100000000000000000000 { result := mul(uUNIT, 2) }
case 1000000000000000000000 { result := mul(uUNIT, 3) }
case 10000000000000000000000 { result := mul(uUNIT, 4) }
case 100000000000000000000000 { result := mul(uUNIT, 5) }
case 1000000000000000000000000 { result := mul(uUNIT, 6) }
case 10000000000000000000000000 { result := mul(uUNIT, 7) }
case 100000000000000000000000000 { result := mul(uUNIT, 8) }
case 1000000000000000000000000000 { result := mul(uUNIT, 9) }
case 10000000000000000000000000000 { result := mul(uUNIT, 10) }
case 100000000000000000000000000000 { result := mul(uUNIT, 11) }
case 1000000000000000000000000000000 { result := mul(uUNIT, 12) }
case 10000000000000000000000000000000 { result := mul(uUNIT, 13) }
case 100000000000000000000000000000000 { result := mul(uUNIT, 14) }
case 1000000000000000000000000000000000 { result := mul(uUNIT, 15) }
case 10000000000000000000000000000000000 { result := mul(uUNIT, 16) }
case 100000000000000000000000000000000000 { result := mul(uUNIT, 17) }
case 1000000000000000000000000000000000000 { result := mul(uUNIT, 18) }
case 10000000000000000000000000000000000000 { result := mul(uUNIT, 19) }
case 100000000000000000000000000000000000000 { result := mul(uUNIT, 20) }
case 1000000000000000000000000000000000000000 { result := mul(uUNIT, 21) }
case 10000000000000000000000000000000000000000 { result := mul(uUNIT, 22) }
case 100000000000000000000000000000000000000000 { result := mul(uUNIT, 23) }
case 1000000000000000000000000000000000000000000 { result := mul(uUNIT, 24) }
case 10000000000000000000000000000000000000000000 { result := mul(uUNIT, 25) }
case 100000000000000000000000000000000000000000000 { result := mul(uUNIT, 26) }
case 1000000000000000000000000000000000000000000000 { result := mul(uUNIT, 27) }
case 10000000000000000000000000000000000000000000000 { result := mul(uUNIT, 28) }
case 100000000000000000000000000000000000000000000000 { result := mul(uUNIT, 29) }
case 1000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 30) }
case 10000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 31) }
case 100000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 32) }
case 1000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 33) }
case 10000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 34) }
case 100000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 35) }
case 1000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 36) }
case 10000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 37) }
case 100000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 38) }
case 1000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 39) }
case 10000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 40) }
case 100000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 41) }
case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 42) }
case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 43) }
case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 44) }
case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 45) }
case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 46) }
case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 47) }
case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 48) }
case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 49) }
case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 50) }
case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 51) }
case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 52) }
case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 53) }
case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 54) }
case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 55) }
case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 56) }
case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 57) }
case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 58) }
case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 59) }
default { result := uMAX_UD60x18 }
}
if (result.unwrap() == uMAX_UD60x18) {
unchecked {
// Inline the fixed-point division to save gas.
result = wrap(log2(x).unwrap() * uUNIT / uLOG2_10);
}
}
}
/// @notice Calculates the binary logarithm of x using the iterative approximation algorithm:
///
/// $$
/// log_2{x} = n + log_2{y}, \text{ where } y = x*2^{-n}, \ y \in [1, 2)
/// $$
///
/// For $0 \leq x \lt 1$, the input is inverted:
///
/// $$
/// log_2{x} = -log_2{\frac{1}{x}}
/// $$
///
/// @dev See https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
///
/// Notes:
/// - Due to the lossy precision of the iterative approximation, the results are not perfectly accurate to the last decimal.
///
/// Requirements:
/// - x ≥ UNIT
///
/// @param x The UD60x18 number for which to calculate the binary logarithm.
/// @return result The binary logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function log2(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
if (xUint < uUNIT) {
revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
}
unchecked {
// Calculate the integer part of the logarithm.
uint256 n = Common.msb(xUint / uUNIT);
// This is the integer part of the logarithm as a UD60x18 number. The operation can't overflow because n
// n is at most 255 and UNIT is 1e18.
uint256 resultUint = n * uUNIT;
// Calculate $y = x * 2^{-n}$.
uint256 y = xUint >> n;
// If y is the unit number, the fractional part is zero.
if (y == uUNIT) {
return wrap(resultUint);
}
// Calculate the fractional part via the iterative approximation.
// The `delta >>= 1` part is equivalent to `delta /= 2`, but shifting bits is more gas efficient.
uint256 DOUBLE_UNIT = 2e18;
for (uint256 delta = uHALF_UNIT; delta > 0; delta >>= 1) {
y = (y * y) / uUNIT;
// Is y^2 >= 2e18 and so in the range [2e18, 4e18)?
if (y >= DOUBLE_UNIT) {
// Add the 2^{-m} factor to the logarithm.
resultUint += delta;
// Halve y, which corresponds to z/2 in the Wikipedia article.
y >>= 1;
}
}
result = wrap(resultUint);
}
}
/// @notice Multiplies two UD60x18 numbers together, returning a new UD60x18 number.
///
/// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
///
/// @dev See the documentation in {Common.mulDiv18}.
/// @param x The multiplicand as a UD60x18 number.
/// @param y The multiplier as a UD60x18 number.
/// @return result The product as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function mul(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(Common.mulDiv18(x.unwrap(), y.unwrap()));
}
/// @notice Raises x to the power of y.
///
/// For $1 \leq x \leq \infty$, the following standard formula is used:
///
/// $$
/// x^y = 2^{log_2{x} * y}
/// $$
///
/// For $0 \leq x \lt 1$, since the unsigned {log2} is undefined, an equivalent formula is used:
///
/// $$
/// i = \frac{1}{x}
/// w = 2^{log_2{i} * y}
/// x^y = \frac{1}{w}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2} and {mul}.
/// - Returns `UNIT` for 0^0.
/// - It may not perform well with very small values of x. Consider using SD59x18 as an alternative.
///
/// Requirements:
/// - Refer to the requirements in {exp2}, {log2}, and {mul}.
///
/// @param x The base as a UD60x18 number.
/// @param y The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function pow(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
uint256 yUint = y.unwrap();
// If both x and y are zero, the result is `UNIT`. If just x is zero, the result is always zero.
if (xUint == 0) {
return yUint == 0 ? UNIT : ZERO;
}
// If x is `UNIT`, the result is always `UNIT`.
else if (xUint == uUNIT) {
return UNIT;
}
// If y is zero, the result is always `UNIT`.
if (yUint == 0) {
return UNIT;
}
// If y is `UNIT`, the result is always x.
else if (yUint == uUNIT) {
return x;
}
// If x is > UNIT, use the standard formula.
if (xUint > uUNIT) {
result = exp2(mul(log2(x), y));
}
// Conversely, if x < UNIT, use the equivalent formula.
else {
UD60x18 i = wrap(uUNIT_SQUARED / xUint);
UD60x18 w = exp2(mul(log2(i), y));
result = wrap(uUNIT_SQUARED / w.unwrap());
}
}
/// @notice Raises x (a UD60x18 number) to the power y (an unsigned basic integer) using the well-known
/// algorithm "exponentiation by squaring".
///
/// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv18}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - The result must fit in UD60x18.
///
/// @param x The base as a UD60x18 number.
/// @param y The exponent as a uint256.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function powu(UD60x18 x, uint256 y) pure returns (UD60x18 result) {
// Calculate the first iteration of the loop in advance.
uint256 xUint = x.unwrap();
uint256 resultUint = y & 1 > 0 ? xUint : uUNIT;
// Equivalent to `for(y /= 2; y > 0; y /= 2)`.
for (y >>= 1; y > 0; y >>= 1) {
xUint = Common.mulDiv18(xUint, xUint);
// Equivalent to `y % 2 == 1`.
if (y & 1 > 0) {
resultUint = Common.mulDiv18(resultUint, xUint);
}
}
result = wrap(resultUint);
}
/// @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:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x ≤ MAX_UD60x18 / UNIT
///
/// @param x The UD60x18 number for which to calculate the square root.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function sqrt(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
unchecked {
if (xUint > uMAX_UD60x18 / uUNIT) {
revert Errors.PRBMath_UD60x18_Sqrt_Overflow(x);
}
// Multiply x by `UNIT` to account for the factor of `UNIT` picked up when multiplying two UD60x18 numbers.
// In this case, the two numbers are both the square root.
result = wrap(Common.sqrt(xUint * uUNIT));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[ERC-191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an ERC-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
assembly ("memory-safe") {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an ERC-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an ERC-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (ERC-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Nonces.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides tracking nonces for addresses. Nonces will only increment.
*/
abstract contract Nonces {
/**
* @dev The nonce used for an `account` is not the expected current nonce.
*/
error InvalidAccountNonce(address account, uint256 currentNonce);
mapping(address account => uint256) private _nonces;
/**
* @dev Returns the next unused nonce for an address.
*/
function nonces(address owner) public view virtual returns (uint256) {
return _nonces[owner];
}
/**
* @dev Consumes a nonce.
*
* Returns the current value and increments nonce.
*/
function _useNonce(address owner) internal virtual returns (uint256) {
// For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be
// decremented or reset. This guarantees that the nonce never overflows.
unchecked {
// It is important to do x++ and not ++x here.
return _nonces[owner]++;
}
}
/**
* @dev Same as {_useNonce} but checking that `nonce` is the next valid for `owner`.
*/
function _useCheckedNonce(address owner, uint256 nonce) internal virtual {
uint256 current = _useNonce(owner);
if (nonce != current) {
revert InvalidAccountNonce(owner, current);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IOAppCore, ILayerZeroEndpointV2 } from "./interfaces/IOAppCore.sol";
/**
* @title OAppCore
* @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.
*/
abstract contract OAppCore is IOAppCore, Ownable {
// The LayerZero endpoint associated with the given OApp
ILayerZeroEndpointV2 public immutable endpoint;
// Mapping to store peers associated with corresponding endpoints
mapping(uint32 eid => bytes32 peer) public peers;
/**
* @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.
* @param _endpoint The address of the LOCAL Layer Zero endpoint.
* @param _delegate The delegate capable of making OApp configurations inside of the endpoint.
*
* @dev The delegate typically should be set as the owner of the contract.
*/
constructor(address _endpoint, address _delegate) {
endpoint = ILayerZeroEndpointV2(_endpoint);
if (_delegate == address(0)) revert InvalidDelegate();
endpoint.setDelegate(_delegate);
}
/**
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
* @param _eid The endpoint ID.
* @param _peer The address of the peer to be associated with the corresponding endpoint.
*
* @dev Only the owner/admin of the OApp can call this function.
* @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
* @dev Set this to bytes32(0) to remove the peer address.
* @dev Peer is a bytes32 to accommodate non-evm chains.
*/
function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {
_setPeer(_eid, _peer);
}
/**
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
* @param _eid The endpoint ID.
* @param _peer The address of the peer to be associated with the corresponding endpoint.
*
* @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
* @dev Set this to bytes32(0) to remove the peer address.
* @dev Peer is a bytes32 to accommodate non-evm chains.
*/
function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {
peers[_eid] = _peer;
emit PeerSet(_eid, _peer);
}
/**
* @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.
* ie. the peer is set to bytes32(0).
* @param _eid The endpoint ID.
* @return peer The address of the peer associated with the specified endpoint.
*/
function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {
bytes32 peer = peers[_eid];
if (peer == bytes32(0)) revert NoPeer(_eid);
return peer;
}
/**
* @notice Sets the delegate address for the OApp.
* @param _delegate The address of the delegate to be set.
*
* @dev Only the owner/admin of the OApp can call this function.
* @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.
*/
function setDelegate(address _delegate) public onlyOwner {
endpoint.setDelegate(_delegate);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { MessagingParams, MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { OAppCore } from "./OAppCore.sol";
/**
* @title OAppSender
* @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.
*/
abstract contract OAppSender is OAppCore {
using SafeERC20 for IERC20;
// Custom error messages
error NotEnoughNative(uint256 msgValue);
error LzTokenUnavailable();
// @dev The version of the OAppSender implementation.
// @dev Version is bumped when changes are made to this contract.
uint64 internal constant SENDER_VERSION = 1;
/**
* @notice Retrieves the OApp version information.
* @return senderVersion The version of the OAppSender.sol contract.
* @return receiverVersion The version of the OAppReceiver.sol contract.
*
* @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.
* ie. this is a SEND only OApp.
* @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions
*/
function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {
return (SENDER_VERSION, 0);
}
/**
* @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.
* @param _dstEid The destination endpoint ID.
* @param _message The message payload.
* @param _options Additional options for the message.
* @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.
* @return fee The calculated MessagingFee for the message.
* - nativeFee: The native fee for the message.
* - lzTokenFee: The LZ token fee for the message.
*/
function _quote(
uint32 _dstEid,
bytes memory _message,
bytes memory _options,
bool _payInLzToken
) internal view virtual returns (MessagingFee memory fee) {
return
endpoint.quote(
MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),
address(this)
);
}
/**
* @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.
* @param _dstEid The destination endpoint ID.
* @param _message The message payload.
* @param _options Additional options for the message.
* @param _fee The calculated LayerZero fee for the message.
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
* @param _refundAddress The address to receive any excess fee values sent to the endpoint.
* @return receipt The receipt for the sent message.
* - guid: The unique identifier for the sent message.
* - nonce: The nonce of the sent message.
* - fee: The LayerZero fee incurred for the message.
*/
function _lzSend(
uint32 _dstEid,
bytes memory _message,
bytes memory _options,
MessagingFee memory _fee,
address _refundAddress
) internal virtual returns (MessagingReceipt memory receipt) {
// @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.
uint256 messageValue = _payNative(_fee.nativeFee);
if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);
return
// solhint-disable-next-line check-send-result
endpoint.send{ value: messageValue }(
MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),
_refundAddress
);
}
/**
* @dev Internal function to pay the native fee associated with the message.
* @param _nativeFee The native fee to be paid.
* @return nativeFee The amount of native currency paid.
*
* @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,
* this will need to be overridden because msg.value would contain multiple lzFees.
* @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.
* @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.
* @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.
*/
function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {
if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);
return _nativeFee;
}
/**
* @dev Internal function to pay the LZ token fee associated with the message.
* @param _lzTokenFee The LZ token fee to be paid.
*
* @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.
* @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().
*/
function _payLzToken(uint256 _lzTokenFee) internal virtual {
// @dev Cannot cache the token because it is not immutable in the endpoint.
address lzToken = endpoint.lzToken();
if (lzToken == address(0)) revert LzTokenUnavailable();
// Pay LZ token fee by sending tokens to the endpoint.
IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../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.
*
* The initial owner is set to the address provided by the deployer. 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;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @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 {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @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 {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_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
pragma solidity ^0.8.20;
/**
* @dev Helper library for emitting standardized panic codes.
*
* ```solidity
* contract Example {
* using Panic for uint256;
*
* // Use any of the declared internal constants
* function foo() { Panic.GENERIC.panic(); }
*
* // Alternatively
* function foo() { Panic.panic(Panic.GENERIC); }
* }
* ```
*
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
*
* _Available since v5.1._
*/
// slither-disable-next-line unused-state
library Panic {
/// @dev generic / unspecified error
uint256 internal constant GENERIC = 0x00;
/// @dev used by the assert() builtin
uint256 internal constant ASSERT = 0x01;
/// @dev arithmetic underflow or overflow
uint256 internal constant UNDER_OVERFLOW = 0x11;
/// @dev division or modulo by zero
uint256 internal constant DIVISION_BY_ZERO = 0x12;
/// @dev enum conversion error
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
/// @dev invalid encoding in storage
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
/// @dev empty array pop
uint256 internal constant EMPTY_ARRAY_POP = 0x31;
/// @dev array out of bounds access
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
/// @dev resource error (too large allocation or too large array)
uint256 internal constant RESOURCE_ERROR = 0x41;
/// @dev calling invalid internal function
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
/// @dev Reverts with a panic code. Recommended to use with
/// the internal constants with predefined codes.
function panic(uint256 code) internal pure {
assembly ("memory-safe") {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
/**
* @title Percentage
* @author Roberto Cano
* @notice Custom type for Percentage values with associated utility functions
* @dev This contract defines a custom Percentage type and overloaded operators
* to perform arithmetic and comparison operations on Percentage values.
*/
/**
* @dev Custom percentage type as uint256
* @notice This type is used to represent percentage values with high precision
*/
type Percentage is uint256;
/**
* @dev Overridden operators declaration for Percentage
* @notice These operators allow for intuitive arithmetic and comparison operations
* on Percentage values
*/
using {
add as +,
subtract as -,
multiply as *,
divide as /,
lessOrEqualThan as <=,
lessThan as <,
greaterOrEqualThan as >=,
greaterThan as >,
equalTo as ==
} for Percentage global;
/**
* @dev The number of decimals used for the percentage
* This constant defines the precision of the Percentage type
*/
uint256 constant PERCENTAGE_DECIMALS = 18;
/**
* @dev The factor used to scale the percentage
* This constant is used to convert between human-readable percentages
* and the internal representation
*/
uint256 constant PERCENTAGE_FACTOR = 10 ** PERCENTAGE_DECIMALS;
/**
* @dev Percentage of 100% with the given `PERCENTAGE_DECIMALS`
* This constant represents 100% in the Percentage type
*/
Percentage constant PERCENTAGE_100 = Percentage.wrap(100 * PERCENTAGE_FACTOR);
/**
* OPERATOR FUNCTIONS
*/
/**
* @dev Adds two Percentage values
* @param a The first Percentage value
* @param b The second Percentage value
* @return The sum of a and b as a Percentage
*/
function add(Percentage a, Percentage b) pure returns (Percentage) {
return Percentage.wrap(Percentage.unwrap(a) + Percentage.unwrap(b));
}
/**
* @dev Subtracts one Percentage value from another
* @param a The Percentage value to subtract from
* @param b The Percentage value to subtract
* @return The difference between a and b as a Percentage
*/
function subtract(Percentage a, Percentage b) pure returns (Percentage) {
return Percentage.wrap(Percentage.unwrap(a) - Percentage.unwrap(b));
}
/**
* @dev Multiplies two Percentage values
* @param a The first Percentage value
* @param b The second Percentage value
* @return The product of a and b as a Percentage, scaled appropriately
*/
function multiply(Percentage a, Percentage b) pure returns (Percentage) {
return
Percentage.wrap(
(Percentage.unwrap(a) * Percentage.unwrap(b)) /
Percentage.unwrap(PERCENTAGE_100)
);
}
/**
* @dev Divides one Percentage value by another
* @param a The Percentage value to divide
* @param b The Percentage value to divide by
* @return The quotient of a divided by b as a Percentage, scaled appropriately
*/
function divide(Percentage a, Percentage b) pure returns (Percentage) {
return
Percentage.wrap(
(Percentage.unwrap(a) * Percentage.unwrap(PERCENTAGE_100)) /
Percentage.unwrap(b)
);
}
/**
* @dev Checks if one Percentage value is less than or equal to another
* @param a The first Percentage value
* @param b The second Percentage value
* @return True if a is less than or equal to b, false otherwise
*/
function lessOrEqualThan(Percentage a, Percentage b) pure returns (bool) {
return Percentage.unwrap(a) <= Percentage.unwrap(b);
}
/**
* @dev Checks if one Percentage value is less than another
* @param a The first Percentage value
* @param b The second Percentage value
* @return True if a is less than b, false otherwise
*/
function lessThan(Percentage a, Percentage b) pure returns (bool) {
return Percentage.unwrap(a) < Percentage.unwrap(b);
}
/**
* @dev Checks if one Percentage value is greater than or equal to another
* @param a The first Percentage value
* @param b The second Percentage value
* @return True if a is greater than or equal to b, false otherwise
*/
function greaterOrEqualThan(Percentage a, Percentage b) pure returns (bool) {
return Percentage.unwrap(a) >= Percentage.unwrap(b);
}
/**
* @dev Checks if one Percentage value is greater than another
* @param a The first Percentage value
* @param b The second Percentage value
* @return True if a is greater than b, false otherwise
*/
function greaterThan(Percentage a, Percentage b) pure returns (bool) {
return Percentage.unwrap(a) > Percentage.unwrap(b);
}
/**
* @dev Checks if two Percentage values are equal
* @param a The first Percentage value
* @param b The second Percentage value
* @return True if a is equal to b, false otherwise
*/
function equalTo(Percentage a, Percentage b) pure returns (bool) {
return Percentage.unwrap(a) == Percentage.unwrap(b);
}
/**
* @dev Alias for equalTo function
* @param a The first Percentage value
* @param b The second Percentage value
* @return True if a is equal to b, false otherwise
*/
function equals(Percentage a, Percentage b) pure returns (bool) {
return Percentage.unwrap(a) == Percentage.unwrap(b);
}
/**
* @dev Converts a uint256 value to a Percentage
* @param value The uint256 value to convert
* @return The input value as a Percentage
*/
function toPercentage(uint256 value) pure returns (Percentage) {
return Percentage.wrap(value * PERCENTAGE_FACTOR);
}
/**
* @dev Converts a Percentage value to a uint256
* @param value The Percentage value to convert
* @return The Percentage value as a uint256
*/
function fromPercentage(Percentage value) pure returns (uint256) {
return Percentage.unwrap(value) / PERCENTAGE_FACTOR;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {IAccessControlErrors} from "../interfaces/IAccessControlErrors.sol";
import {ContractSpecificRoles, IProtocolAccessManager} from "../interfaces/IProtocolAccessManager.sol";
import {ProtocolAccessManager} from "./ProtocolAccessManager.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
/**
* @title ProtocolAccessManaged
* @notice This contract provides role-based access control functionality for protocol contracts
* by interfacing with a central ProtocolAccessManager.
*
* @dev This contract is meant to be inherited by other protocol contracts that need
* role-based access control. It provides modifiers and utilities to check various roles.
*
* The contract supports several key roles through modifiers:
* 1. GOVERNOR_ROLE: System-wide administrators
* 2. KEEPER_ROLE: Routine maintenance operators (contract-specific)
* 3. SUPER_KEEPER_ROLE: Advanced maintenance operators (global)
* 4. CURATOR_ROLE: Fleet-specific managers
* 5. GUARDIAN_ROLE: Emergency response operators
* 6. DECAY_CONTROLLER_ROLE: Specific role for decay management
* 7. ADMIRALS_QUARTERS_ROLE: Specific role for admirals quarters bundler contract
*
* Usage:
* - Inherit from this contract to gain access to role-checking modifiers
* - Use modifiers like onlyGovernor, onlyKeeper, etc. to protect functions
* - Access the internal _accessManager to perform custom role checks
*
* Security Considerations:
* - The contract validates the access manager address during construction
* - All role checks are performed against the immutable access manager instance
* - Contract-specific roles are generated using the contract's address to prevent conflicts
*/
contract ProtocolAccessManaged is IAccessControlErrors, Context {
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/// @notice Role identifier for protocol governors - highest privilege level with admin capabilities
bytes32 public constant GOVERNOR_ROLE = keccak256("GOVERNOR_ROLE");
/// @notice Role identifier for super keepers who can globally perform fleet maintanence roles
bytes32 public constant SUPER_KEEPER_ROLE = keccak256("SUPER_KEEPER_ROLE");
/**
* @notice Role identifier for protocol guardians
* @dev Guardians have emergency powers across multiple protocol components:
* - Can pause/unpause Fleet operations for security
* - Can pause/unpause TipJar operations
* - Can cancel governance proposals on SummerGovernor even if they don't meet normal cancellation requirements
* - Can cancel TipJar proposals
*
* The guardian role serves as an emergency backstop to protect the protocol, but with less
* privilege than governors.
*/
bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
/**
* @notice Role identifier for decay controller
* @dev This role allows the decay controller to manage the decay of user voting power
*/
bytes32 public constant DECAY_CONTROLLER_ROLE =
keccak256("DECAY_CONTROLLER_ROLE");
/**
* @notice Role identifier for admirals quarters bundler contract
* @dev This role allows Admirals Quarters to unstake and withdraw assets from fleets, on behalf of users
* @dev Withdrawn tokens go straight to users wallet, lowering the risk of manipulation if the role is compromised
*/
bytes32 public constant ADMIRALS_QUARTERS_ROLE =
keccak256("ADMIRALS_QUARTERS_ROLE");
/*//////////////////////////////////////////////////////////////
STATE VARIABLES
//////////////////////////////////////////////////////////////*/
/// @notice The ProtocolAccessManager instance used for access control
ProtocolAccessManager internal immutable _accessManager;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
/**
* @notice Initializes the ProtocolAccessManaged contract
* @param accessManager Address of the ProtocolAccessManager contract
* @dev Validates the provided accessManager address and initializes the _accessManager
*/
constructor(address accessManager) {
if (accessManager == address(0)) {
revert InvalidAccessManagerAddress(address(0));
}
if (
!IERC165(accessManager).supportsInterface(
type(IProtocolAccessManager).interfaceId
)
) {
revert InvalidAccessManagerAddress(accessManager);
}
_accessManager = ProtocolAccessManager(accessManager);
}
/*//////////////////////////////////////////////////////////////
MODIFIERS
//////////////////////////////////////////////////////////////*/
/**
* @notice Modifier to restrict access to governors only
*
* @dev Modifier to check that the caller has the Governor role
* @custom:internal-logic
* - Checks if the caller has the GOVERNOR_ROLE in the access manager
* @custom:effects
* - Reverts if the caller doesn't have the GOVERNOR_ROLE
* - Allows the function to proceed if the caller has the role
* @custom:security-considerations
* - Ensures that only authorized governors can access critical functions
* - Relies on the correct setup of the access manager
*/
modifier onlyGovernor() {
if (!_accessManager.hasRole(GOVERNOR_ROLE, msg.sender)) {
revert CallerIsNotGovernor(msg.sender);
}
_;
}
/**
* @notice Modifier to restrict access to keepers only
* @dev Modifier to check that the caller has the Keeper role
* @custom:internal-logic
* - Checks if the caller has either the contract-specific KEEPER_ROLE or the SUPER_KEEPER_ROLE
* @custom:effects
* - Reverts if the caller doesn't have either of the required roles
* - Allows the function to proceed if the caller has one of the roles
* @custom:security-considerations
* - Ensures that only authorized keepers can access maintenance functions
* - Allows for both contract-specific and super keepers
* @custom:gas-considerations
* - Performs two role checks, which may impact gas usage
*/
modifier onlyKeeper() {
if (
!_accessManager.hasRole(
generateRole(ContractSpecificRoles.KEEPER_ROLE, address(this)),
msg.sender
) && !_accessManager.hasRole(SUPER_KEEPER_ROLE, msg.sender)
) {
revert CallerIsNotKeeper(msg.sender);
}
_;
}
/**
* @notice Modifier to restrict access to super keepers only
* @dev Modifier to check that the caller has the Super Keeper role
* @custom:internal-logic
* - Checks if the caller has the SUPER_KEEPER_ROLE in the access manager
* @custom:effects
* - Reverts if the caller doesn't have the SUPER_KEEPER_ROLE
* - Allows the function to proceed if the caller has the role
* @custom:security-considerations
* - Ensures that only authorized super keepers can access advanced maintenance functions
* - Relies on the correct setup of the access manager
*/
modifier onlySuperKeeper() {
if (!_accessManager.hasRole(SUPER_KEEPER_ROLE, msg.sender)) {
revert CallerIsNotSuperKeeper(msg.sender);
}
_;
}
/**
* @notice Modifier to restrict access to curators only
* @param fleetAddress The address of the fleet to check the curator role for
* @dev Checks if the caller has the contract-specific CURATOR_ROLE
*/
modifier onlyCurator(address fleetAddress) {
if (
fleetAddress == address(0) ||
!_accessManager.hasRole(
generateRole(ContractSpecificRoles.CURATOR_ROLE, fleetAddress),
msg.sender
)
) {
revert CallerIsNotCurator(msg.sender);
}
_;
}
/**
* @notice Modifier to restrict access to guardians only
* @dev Modifier to check that the caller has the Guardian role
* @custom:internal-logic
* - Checks if the caller has the GUARDIAN_ROLE in the access manager
* @custom:effects
* - Reverts if the caller doesn't have the GUARDIAN_ROLE
* - Allows the function to proceed if the caller has the role
* @custom:security-considerations
* - Ensures that only authorized guardians can access emergency functions
* - Relies on the correct setup of the access manager
*/
modifier onlyGuardian() {
if (!_accessManager.hasRole(GUARDIAN_ROLE, msg.sender)) {
revert CallerIsNotGuardian(msg.sender);
}
_;
}
/**
* @notice Modifier to restrict access to either guardians or governors
* @dev Modifier to check that the caller has either the Guardian or Governor role
* @custom:internal-logic
* - Checks if the caller has either the GUARDIAN_ROLE or the GOVERNOR_ROLE
* @custom:effects
* - Reverts if the caller doesn't have either of the required roles
* - Allows the function to proceed if the caller has one of the roles
* @custom:security-considerations
* - Ensures that only authorized guardians or governors can access certain functions
* - Provides flexibility for functions that can be accessed by either role
* @custom:gas-considerations
* - Performs two role checks, which may impact gas usage
*/
modifier onlyGuardianOrGovernor() {
if (
!_accessManager.hasRole(GUARDIAN_ROLE, msg.sender) &&
!_accessManager.hasRole(GOVERNOR_ROLE, msg.sender)
) {
revert CallerIsNotGuardianOrGovernor(msg.sender);
}
_;
}
/**
* @notice Modifier to restrict access to decay controllers only
*/
modifier onlyDecayController() {
if (!_accessManager.hasRole(DECAY_CONTROLLER_ROLE, msg.sender)) {
revert CallerIsNotDecayController(msg.sender);
}
_;
}
/**
* @notice Modifier to restrict access to foundation only
* @dev Modifier to check that the caller has the Foundation role
* @custom:security-considerations
* - Ensures that only the Foundation can access vesting and related functions
* - Relies on the correct setup of the access manager
*/
modifier onlyFoundation() {
if (
!_accessManager.hasRole(
_accessManager.FOUNDATION_ROLE(),
msg.sender
)
) {
revert CallerIsNotFoundation(msg.sender);
}
_;
}
/*//////////////////////////////////////////////////////////////
PUBLIC FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Generates a role identifier for a specific contract and role
* @param roleName The name of the role
* @param roleTargetContract The address of the contract the role is for
* @return The generated role identifier
* @dev This function is used to create unique role identifiers for contract-specific roles
*/
function generateRole(
ContractSpecificRoles roleName,
address roleTargetContract
) public pure returns (bytes32) {
return keccak256(abi.encodePacked(roleName, roleTargetContract));
}
/**
* @notice Checks if an account has the Admirals Quarters role
* @param account The address to check
* @return bool True if the account has the Admirals Quarters role
*/
function hasAdmiralsQuartersRole(
address account
) public view returns (bool) {
return _accessManager.hasRole(ADMIRALS_QUARTERS_ROLE, account);
}
/*//////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Helper function to check if an address has the Governor role
* @param account The address to check
* @return bool True if the address has the Governor role
*/
function _isGovernor(address account) internal view returns (bool) {
return _accessManager.hasRole(GOVERNOR_ROLE, account);
}
function _isDecayController(address account) internal view returns (bool) {
return _accessManager.hasRole(DECAY_CONTROLLER_ROLE, account);
}
/**
* @notice Helper function to check if an address has the Foundation role
* @param account The address to check
* @return bool True if the address has the Foundation role
*/
function _isFoundation(address account) internal view returns (bool) {
return
_accessManager.hasRole(_accessManager.FOUNDATION_ROLE(), account);
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {ContractSpecificRoles, IProtocolAccessManager} from "../interfaces/IProtocolAccessManager.sol";
import {LimitedAccessControl} from "./LimitedAccessControl.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
/**
* @title ProtocolAccessManager
* @notice This contract is the central authority for access control within the protocol.
* It defines and manages various roles that govern different aspects of the system.
*
* @dev This contract extends LimitedAccessControl, which restricts direct role management.
* Roles are typically assigned during deployment or through governance proposals.
*
* The contract defines four main roles:
* 1. GOVERNOR_ROLE: System-wide administrators
* 2. KEEPER_ROLE: Routine maintenance operators
* 3. SUPER_KEEPER_ROLE: Advanced maintenance operators
* 4. COMMANDER_ROLE: Managers of specific protocol components (Arks)
* 5. ADMIRALS_QUARTERS_ROLE: Specific role for admirals quarters bundler contract
* Role Hierarchy and Management:
* - The GOVERNOR_ROLE is at the top of the hierarchy and can manage all other roles.
* - Other roles cannot manage roles directly due to LimitedAccessControl restrictions.
* - Role assignments are typically done through governance proposals or during initial setup.
*
* Usage in the System:
* - Other contracts in the system inherit from ProtocolAccessManaged, which checks permissions
* against this ProtocolAccessManager.
* - Critical functions in various contracts are protected by role-based modifiers
* (e.g., onlyGovernor, onlyKeeper, etc.) which query this contract for permissions.
*
* Security Considerations:
* - The GOVERNOR_ROLE has significant power and should be managed carefully, potentially
* through a multi-sig wallet or governance contract.
* - The SUPER_KEEPER_ROLE has elevated privileges and should be assigned judiciously.
* - The COMMANDER_ROLE is not directly manageable through this contract but is used
* in other parts of the system for specific access control.
*/
contract ProtocolAccessManager is IProtocolAccessManager, LimitedAccessControl {
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/// @notice Role identifier for protocol governors - highest privilege level with admin capabilities
bytes32 public constant GOVERNOR_ROLE = keccak256("GOVERNOR_ROLE");
/// @notice Role identifier for super keepers who can globally perform fleet maintanence roles
bytes32 public constant SUPER_KEEPER_ROLE = keccak256("SUPER_KEEPER_ROLE");
/**
* @notice Role identifier for protocol guardians
* @dev Guardians have emergency powers across multiple protocol components:
* - Can pause/unpause Fleet operations for security
* - Can pause/unpause TipJar operations
* - Can cancel governance proposals on SummerGovernor even if they don't meet normal cancellation requirements
* - Can cancel TipJar proposals
*
* The guardian role serves as an emergency backstop to protect the protocol, but with less
* privilege than governors.
*/
bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
/**
* @notice Role identifier for decay controller
* @dev This role allows the decay controller to manage the decay of user voting power
*/
bytes32 public constant DECAY_CONTROLLER_ROLE =
keccak256("DECAY_CONTROLLER_ROLE");
/**
* @notice Role identifier for admirals quarters bundler contract
* @dev This role allows Admirals Quarters to unstake and withdraw assets from fleets, on behalf of users
* @dev Withdrawn tokens go straight to users wallet, lowering the risk of manipulation if the role is compromised
*/
bytes32 public constant ADMIRALS_QUARTERS_ROLE =
keccak256("ADMIRALS_QUARTERS_ROLE");
/// @notice Minimum allowed guardian expiration period (7 days)
uint256 public constant MIN_GUARDIAN_EXPIRY = 7 days;
/// @notice Maximum allowed guardian expiration period (180 days)
uint256 public constant MAX_GUARDIAN_EXPIRY = 180 days;
/// @notice Role identifier for the Foundation which manages vesting wallets and related operations
bytes32 public constant FOUNDATION_ROLE = keccak256("FOUNDATION_ROLE");
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
/**
* @notice Initializes the ProtocolAccessManager contract
* @param governor Address of the initial governor
* @dev Grants the governor address the GOVERNOR_ROLE
*/
constructor(address governor) {
_grantRole(GOVERNOR_ROLE, governor);
}
/**
* @dev Modifier to check that the caller has the Governor role
*/
modifier onlyGovernor() {
if (!hasRole(GOVERNOR_ROLE, msg.sender)) {
revert CallerIsNotGovernor(msg.sender);
}
_;
}
/*//////////////////////////////////////////////////////////////
PUBLIC FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Checks if the contract supports a given interface
* @dev Overrides the supportsInterface function from AccessControl
* @param interfaceId The interface identifier, as specified in ERC-165
* @return bool True if the contract supports the interface, false otherwise
*
* This function supports:
* - IProtocolAccessManager interface
* - All interfaces supported by the parent AccessControl contract
*/
function supportsInterface(
bytes4 interfaceId
) public view override returns (bool) {
return
interfaceId == type(IProtocolAccessManager).interfaceId ||
super.supportsInterface(interfaceId);
}
/// @inheritdoc IProtocolAccessManager
function grantGovernorRole(address account) external onlyGovernor {
_grantRole(GOVERNOR_ROLE, account);
}
/// @inheritdoc IProtocolAccessManager
function revokeGovernorRole(address account) external onlyGovernor {
_revokeRole(GOVERNOR_ROLE, account);
}
/*//////////////////////////////////////////////////////////////
EXTERNAL GOVERNOR FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @inheritdoc IProtocolAccessManager
function grantSuperKeeperRole(address account) external onlyGovernor {
_grantRole(SUPER_KEEPER_ROLE, account);
}
/// @inheritdoc IProtocolAccessManager
function grantGuardianRole(address account) external onlyGovernor {
_grantRole(GUARDIAN_ROLE, account);
}
/// @inheritdoc IProtocolAccessManager
function revokeGuardianRole(address account) external onlyGovernor {
_revokeRole(GUARDIAN_ROLE, account);
}
/// @inheritdoc IProtocolAccessManager
function revokeSuperKeeperRole(address account) external onlyGovernor {
_revokeRole(SUPER_KEEPER_ROLE, account);
}
/// @inheritdoc IProtocolAccessManager
function grantContractSpecificRole(
ContractSpecificRoles roleName,
address roleTargetContract,
address roleOwner
) public onlyGovernor {
bytes32 role = generateRole(roleName, roleTargetContract);
_grantRole(role, roleOwner);
}
/// @inheritdoc IProtocolAccessManager
function revokeContractSpecificRole(
ContractSpecificRoles roleName,
address roleTargetContract,
address roleOwner
) public onlyGovernor {
bytes32 role = generateRole(roleName, roleTargetContract);
_revokeRole(role, roleOwner);
}
/// @inheritdoc IProtocolAccessManager
function grantCuratorRole(
address fleetCommanderAddress,
address account
) public onlyGovernor {
grantContractSpecificRole(
ContractSpecificRoles.CURATOR_ROLE,
fleetCommanderAddress,
account
);
}
/// @inheritdoc IProtocolAccessManager
function revokeCuratorRole(
address fleetCommanderAddress,
address account
) public onlyGovernor {
revokeContractSpecificRole(
ContractSpecificRoles.CURATOR_ROLE,
fleetCommanderAddress,
account
);
}
/// @inheritdoc IProtocolAccessManager
function grantKeeperRole(
address fleetCommanderAddress,
address account
) public onlyGovernor {
grantContractSpecificRole(
ContractSpecificRoles.KEEPER_ROLE,
fleetCommanderAddress,
account
);
}
/// @inheritdoc IProtocolAccessManager
function revokeKeeperRole(
address fleetCommanderAddress,
address account
) public onlyGovernor {
revokeContractSpecificRole(
ContractSpecificRoles.KEEPER_ROLE,
fleetCommanderAddress,
account
);
}
/// @inheritdoc IProtocolAccessManager
function grantCommanderRole(
address arkAddress,
address account
) public onlyGovernor {
grantContractSpecificRole(
ContractSpecificRoles.COMMANDER_ROLE,
arkAddress,
account
);
}
/// @inheritdoc IProtocolAccessManager
function revokeCommanderRole(
address arkAddress,
address account
) public onlyGovernor {
revokeContractSpecificRole(
ContractSpecificRoles.COMMANDER_ROLE,
arkAddress,
account
);
}
/// @inheritdoc IProtocolAccessManager
function grantDecayControllerRole(address account) public onlyGovernor {
_grantRole(DECAY_CONTROLLER_ROLE, account);
}
/// @inheritdoc IProtocolAccessManager
function revokeDecayControllerRole(address account) public onlyGovernor {
_revokeRole(DECAY_CONTROLLER_ROLE, account);
}
/*//////////////////////////////////////////////////////////////
PUBLIC FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @inheritdoc IProtocolAccessManager
function selfRevokeContractSpecificRole(
ContractSpecificRoles roleName,
address roleTargetContract
) public {
bytes32 role = generateRole(roleName, roleTargetContract);
if (!hasRole(role, msg.sender)) {
revert CallerIsNotContractSpecificRole(msg.sender, role);
}
_revokeRole(role, msg.sender);
}
/// @inheritdoc IProtocolAccessManager
function generateRole(
ContractSpecificRoles roleName,
address roleTargetContract
) public pure returns (bytes32) {
return keccak256(abi.encodePacked(roleName, roleTargetContract));
}
/// @inheritdoc IProtocolAccessManager
function grantAdmiralsQuartersRole(
address account
) external onlyRole(GOVERNOR_ROLE) {
_grantRole(ADMIRALS_QUARTERS_ROLE, account);
}
/// @inheritdoc IProtocolAccessManager
function revokeAdmiralsQuartersRole(
address account
) external onlyRole(GOVERNOR_ROLE) {
_revokeRole(ADMIRALS_QUARTERS_ROLE, account);
}
mapping(address guardian => uint256 expirationTimestamp)
public guardianExpirations;
/**
* @notice Checks if an account is an active guardian (has role and not expired)
* @param account Address to check
* @return bool True if account is an active guardian
*/
function isActiveGuardian(address account) public view returns (bool) {
return
hasRole(GUARDIAN_ROLE, account) &&
guardianExpirations[account] > block.timestamp;
}
/**
* @notice Sets the expiration timestamp for a guardian
* @param account Guardian address
* @param expiration Timestamp when guardian powers expire
* @dev The expiration period (time from now until expiration) must be between MIN_GUARDIAN_EXPIRY and MAX_GUARDIAN_EXPIRY
* This ensures guardians can't be immediately removed (protecting against malicious proposals) while still
* allowing for their eventual phase-out (protecting against malicious guardians)
*/
function setGuardianExpiration(
address account,
uint256 expiration
) external onlyRole(GOVERNOR_ROLE) {
if (!hasRole(GUARDIAN_ROLE, account)) {
revert CallerIsNotGuardian(account);
}
uint256 expiryPeriod = expiration - block.timestamp;
if (
expiryPeriod < MIN_GUARDIAN_EXPIRY ||
expiryPeriod > MAX_GUARDIAN_EXPIRY
) {
revert InvalidGuardianExpiryPeriod(
expiryPeriod,
MIN_GUARDIAN_EXPIRY,
MAX_GUARDIAN_EXPIRY
);
}
guardianExpirations[account] = expiration;
emit GuardianExpirationSet(account, expiration);
}
/**
* @inheritdoc IProtocolAccessManager
*/
function hasRole(
bytes32 role,
address account
)
public
view
virtual
override(IProtocolAccessManager, AccessControl)
returns (bool)
{
return super.hasRole(role, account);
}
/// @inheritdoc IProtocolAccessManager
function getGuardianExpiration(
address account
) external view returns (uint256 expiration) {
if (!hasRole(GUARDIAN_ROLE, account)) {
revert CallerIsNotGuardian(account);
}
return guardianExpirations[account];
}
/// @inheritdoc IProtocolAccessManager
function grantFoundationRole(address account) external onlyGovernor {
_grantRole(FOUNDATION_ROLE, account);
}
/// @inheritdoc IProtocolAccessManager
function revokeFoundationRole(address account) external onlyGovernor {
_revokeRole(FOUNDATION_ROLE, account);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {StorageSlot} from "./StorageSlot.sol";
/**
* @dev Variant of {ReentrancyGuard} that uses transient storage.
*
* NOTE: This variant only works on networks where EIP-1153 is available.
*/
abstract contract ReentrancyGuardTransient {
using StorageSlot for *;
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant REENTRANCY_GUARD_STORAGE =
0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
/**
* @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
if (_reentrancyGuardEntered()) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
REENTRANCY_GUARD_STORAGE.asBoolean().tstore(true);
}
function _nonReentrantAfter() private {
REENTRANCY_GUARD_STORAGE.asBoolean().tstore(false);
}
/**
* @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 REENTRANCY_GUARD_STORAGE.asBoolean().tload();
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 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 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @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.encodeCall(token.transfer, (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.encodeCall(token.transferFrom, (from, to, 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);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @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.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @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 {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @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 silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ShortStrings.sol)
pragma solidity ^0.8.20;
import {StorageSlot} from "./StorageSlot.sol";
// | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
// | length | 0x BB |
type ShortString is bytes32;
/**
* @dev This library provides functions to convert short memory strings
* into a `ShortString` type that can be used as an immutable variable.
*
* Strings of arbitrary length can be optimized using this library if
* they are short enough (up to 31 bytes) by packing them with their
* length (1 byte) in a single EVM word (32 bytes). Additionally, a
* fallback mechanism can be used for every other case.
*
* Usage example:
*
* ```solidity
* contract Named {
* using ShortStrings for *;
*
* ShortString private immutable _name;
* string private _nameFallback;
*
* constructor(string memory contractName) {
* _name = contractName.toShortStringWithFallback(_nameFallback);
* }
*
* function name() external view returns (string memory) {
* return _name.toStringWithFallback(_nameFallback);
* }
* }
* ```
*/
library ShortStrings {
// Used as an identifier for strings longer than 31 bytes.
bytes32 private constant FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
error StringTooLong(string str);
error InvalidShortString();
/**
* @dev Encode a string of at most 31 chars into a `ShortString`.
*
* This will trigger a `StringTooLong` error is the input string is too long.
*/
function toShortString(string memory str) internal pure returns (ShortString) {
bytes memory bstr = bytes(str);
if (bstr.length > 31) {
revert StringTooLong(str);
}
return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
}
/**
* @dev Decode a `ShortString` back to a "normal" string.
*/
function toString(ShortString sstr) internal pure returns (string memory) {
uint256 len = byteLength(sstr);
// using `new string(len)` would work locally but is not memory safe.
string memory str = new string(32);
assembly ("memory-safe") {
mstore(str, len)
mstore(add(str, 0x20), sstr)
}
return str;
}
/**
* @dev Return the length of a `ShortString`.
*/
function byteLength(ShortString sstr) internal pure returns (uint256) {
uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
if (result > 31) {
revert InvalidShortString();
}
return result;
}
/**
* @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
*/
function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
if (bytes(value).length < 32) {
return toShortString(value);
} else {
StorageSlot.getStringSlot(store).value = value;
return ShortString.wrap(FALLBACK_SENTINEL);
}
}
/**
* @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*/
function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return toString(value);
} else {
return store;
}
}
/**
* @dev Return the length of a string that was encoded to `ShortString` or written to storage using
* {setWithFallback}.
*
* WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
* actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
*/
function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return byteLength(value);
} else {
return bytes(store).length;
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/SignatureChecker.sol)
pragma solidity ^0.8.20;
import {ECDSA} from "./ECDSA.sol";
import {IERC1271} from "../../interfaces/IERC1271.sol";
/**
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
* signatures from externally owned accounts (EOAs) as well as ERC-1271 signatures from smart contract wallets like
* Argent and Safe Wallet (previously Gnosis Safe).
*/
library SignatureChecker {
/**
* @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
* signature is validated against that smart contract using ERC-1271, otherwise it's validated using `ECDSA.recover`.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
if (signer.code.length == 0) {
(address recovered, ECDSA.RecoverError err, ) = ECDSA.tryRecover(hash, signature);
return err == ECDSA.RecoverError.NoError && recovered == signer;
} else {
return isValidERC1271SignatureNow(signer, hash, signature);
}
}
/**
* @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
* against the signer smart contract using ERC-1271.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidERC1271SignatureNow(
address signer,
bytes32 hash,
bytes memory signature
) internal view returns (bool) {
(bool success, bytes memory result) = signer.staticcall(
abi.encodeCall(IERC1271.isValidSignature, (hash, signature))
);
return (success &&
result.length >= 32 &&
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, int256 a, int256 b) internal pure returns (int256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * int256(SafeCast.toUint(condition)));
}
}
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return ternary(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 {
// Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson.
// Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift,
// taking advantage of the most significant (or "sign" bit) in two's complement representation.
// This opcode adds new most significant bits set to the value of the previous most significant bit. As a result,
// the mask will either be `bytes32(0)` (if n is positive) or `~bytes32(0)` (if n is negative).
int256 mask = n >> 255;
// A `bytes32(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it.
return uint256((n + mask) ^ mask);
}
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
/**
* @title StakingRewardsManager
* @notice Contract for managing staking rewards with multiple reward tokens in the Summer protocol
* @dev Implements IStakingRewards interface and inherits from ReentrancyGuardTransient and ProtocolAccessManaged
* @dev Inspired by Synthetix's StakingRewards contract:
* https://github.com/Synthetixio/synthetix/blob/v2.101.3/contracts/StakingRewards.sol
*/
import {IStakingRewardsManagerBase} from "../interfaces/IStakingRewardsManagerBase.sol";
import {ProtocolAccessManaged} from "@summerfi/access-contracts/contracts/ProtocolAccessManaged.sol";
import {ReentrancyGuardTransient} from "@summerfi/dependencies/openzeppelin-next/ReentrancyGuardTransient.sol";
import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {Constants} from "@summerfi/constants/Constants.sol";
import {ERC20Wrapper} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Wrapper.sol";
/**
* @title StakingRewards
* @notice Contract for managing staking rewards with multiple reward tokens in the Summer protocol
* @dev Implements IStakingRewards interface and inherits from ReentrancyGuardTransient and ProtocolAccessManaged
*/
abstract contract StakingRewardsManagerBase is
IStakingRewardsManagerBase,
ReentrancyGuardTransient,
ProtocolAccessManaged
{
using SafeERC20 for IERC20;
using EnumerableSet for EnumerableSet.AddressSet;
struct RewardData {
uint256 periodFinish;
uint256 rewardRate;
uint256 rewardsDuration;
uint256 lastUpdateTime;
uint256 rewardPerTokenStored;
}
/*//////////////////////////////////////////////////////////////
STATE VARIABLES
//////////////////////////////////////////////////////////////*/
/* @notice List of all reward tokens supported by this contract */
EnumerableSet.AddressSet internal _rewardTokensList;
/* @notice The token that users stake to earn rewards */
address public immutable stakingToken;
/* @notice Mapping of reward token to its reward distribution data */
mapping(address rewardToken => RewardData data) public rewardData;
/* @notice Tracks the last reward per token paid to each user for each reward token */
mapping(address rewardToken => mapping(address account => uint256 rewardPerTokenPaid))
public userRewardPerTokenPaid;
/* @notice Tracks the unclaimed rewards for each user for each reward token */
mapping(address rewardToken => mapping(address account => uint256 rewardAmount))
public rewards;
/* @notice Total amount of tokens staked in the contract */
uint256 public totalSupply;
mapping(address account => uint256 balance) internal _balances;
uint256 private constant MAX_REWARD_DURATION = 360 days; // 1 year
/*//////////////////////////////////////////////////////////////
MODIFIERS
//////////////////////////////////////////////////////////////*/
modifier updateReward(address account) virtual {
_updateReward(account);
_;
}
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
/**
* @notice Initializes the StakingRewards contract
* @param accessManager The address of the access manager
*/
constructor(address accessManager) ProtocolAccessManaged(accessManager) {}
/*//////////////////////////////////////////////////////////////
VIEWS
//////////////////////////////////////////////////////////////*/
/// @inheritdoc IStakingRewardsManagerBase
function rewardTokens(
uint256 index
) external view override returns (address) {
if (index >= _rewardTokensList.length()) revert IndexOutOfBounds();
address rewardTokenAddress = _rewardTokensList.at(index);
return rewardTokenAddress;
}
/// @inheritdoc IStakingRewardsManagerBase
function rewardTokensLength() external view returns (uint256) {
return _rewardTokensList.length();
}
/// @inheritdoc IStakingRewardsManagerBase
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/// @inheritdoc IStakingRewardsManagerBase
function lastTimeRewardApplicable(
address rewardToken
) public view returns (uint256) {
return
block.timestamp < rewardData[rewardToken].periodFinish
? block.timestamp
: rewardData[rewardToken].periodFinish;
}
/// @inheritdoc IStakingRewardsManagerBase
function rewardPerToken(address rewardToken) public view returns (uint256) {
if (totalSupply == 0) {
return rewardData[rewardToken].rewardPerTokenStored;
}
return
rewardData[rewardToken].rewardPerTokenStored +
((lastTimeRewardApplicable(rewardToken) -
rewardData[rewardToken].lastUpdateTime) *
rewardData[rewardToken].rewardRate) /
totalSupply;
}
/// @inheritdoc IStakingRewardsManagerBase
function earned(
address account,
address rewardToken
) public view virtual returns (uint256) {
return _earned(account, rewardToken);
}
/// @inheritdoc IStakingRewardsManagerBase
function getRewardForDuration(
address rewardToken
) external view returns (uint256) {
RewardData storage data = rewardData[rewardToken];
if (block.timestamp >= data.periodFinish) {
return (data.rewardRate * data.rewardsDuration) / Constants.WAD;
}
// For active periods, calculate remaining rewards plus any new rewards
uint256 remaining = data.periodFinish - block.timestamp;
return (data.rewardRate * remaining) / Constants.WAD;
}
/// @inheritdoc IStakingRewardsManagerBase
function isRewardToken(address rewardToken) external view returns (bool) {
return _isRewardToken(rewardToken);
}
/*//////////////////////////////////////////////////////////////
MUTATIVE FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @inheritdoc IStakingRewardsManagerBase
function stake(uint256 amount) external virtual updateReward(_msgSender()) {
_stake(_msgSender(), _msgSender(), amount);
}
/// @inheritdoc IStakingRewardsManagerBase
function unstake(
uint256 amount
) external virtual updateReward(_msgSender()) {
_unstake(_msgSender(), _msgSender(), amount);
}
/// @inheritdoc IStakingRewardsManagerBase
function getReward() public virtual nonReentrant {
uint256 rewardTokenCount = _rewardTokensList.length();
for (uint256 i = 0; i < rewardTokenCount; i++) {
address rewardTokenAddress = _rewardTokensList.at(i);
_getReward(_msgSender(), rewardTokenAddress);
}
}
/// @inheritdoc IStakingRewardsManagerBase
function getReward(address rewardToken) public virtual nonReentrant {
if (!_isRewardToken(rewardToken)) revert RewardTokenDoesNotExist();
_getReward(_msgSender(), rewardToken);
}
/// @inheritdoc IStakingRewardsManagerBase
function exit() external virtual {
getReward();
_unstake(_msgSender(), _msgSender(), _balances[_msgSender()]);
}
/// @notice Claims rewards for a specific account
/// @param account The address to claim rewards for
function getRewardFor(address account) public virtual nonReentrant {
uint256 rewardTokenCount = _rewardTokensList.length();
for (uint256 i = 0; i < rewardTokenCount; i++) {
address rewardTokenAddress = _rewardTokensList.at(i);
_getReward(account, rewardTokenAddress);
}
}
/// @notice Claims rewards for a specific account and specific reward token
/// @param account The address to claim rewards for
/// @param rewardToken The address of the reward token to claim
function getRewardFor(
address account,
address rewardToken
) public virtual nonReentrant {
if (!_isRewardToken(rewardToken)) revert RewardTokenDoesNotExist();
_getReward(account, rewardToken);
}
/*//////////////////////////////////////////////////////////////
RESTRICTED FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @inheritdoc IStakingRewardsManagerBase
function notifyRewardAmount(
address rewardToken,
uint256 reward,
uint256 newRewardsDuration
) external virtual onlyGovernor updateReward(address(0)) {
_notifyRewardAmount(rewardToken, reward, newRewardsDuration);
}
/// @inheritdoc IStakingRewardsManagerBase
function setRewardsDuration(
address rewardToken,
uint256 _rewardsDuration
) external onlyGovernor {
if (!_isRewardToken(rewardToken)) {
revert RewardTokenDoesNotExist();
}
if (_rewardsDuration == 0) {
revert RewardsDurationCannotBeZero();
}
if (_rewardsDuration > MAX_REWARD_DURATION) {
revert RewardsDurationTooLong();
}
RewardData storage data = rewardData[rewardToken];
if (block.timestamp <= data.periodFinish) {
revert RewardPeriodNotComplete();
}
data.rewardsDuration = _rewardsDuration;
emit RewardsDurationUpdated(address(rewardToken), _rewardsDuration);
}
/// @notice Removes a reward token from the list of reward tokens
/// @param rewardToken The address of the reward token to remove
function removeRewardToken(address rewardToken) external onlyGovernor {
if (!_isRewardToken(rewardToken)) {
revert RewardTokenDoesNotExist();
}
if (block.timestamp <= rewardData[rewardToken].periodFinish) {
revert RewardPeriodNotComplete();
}
// Check if all tokens have been claimed, allowing a small dust balance
uint256 remainingBalance = IERC20(rewardToken).balanceOf(address(this));
uint256 dustThreshold;
try IERC20Metadata(address(rewardToken)).decimals() returns (
uint8 decimals
) {
// For tokens with 4 or fewer decimals, use a minimum threshold of 1
// For tokens with more decimals, use 0.01% of 1 token
if (decimals <= 4) {
dustThreshold = 1;
} else {
dustThreshold = 10 ** (decimals - 4); // 0.01% of 1 token
}
} catch {
dustThreshold = 1e14; // Default threshold for tokens without decimals
}
if (remainingBalance > dustThreshold) {
revert RewardTokenStillHasBalance(remainingBalance);
}
// Remove the token from the rewardTokens map
bool success = _rewardTokensList.remove(address(rewardToken));
if (!success) revert RewardTokenDoesNotExist();
emit RewardTokenRemoved(address(rewardToken));
}
/*//////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////*/
function _isRewardToken(address rewardToken) internal view returns (bool) {
return _rewardTokensList.contains(rewardToken);
}
function _stake(
address staker,
address receiver,
uint256 amount
) internal virtual {
if (receiver == address(0)) revert CannotStakeToZeroAddress();
if (amount == 0) revert CannotStakeZero();
if (address(stakingToken) == address(0)) {
revert StakingTokenNotInitialized();
}
totalSupply += amount;
_balances[receiver] += amount;
IERC20(stakingToken).safeTransferFrom(staker, address(this), amount);
emit Staked(staker, receiver, amount);
}
function _unstake(
address staker,
address receiver,
uint256 amount
) internal virtual {
if (amount == 0) revert CannotUnstakeZero();
totalSupply -= amount;
_balances[staker] -= amount;
IERC20(stakingToken).safeTransfer(receiver, amount);
emit Unstaked(staker, receiver, amount);
}
/*
* @notice Internal function to calculate earned rewards for an account
* @param account The address to calculate earnings for
* @param rewardToken The reward token to calculate earnings for
* @return The amount of reward tokens earned
*/
function _earned(
address account,
address rewardToken
) internal view returns (uint256) {
return
(_balances[account] *
(rewardPerToken(rewardToken) -
userRewardPerTokenPaid[rewardToken][account])) /
Constants.WAD +
rewards[rewardToken][account];
}
function _updateReward(address account) internal {
uint256 rewardTokenCount = _rewardTokensList.length();
for (uint256 i = 0; i < rewardTokenCount; i++) {
address rewardTokenAddress = _rewardTokensList.at(i);
RewardData storage rewardTokenData = rewardData[rewardTokenAddress];
rewardTokenData.rewardPerTokenStored = rewardPerToken(
rewardTokenAddress
);
rewardTokenData.lastUpdateTime = lastTimeRewardApplicable(
rewardTokenAddress
);
if (account != address(0)) {
rewards[rewardTokenAddress][account] = earned(
account,
rewardTokenAddress
);
userRewardPerTokenPaid[rewardTokenAddress][
account
] = rewardTokenData.rewardPerTokenStored;
}
}
}
/**
* @notice Internal function to claim rewards for an account for a specific token
* @param account The address to claim rewards for
* @param rewardTokenAddress The address of the reward token to claim
* @dev rewards go straight to the user's wallet
*/
function _getReward(
address account,
address rewardTokenAddress
) internal virtual updateReward(account) {
uint256 reward = rewards[rewardTokenAddress][account];
if (reward > 0) {
rewards[rewardTokenAddress][account] = 0;
IERC20(rewardTokenAddress).safeTransfer(account, reward);
emit RewardPaid(account, rewardTokenAddress, reward);
}
}
/**
* @dev Internal implementation of notifyRewardAmount
* @param rewardToken The token to distribute as rewards
* @param reward The amount of reward tokens to distribute
* @param newRewardsDuration The duration for new reward tokens (only used for first time)
*/
function _notifyRewardAmount(
address rewardToken,
uint256 reward,
uint256 newRewardsDuration
) internal {
RewardData storage rewardTokenData = rewardData[rewardToken];
if (newRewardsDuration == 0) {
revert RewardsDurationCannotBeZero();
}
if (newRewardsDuration > MAX_REWARD_DURATION) {
revert RewardsDurationTooLong();
}
// For existing reward tokens, check if current period is complete
if (_isRewardToken(rewardToken)) {
if (newRewardsDuration != rewardTokenData.rewardsDuration) {
revert CannotChangeRewardsDuration();
}
} else {
// First time setup for new reward token
bool success = _rewardTokensList.add(rewardToken);
if (!success) revert RewardTokenAlreadyExists();
rewardTokenData.rewardsDuration = newRewardsDuration;
emit RewardTokenAdded(rewardToken, rewardTokenData.rewardsDuration);
}
// Transfer exact amount needed for new rewards
IERC20(rewardToken).safeTransferFrom(msg.sender, address(this), reward);
// Calculate new reward rate
rewardTokenData.rewardRate =
(reward * Constants.WAD) /
rewardTokenData.rewardsDuration;
rewardTokenData.lastUpdateTime = block.timestamp;
rewardTokenData.periodFinish =
block.timestamp +
rewardTokenData.rewardsDuration;
emit RewardAdded(address(rewardToken), reward);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.24;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC-1967 implementation slot:
* ```solidity
* contract ERC1967 {
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* Since version 5.1, this library also support writing and reading value types to and from transient storage.
*
* * Example using transient storage:
* ```solidity
* contract Lock {
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _LOCK_SLOT = 0xf4678858b2b588224636b8522b729e7722d32fc491da849ed75b3fdf3c84f542;
*
* modifier locked() {
* require(!_LOCK_SLOT.asBoolean().tload());
*
* _LOCK_SLOT.asBoolean().tstore(true);
* _;
* _LOCK_SLOT.asBoolean().tstore(false);
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct Int256Slot {
int256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(
bytes32 slot
) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(
bytes32 slot
) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(
bytes32 slot
) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(
bytes32 slot
) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Int256Slot` with member `value` located at `slot`.
*/
function getInt256Slot(
bytes32 slot
) internal pure returns (Int256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(
bytes32 slot
) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(
string storage store
) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(
bytes32 slot
) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(
bytes storage store
) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev UDVT that represent a slot holding a address.
*/
type AddressSlotType is bytes32;
/**
* @dev Cast an arbitrary slot to a AddressSlotType.
*/
function asAddress(bytes32 slot) internal pure returns (AddressSlotType) {
return AddressSlotType.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a bool.
*/
type BooleanSlotType is bytes32;
/**
* @dev Cast an arbitrary slot to a BooleanSlotType.
*/
function asBoolean(bytes32 slot) internal pure returns (BooleanSlotType) {
return BooleanSlotType.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a bytes32.
*/
type Bytes32SlotType is bytes32;
/**
* @dev Cast an arbitrary slot to a Bytes32SlotType.
*/
function asBytes32(bytes32 slot) internal pure returns (Bytes32SlotType) {
return Bytes32SlotType.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a uint256.
*/
type Uint256SlotType is bytes32;
/**
* @dev Cast an arbitrary slot to a Uint256SlotType.
*/
function asUint256(bytes32 slot) internal pure returns (Uint256SlotType) {
return Uint256SlotType.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a int256.
*/
type Int256SlotType is bytes32;
/**
* @dev Cast an arbitrary slot to a Int256SlotType.
*/
function asInt256(bytes32 slot) internal pure returns (Int256SlotType) {
return Int256SlotType.wrap(slot);
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(AddressSlotType slot) internal view returns (address value) {
/// @solidity memory-safe-assembly
assembly {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(AddressSlotType slot, address value) internal {
/// @solidity memory-safe-assembly
assembly {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(BooleanSlotType slot) internal view returns (bool value) {
/// @solidity memory-safe-assembly
assembly {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(BooleanSlotType slot, bool value) internal {
/// @solidity memory-safe-assembly
assembly {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Bytes32SlotType slot) internal view returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Bytes32SlotType slot, bytes32 value) internal {
/// @solidity memory-safe-assembly
assembly {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Uint256SlotType slot) internal view returns (uint256 value) {
/// @solidity memory-safe-assembly
assembly {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Uint256SlotType slot, uint256 value) internal {
/// @solidity memory-safe-assembly
assembly {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Int256SlotType slot) internal view returns (int256 value) {
/// @solidity memory-safe-assembly
assembly {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Int256SlotType slot, int256 value) internal {
/// @solidity memory-safe-assembly
assembly {
tstore(slot, value)
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @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;
assembly ("memory-safe") {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
assembly ("memory-safe") {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(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) {
uint256 localValue = value;
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] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
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 Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal
* representation, according to EIP-55.
*/
function toChecksumHexString(address addr) internal pure returns (string memory) {
bytes memory buffer = bytes(toHexString(addr));
// hash the hex part of buffer (skip length + 2 bytes, length 40)
uint256 hashValue;
assembly ("memory-safe") {
hashValue := shr(96, keccak256(add(buffer, 0x22), 40))
}
for (uint256 i = 41; i > 1; --i) {
// possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)
if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) {
// case shift by xoring with 0x20
buffer[i] ^= 0x20;
}
hashValue >>= 4;
}
return string(buffer);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/types/Time.sol)
pragma solidity ^0.8.20;
import {Math} from "../math/Math.sol";
import {SafeCast} from "../math/SafeCast.sol";
/**
* @dev This library provides helpers for manipulating time-related objects.
*
* It uses the following types:
* - `uint48` for timepoints
* - `uint32` for durations
*
* While the library doesn't provide specific types for timepoints and duration, it does provide:
* - a `Delay` type to represent duration that can be programmed to change value automatically at a given point
* - additional helper functions
*/
library Time {
using Time for *;
/**
* @dev Get the block timestamp as a Timepoint.
*/
function timestamp() internal view returns (uint48) {
return SafeCast.toUint48(block.timestamp);
}
/**
* @dev Get the block number as a Timepoint.
*/
function blockNumber() internal view returns (uint48) {
return SafeCast.toUint48(block.number);
}
// ==================================================== Delay =====================================================
/**
* @dev A `Delay` is a uint32 duration that can be programmed to change value automatically at a given point in the
* future. The "effect" timepoint describes when the transitions happens from the "old" value to the "new" value.
* This allows updating the delay applied to some operation while keeping some guarantees.
*
* In particular, the {update} function guarantees that if the delay is reduced, the old delay still applies for
* some time. For example if the delay is currently 7 days to do an upgrade, the admin should not be able to set
* the delay to 0 and upgrade immediately. If the admin wants to reduce the delay, the old delay (7 days) should
* still apply for some time.
*
*
* The `Delay` type is 112 bits long, and packs the following:
*
* ```
* | [uint48]: effect date (timepoint)
* | | [uint32]: value before (duration)
* ↓ ↓ ↓ [uint32]: value after (duration)
* 0xAAAAAAAAAAAABBBBBBBBCCCCCCCC
* ```
*
* NOTE: The {get} and {withUpdate} functions operate using timestamps. Block number based delays are not currently
* supported.
*/
type Delay is uint112;
/**
* @dev Wrap a duration into a Delay to add the one-step "update in the future" feature
*/
function toDelay(uint32 duration) internal pure returns (Delay) {
return Delay.wrap(duration);
}
/**
* @dev Get the value at a given timepoint plus the pending value and effect timepoint if there is a scheduled
* change after this timepoint. If the effect timepoint is 0, then the pending value should not be considered.
*/
function _getFullAt(
Delay self,
uint48 timepoint
) private pure returns (uint32 valueBefore, uint32 valueAfter, uint48 effect) {
(valueBefore, valueAfter, effect) = self.unpack();
return effect <= timepoint ? (valueAfter, 0, 0) : (valueBefore, valueAfter, effect);
}
/**
* @dev Get the current value plus the pending value and effect timepoint if there is a scheduled change. If the
* effect timepoint is 0, then the pending value should not be considered.
*/
function getFull(Delay self) internal view returns (uint32 valueBefore, uint32 valueAfter, uint48 effect) {
return _getFullAt(self, timestamp());
}
/**
* @dev Get the current value.
*/
function get(Delay self) internal view returns (uint32) {
(uint32 delay, , ) = self.getFull();
return delay;
}
/**
* @dev Update a Delay object so that it takes a new duration after a timepoint that is automatically computed to
* enforce the old delay at the moment of the update. Returns the updated Delay object and the timestamp when the
* new delay becomes effective.
*/
function withUpdate(
Delay self,
uint32 newValue,
uint32 minSetback
) internal view returns (Delay updatedDelay, uint48 effect) {
uint32 value = self.get();
uint32 setback = uint32(Math.max(minSetback, value > newValue ? value - newValue : 0));
effect = timestamp() + setback;
return (pack(value, newValue, effect), effect);
}
/**
* @dev Split a delay into its components: valueBefore, valueAfter and effect (transition timepoint).
*/
function unpack(Delay self) internal pure returns (uint32 valueBefore, uint32 valueAfter, uint48 effect) {
uint112 raw = Delay.unwrap(self);
valueAfter = uint32(raw);
valueBefore = uint32(raw >> 32);
effect = uint48(raw >> 64);
return (valueBefore, valueAfter, effect);
}
/**
* @dev pack the components into a Delay object.
*/
function pack(uint32 valueBefore, uint32 valueAfter, uint48 effect) internal pure returns (Delay) {
return Delay.wrap((uint112(effect) << 64) | (uint112(valueBefore) << 32) | uint112(valueAfter));
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
/*
██████╗ ██████╗ ██████╗ ███╗ ███╗ █████╗ ████████╗██╗ ██╗
██╔══██╗██╔══██╗██╔══██╗████╗ ████║██╔══██╗╚══██╔══╝██║ ██║
██████╔╝██████╔╝██████╔╝██╔████╔██║███████║ ██║ ███████║
██╔═══╝ ██╔══██╗██╔══██╗██║╚██╔╝██║██╔══██║ ██║ ██╔══██║
██║ ██║ ██║██████╔╝██║ ╚═╝ ██║██║ ██║ ██║ ██║ ██║
╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
██╗ ██╗██████╗ ██████╗ ██████╗ ██╗ ██╗ ██╗ █████╗
██║ ██║██╔══██╗██╔════╝ ██╔═████╗╚██╗██╔╝███║██╔══██╗
██║ ██║██║ ██║███████╗ ██║██╔██║ ╚███╔╝ ╚██║╚█████╔╝
██║ ██║██║ ██║██╔═══██╗████╔╝██║ ██╔██╗ ██║██╔══██╗
╚██████╔╝██████╔╝╚██████╔╝╚██████╔╝██╔╝ ██╗ ██║╚█████╔╝
╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚════╝
*/
import "./ud60x18/Casting.sol";
import "./ud60x18/Constants.sol";
import "./ud60x18/Conversions.sol";
import "./ud60x18/Errors.sol";
import "./ud60x18/Helpers.sol";
import "./ud60x18/Math.sol";
import "./ud60x18/ValueType.sol";
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "./Casting.sol" as Casting;
import "./Helpers.sol" as Helpers;
import "./Math.sol" as Math;
/// @notice The unsigned 60.18-decimal fixed-point number representation, which can have up to 60 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the Solidity type uint256.
/// @dev The value type is defined here so it can be imported in all other files.
type UD60x18 is uint256;
/*//////////////////////////////////////////////////////////////////////////
CASTING
//////////////////////////////////////////////////////////////////////////*/
using {
Casting.intoSD1x18,
Casting.intoSD21x18,
Casting.intoSD59x18,
Casting.intoUD2x18,
Casting.intoUD21x18,
Casting.intoUint128,
Casting.intoUint256,
Casting.intoUint40,
Casting.unwrap
} for UD60x18 global;
/*//////////////////////////////////////////////////////////////////////////
MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
// The global "using for" directive makes the functions in this library callable on the UD60x18 type.
using {
Math.avg,
Math.ceil,
Math.div,
Math.exp,
Math.exp2,
Math.floor,
Math.frac,
Math.gm,
Math.inv,
Math.ln,
Math.log10,
Math.log2,
Math.mul,
Math.pow,
Math.powu,
Math.sqrt
} for UD60x18 global;
/*//////////////////////////////////////////////////////////////////////////
HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
// The global "using for" directive makes the functions in this library callable on the UD60x18 type.
using {
Helpers.add,
Helpers.and,
Helpers.eq,
Helpers.gt,
Helpers.gte,
Helpers.isZero,
Helpers.lshift,
Helpers.lt,
Helpers.lte,
Helpers.mod,
Helpers.neq,
Helpers.not,
Helpers.or,
Helpers.rshift,
Helpers.sub,
Helpers.uncheckedAdd,
Helpers.uncheckedSub,
Helpers.xor
} for UD60x18 global;
/*//////////////////////////////////////////////////////////////////////////
OPERATORS
//////////////////////////////////////////////////////////////////////////*/
// The global "using for" directive makes it possible to use these operators on the UD60x18 type.
using {
Helpers.add as +,
Helpers.and2 as &,
Math.div as /,
Helpers.eq as ==,
Helpers.gt as >,
Helpers.gte as >=,
Helpers.lt as <,
Helpers.lte as <=,
Helpers.or as |,
Helpers.mod as %,
Math.mul as *,
Helpers.neq as !=,
Helpers.not as ~,
Helpers.sub as -,
Helpers.xor as ^
} for UD60x18 global;
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {VotingDecayMath} from "./VotingDecayMath.sol";
import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol";
/*
* @title VotingDecayLibrary
* @notice A library for managing voting power decay in governance systems
* @dev Utilizes VotingDecayMath for decay calculations
*/
library VotingDecayLibrary {
using VotingDecayMath for uint256;
using Checkpoints for Checkpoints.Trace224;
/* @notice Constant representing 1 in the system's fixed-point arithmetic (18 decimal places) */
uint256 private constant WAD = 1e18;
/* @notice Number of seconds in a year, used for annualized rate calculations */
uint256 private constant SECONDS_PER_YEAR = 365 days;
/* @notice Enumeration of supported decay function types */
enum DecayFunction {
Linear,
Exponential
}
/*
* @notice Structure to store decay information for an account
* @param decayFactor The current decay factor of the account's voting power
* @param lastUpdateTimestamp The timestamp of the last update to the account's decay info
*/
struct DecayInfo {
uint256 decayFactor;
uint40 lastUpdateTimestamp;
}
struct DecayState {
mapping(address => DecayInfo) decayInfoByAccount;
uint40 decayFreeWindow;
uint256 decayRatePerSecond;
DecayFunction decayFunction;
uint40 originTimestamp;
mapping(address => Checkpoints.Trace224) decayFactorCheckpoints;
}
/**
* @notice Thrown when the decay type is invalid
*/
error InvalidDecayType();
// Events
event DecayRateSet(uint256 newRate);
event DecayFreeWindowSet(uint40 newWindow);
event DecayFunctionSet(uint8 newFunction);
event AccountInitialized(address account);
event DecayUpdated(address account, uint256 newDecayFactor);
event DecayReset(address account);
// Errors
error AccountNotInitialized();
error InvalidDecayRate();
/**
* @notice Maximum allowed depth for delegation chains to prevent recursion attacks
* @dev When this depth is exceeded, voting power decays to 0 to maintain EIP-5805 invariants
* Example chain at max depth (2):
* User A -> delegates to B -> delegates to C (ok)
* User A -> delegates to B -> delegates to C -> delegates to D (returns 0)
*/
uint256 public constant MAX_DELEGATION_DEPTH = 2;
/*//////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////*/
function initializeAccount(
DecayState storage self,
address accountAddress
) internal {
_initializeAccount(self, accountAddress);
}
/**
* @notice Gets the current decay factor for an account, considering delegation
* @param self The DecayState storage
* @param accountAddress The address of the account to check
* @param getDelegateTo Function to retrieve delegation information
* @return The current decay factor for the account
*/
function getDecayFactor(
DecayState storage self,
address accountAddress,
function(address) view returns (address) getDelegateTo
) internal view returns (uint256) {
return
_getDecayFactorWithDepth(
self,
accountAddress,
0,
accountAddress,
getDelegateTo
);
}
/**
* @notice Retrieves the decay info for a specific account
* @param self The DecayState storage
* @param accountAddress The address of the account
* @return DecayInfo struct containing the account's decay information
*/
function getDecayInfo(
DecayState storage self,
address accountAddress
) internal view returns (DecayInfo memory) {
return self.decayInfoByAccount[accountAddress];
}
/**
* @notice Sets the decay rate per second
* @param self The DecayState storage
* @param newRatePerSecond The new decay rate to set
*/
function setDecayRatePerSecond(
DecayState storage self,
uint256 newRatePerSecond
) internal {
if (!isValidDecayRate(newRatePerSecond)) {
revert InvalidDecayRate();
}
self.decayRatePerSecond = newRatePerSecond;
emit DecayRateSet(newRatePerSecond);
}
/**
* @notice Sets the decay-free window period during which no decay occurs
* @param self The DecayState storage
* @param newWindow The new decay-free window duration in seconds
*/
function setDecayFreeWindow(
DecayState storage self,
uint40 newWindow
) internal {
self.decayFreeWindow = newWindow;
emit DecayFreeWindowSet(newWindow);
}
/**
* @notice Sets the decay function type (Linear or Exponential)
* @param self The DecayState storage
* @param newFunction The new decay function to use
*/
function setDecayFunction(
DecayState storage self,
DecayFunction newFunction
) internal {
self.decayFunction = newFunction;
emit DecayFunctionSet(uint8(newFunction));
}
/**
* @notice Updates the decay factor for an account and creates a checkpoint
* @param self The DecayState storage
* @param accountAddress The address of the account to update
* @param getDelegateTo Function to retrieve delegation information
*/
function updateDecayFactor(
DecayState storage self,
address accountAddress,
function(address) view returns (address) getDelegateTo
) internal {
_initializeAccount(self, accountAddress);
DecayInfo storage account = self.decayInfoByAccount[accountAddress];
uint256 decayPeriod = block.timestamp - account.lastUpdateTimestamp;
uint256 newDecayFactor = account.decayFactor;
if (decayPeriod > self.decayFreeWindow) {
newDecayFactor = getDecayFactor(
self,
accountAddress,
getDelegateTo
);
}
// Create checkpoint with current timestamp and new decay factor
self.decayFactorCheckpoints[accountAddress].push(
uint32(block.timestamp),
uint224(newDecayFactor)
);
account.decayFactor = newDecayFactor;
account.lastUpdateTimestamp = uint40(block.timestamp);
emit DecayUpdated(accountAddress, newDecayFactor);
}
/**
* @notice Resets the decay factor for an account back to WAD (1e18)
* @param self The DecayState storage
* @param accountAddress The address of the account to reset
*/
function resetDecay(
DecayState storage self,
address accountAddress
) internal {
_initializeAccount(self, accountAddress);
DecayInfo storage account = self.decayInfoByAccount[accountAddress];
account.lastUpdateTimestamp = uint40(block.timestamp);
account.decayFactor = WAD;
emit DecayReset(accountAddress);
}
/**
* @notice Initializes the decay state with initial parameters
* @param self The DecayState storage
* @param decayFreeWindow_ The initial decay-free window duration in seconds
* @param decayRatePerSecond_ The initial decay rate per second
* @param decayFunction_ The initial decay function type
*/
function initialize(
DecayState storage self,
uint40 decayFreeWindow_,
uint256 decayRatePerSecond_,
DecayFunction decayFunction_
) internal {
self.decayFreeWindow = decayFreeWindow_;
self.decayRatePerSecond = decayRatePerSecond_;
self.decayFunction = decayFunction_;
self.originTimestamp = uint40(block.timestamp);
}
/**
* @notice Calculates the current voting power by applying decay to the original value
* @param self The DecayState storage
* @param accountAddress The address of the account
* @param originalValue The original voting power value before decay
* @param getDelegateTo Function to retrieve delegation information
* @return The current voting power after applying decay
*/
function getVotingPower(
DecayState storage self,
address accountAddress,
uint256 originalValue,
function(address) view returns (address) getDelegateTo
) internal view returns (uint256) {
uint256 decayFactor = getDecayFactor(
self,
accountAddress,
getDelegateTo
);
return applyDecay(originalValue, decayFactor);
}
/*
* @notice Applies the decay to the original voting power value
* @param originalValue The original voting power value
* @param retentionFactor The current retention factor
* @return The decayed voting power value
*/
function applyDecay(
uint256 originalValue,
uint256 retentionFactor
) internal pure returns (uint256) {
return VotingDecayMath.mulDiv(originalValue, retentionFactor, WAD);
}
/*
* @notice Checks if a given decay rate is valid
* @param rate The decay rate to check
* @return A boolean indicating whether the rate is valid (less than or equal to WAD)
*/
function isValidDecayRate(uint256 rate) internal pure returns (bool) {
return rate <= WAD;
}
/*//////////////////////////////////////////////////////////////
PRIVATE FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Initializes decay information for an account if it hasn't been initialized before
* @dev Sets initial decay factor to WAD (1e18) and lastUpdateTimestamp to current block timestamp
* @param self The DecayState storage
* @param accountAddress The address of the account to initialize
* @custom:emits AccountInitialized when a new account is initialized
*/
function _initializeAccount(
DecayState storage self,
address accountAddress
) private {
if (self.decayInfoByAccount[accountAddress].lastUpdateTimestamp == 0) {
self.decayInfoByAccount[accountAddress] = DecayInfo({
decayFactor: WAD,
lastUpdateTimestamp: uint40(block.timestamp)
});
self.decayFactorCheckpoints[accountAddress].push(
uint32(block.timestamp),
uint224(WAD)
);
emit AccountInitialized(accountAddress);
}
}
/**
* @notice Recursively calculates decay factor considering delegation depth
* @dev Returns 0 in the following cases:
* 1. When accountAddress is address(0)
* 2. When delegation depth exceeds MAX_DELEGATION_DEPTH
* 3. When the account or its delegate has no decay info
* @param self The DecayState storage
* @param accountAddress Current account being checked
* @param depth Current delegation depth
* @param originalAccount The initial account that started the calculation
* @param getDelegateTo Function to retrieve delegation information
* @return The calculated decay factor, or 0 if max depth exceeded
*/
function _getDecayFactorWithDepth(
DecayState storage self,
address accountAddress,
uint256 depth,
address originalAccount,
function(address) view returns (address) getDelegateTo
) private view returns (uint256) {
if (accountAddress == address(0)) {
return 0;
}
if (depth >= MAX_DELEGATION_DEPTH) {
return 0;
}
address delegateTo = getDelegateTo(accountAddress);
// Detect cycles - if we're back to original account and within depth limit,
// return the original account's decay factor
if (delegateTo == originalAccount && depth > 0) {
return _calculateAccountDecayFactor(self, originalAccount);
}
// Has Delegate + Delegate has Decay Info
if (
delegateTo != address(0) &&
delegateTo != accountAddress &&
hasDecayInfo(self, delegateTo)
) {
return
_getDecayFactorWithDepth(
self,
delegateTo,
depth + 1,
originalAccount,
getDelegateTo
);
}
// For uninitialized accounts, calculate decay from contract origin
if (!hasDecayInfo(self, accountAddress)) {
return
_calculateDecayFactor(
WAD,
block.timestamp - self.originTimestamp,
self.decayRatePerSecond,
self.decayFreeWindow,
self.decayFunction
);
}
// No Delegate + Has Decay Info
return _calculateAccountDecayFactor(self, accountAddress);
}
/**
* @notice Calculates the current decay factor for an account
* @param self The DecayState storage
* @param accountAddress The address of the account
* @return The calculated decay factor
*/
function _calculateAccountDecayFactor(
DecayState storage self,
address accountAddress
) private view returns (uint256) {
DecayInfo storage account = self.decayInfoByAccount[accountAddress];
uint256 decayPeriod = block.timestamp - account.lastUpdateTimestamp;
return
_calculateDecayFactor(
account.decayFactor,
decayPeriod,
self.decayRatePerSecond,
self.decayFreeWindow,
self.decayFunction
);
}
/**
* @notice Checks if an account has decay information initialized
* @param self The DecayState storage
* @param accountAddress The address to check
* @return bool True if the account has decay info, false otherwise
*/
function hasDecayInfo(
DecayState storage self,
address accountAddress
) internal view returns (bool) {
return self.decayInfoByAccount[accountAddress].lastUpdateTimestamp != 0;
}
/*
* @notice Calculates the new decay factor based on elapsed time and decay parameters
* @param currentDecayFactor The current retention factor
* @param elapsedSeconds The number of seconds elapsed since the last update
* @param decayRatePerSecond The decay rate per second
* @param decayFreeWindow The duration (in seconds) during which no decay occurs
* @param decayFunction The type of decay function to use (Linear or Exponential)
* @return The newly calculated retention factor
*/
function _calculateDecayFactor(
uint256 currentDecayFactor,
uint256 elapsedSeconds,
uint256 decayRatePerSecond,
uint256 decayFreeWindow,
DecayFunction decayFunction
) private pure returns (uint256) {
if (elapsedSeconds <= decayFreeWindow) return currentDecayFactor;
uint256 decayTime = elapsedSeconds - decayFreeWindow;
if (decayFunction == DecayFunction.Linear) {
return
currentDecayFactor.linearDecay(decayRatePerSecond, decayTime);
} else if (decayFunction == DecayFunction.Exponential) {
return
currentDecayFactor.exponentialDecay(
decayRatePerSecond,
decayTime
);
} else {
revert InvalidDecayType();
}
}
/**
* @notice Gets the length of a delegation chain for an account
* @dev Counts the number of steps in the delegation chain until:
* 1. A self-delegation is found
* 2. An address(0) delegation is found
* 3. MAX_DELEGATION_DEPTH is reached
* @param self The DecayState storage
* @param accountAddress The address to check delegation chain for
* @param getDelegateTo Function to retrieve delegation information
* @return uint256 The length of the delegation chain
*/
function getDelegationChainLength(
DecayState storage self,
address accountAddress,
function(address) view returns (address) getDelegateTo
) internal view returns (uint256) {
return
_getDelegationChainLengthWithDepth(
self,
accountAddress,
0,
accountAddress,
getDelegateTo
);
}
/**
* @notice Internal recursive function to calculate delegation chain length
* @param self The DecayState storage
* @param accountAddress Current account being checked
* @param depth Current depth in the delegation chain
* @param originalAccount The initial account that started the calculation
* @param getDelegateTo Function to retrieve delegation information
* @return uint256 The length of the delegation chain
*/
function _getDelegationChainLengthWithDepth(
DecayState storage self,
address accountAddress,
uint256 depth,
address originalAccount,
function(address) view returns (address) getDelegateTo
) private view returns (uint256) {
if (accountAddress == address(0)) {
return 0;
}
address delegateTo = getDelegateTo(accountAddress);
// Detect cycles by checking if we're back to the original account
if (delegateTo == originalAccount) {
return depth;
}
// Self-delegation or no delegation
if (delegateTo == address(0) || delegateTo == accountAddress) {
return depth;
}
// Continue counting if there's a valid delegation
return
_getDelegationChainLengthWithDepth(
self,
delegateTo,
depth + 1,
originalAccount,
getDelegateTo
);
}
/**
* @notice Gets the historical decay factor for an account at a specific timestamp
* @param self The DecayState storage
* @param accountAddress The address to check
* @param timestamp The timestamp to check at
* @return The decay factor at that timestamp
*/
function getHistoricalDecayFactor(
DecayState storage self,
address accountAddress,
uint256 timestamp
) internal view returns (uint256) {
if (timestamp < self.originTimestamp) {
return 0;
}
uint224 checkpointValue = self
.decayFactorCheckpoints[accountAddress]
.upperLookup(uint32(timestamp));
// No checkpoint found - calculate from origin
if (checkpointValue == 0) {
uint256 decayPeriod = timestamp - self.originTimestamp;
if (decayPeriod <= self.decayFreeWindow) {
return WAD;
}
// Apply decay from origin with WAD as base
if (self.decayFunction == DecayFunction.Linear) {
return
VotingDecayMath.linearDecay(
WAD,
self.decayRatePerSecond,
decayPeriod - self.decayFreeWindow
);
} else if (self.decayFunction == DecayFunction.Exponential) {
return
VotingDecayMath.exponentialDecay(
WAD,
self.decayRatePerSecond,
decayPeriod - self.decayFreeWindow
);
} else {
revert InvalidDecayType();
}
}
// Checkpoint found - use it as base
else {
return uint256(checkpointValue);
}
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {UD60x18, ud, unwrap} from "@prb/math/src/UD60x18.sol";
/*
* @title VotingDecayMath
* @notice A library for advanced mathematical operations used in voting decay calculations
* @dev Utilizes PRBMath for precise calculations
*/
library VotingDecayMath {
/* @notice Constant representing the scale factor for calculations (18 decimal places) */
uint256 private constant WAD = 1e18;
/**
* @dev Multiplies two numbers and divides the result by a third number, using PRBMath for precision.
* @param a The first number to multiply
* @param b The second number to multiply
* @param denominator The number to divide by
* @return The result of (a * b) / denominator, using PRBMath's UD60x18 type
*/
function mulDiv(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256) {
UD60x18 result = ud(a).mul(ud(b)).div(ud(denominator));
return unwrap(result);
}
/**
* @dev Calculates the exponential decay using PRBMath's UD60x18 type.
* @param initialValue The initial value
* @param decayRatePerSecond The decay rate per second
* @param decayTimeInSeconds The time elapsed in seconds
* @return The decayed value
*/
function exponentialDecay(
uint256 initialValue,
uint256 decayRatePerSecond,
uint256 decayTimeInSeconds
) internal pure returns (uint256) {
// Early returns
if (decayTimeInSeconds == 0 || decayRatePerSecond == 0) {
return initialValue;
}
if (decayRatePerSecond >= WAD) {
return 0;
}
if (initialValue == 0) {
return 0;
}
// Safe conversion to UD60x18
UD60x18 retentionRatePerSecond;
unchecked {
// WAD - decayRatePerSecond is safe because we checked decayRatePerSecond < WAD
retentionRatePerSecond = ud(WAD - decayRatePerSecond);
}
// If retention rate is 0 or time is too large, return 0
if (
unwrap(retentionRatePerSecond) == 0 ||
decayTimeInSeconds > type(uint32).max
) {
return 0;
}
UD60x18 retentionFactor = retentionRatePerSecond.powu(
decayTimeInSeconds
);
// If retention factor became 0 during calculation
if (unwrap(retentionFactor) == 0) {
return 0;
}
UD60x18 result = ud(initialValue).mul(retentionFactor);
return unwrap(result.gt(ud(0)) ? result.div(ud(WAD)) : ud(0));
}
/**
* @dev Calculates the linear decay.
* @param initialValue The initial value
* @param decayRatePerSecond The decay rate per second
* @param decayTimeInSeconds The time elapsed in seconds
* @return The decayed value
*/
function linearDecay(
uint256 initialValue,
uint256 decayRatePerSecond,
uint256 decayTimeInSeconds
) internal pure returns (uint256) {
// Early returns
if (decayTimeInSeconds == 0 || decayRatePerSecond == 0) {
return initialValue;
}
if (decayRatePerSecond >= WAD) {
return 0;
}
if (initialValue == 0) {
return 0;
}
// Check for overflow in multiplication
if (
decayRatePerSecond > 0 &&
decayTimeInSeconds > WAD / decayRatePerSecond
) {
return 0;
}
uint256 totalDecayFactor;
unchecked {
// Safe because of the check above
totalDecayFactor = decayRatePerSecond * decayTimeInSeconds;
}
// Check if total decay exceeds 100%
if (totalDecayFactor >= WAD) {
return 0;
}
uint256 retentionFactor;
unchecked {
// Safe because we checked totalDecayFactor < WAD
retentionFactor = WAD - totalDecayFactor;
}
// Final multiplication and division
return (initialValue * retentionFactor) / WAD;
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {ERC20Wrapper} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Wrapper.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
/**
* @title WrappedStakingToken
* @notice A simple wrapper for the staking token that inherits from ERC20Wrapper
* @dev This contract is used by GovernanceRewardsManager to wrap staking tokens when they are used as rewards
*/
contract WrappedStakingToken is ERC20Wrapper {
constructor(
address underlyingToken
)
ERC20(string.concat("Wrapped ", "Summer"), string.concat("w", "SUMR"))
ERC20Wrapper(IERC20(underlyingToken))
{}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
{
"compilationTarget": {
"src/contracts/GovernanceRewardsManager.sol": "GovernanceRewardsManager"
},
"evmVersion": "cancun",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 50
},
"remappings": [],
"viaIR": true
}
[{"inputs":[{"internalType":"address","name":"_stakingToken","type":"address"},{"internalType":"address","name":"accessManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotAdmin","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotAuthorizedToBoard","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotCommander","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"CallerIsNotContractSpecificRole","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotCurator","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotDecayController","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotFoundation","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotGovernor","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotGuardian","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotGuardianOrGovernor","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotKeeper","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotRaft","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotRaftOrCommander","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotSuperKeeper","type":"error"},{"inputs":[],"name":"CannotChangeRewardsDuration","type":"error"},{"inputs":[],"name":"CannotStakeToZeroAddress","type":"error"},{"inputs":[],"name":"CannotStakeZero","type":"error"},{"inputs":[],"name":"CannotUnstakeZero","type":"error"},{"inputs":[],"name":"CannotWithdrawZero","type":"error"},{"inputs":[],"name":"DecayController__RewardsManagerAlreadySet","type":"error"},{"inputs":[],"name":"DecayController__ZeroAddress","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"DirectGrantIsDisabled","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"DirectRevokeIsDisabled","type":"error"},{"inputs":[],"name":"IndexOutOfBounds","type":"error"},{"inputs":[{"internalType":"address","name":"invalidAddress","type":"address"}],"name":"InvalidAccessManagerAddress","type":"error"},{"inputs":[],"name":"InvalidCaller","type":"error"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"rewardsDuration","type":"uint256"}],"name":"InvalidRewardAmount","type":"error"},{"inputs":[],"name":"InvalidRewardsDuration","type":"error"},{"inputs":[],"name":"NoRewardTokens","type":"error"},{"inputs":[],"name":"NotDelegated","type":"error"},{"inputs":[],"name":"ProvidedRewardTooHigh","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"RewardPeriodNotComplete","type":"error"},{"inputs":[],"name":"RewardTokenAlreadyExists","type":"error"},{"inputs":[],"name":"RewardTokenDoesNotExist","type":"error"},{"inputs":[],"name":"RewardTokenNotInitialized","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"name":"RewardTokenStillHasBalance","type":"error"},{"inputs":[],"name":"RewardsDurationCannotBeZero","type":"error"},{"inputs":[],"name":"RewardsDurationTooLong","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"StakeOnBehalfOfNotSupported","type":"error"},{"inputs":[],"name":"StakingTokenNotInitialized","type":"error"},{"inputs":[],"name":"UnstakeOnBehalfOfNotSupported","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rewardToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"rewardToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"rewardToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewardsDuration","type":"uint256"}],"name":"RewardTokenAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"rewardToken","type":"address"}],"name":"RewardTokenRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rewardToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"RewardsDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UnstakeOnBehalfOfIgnored","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unstaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"ADMIRALS_QUARTERS_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DECAY_CONTROLLER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DECAY_SMOOTHING_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DECAY_SMOOTHING_FACTOR_BASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GOVERNOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GUARDIAN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SUPER_KEEPER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"calculateSmoothedDecayFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum ContractSpecificRoles","name":"roleName","type":"uint8"},{"internalType":"address","name":"roleTargetContract","type":"address"}],"name":"generateRole","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"}],"name":"getReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getRewardFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"}],"name":"getRewardFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"}],"name":"getRewardForDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"hasAdmiralsQuartersRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"}],"name":"isRewardToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"}],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint256","name":"newRewardsDuration","type":"uint256"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"}],"name":"removeRewardToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"}],"name":"rewardData","outputs":[{"internalType":"uint256","name":"periodFinish","type":"uint256"},{"internalType":"uint256","name":"rewardRate","type":"uint256"},{"internalType":"uint256","name":"rewardsDuration","type":"uint256"},{"internalType":"uint256","name":"lastUpdateTime","type":"uint256"},{"internalType":"uint256","name":"rewardPerTokenStored","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"}],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"rewardTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardTokensLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"address","name":"account","type":"address"}],"name":"rewards","outputs":[{"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"name":"setRewardsDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakeOnBehalfOf","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"stakingToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"unstakeAndWithdrawOnBehalfOf","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"updateSmoothedDecayFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"address","name":"account","type":"address"}],"name":"userRewardPerTokenPaid","outputs":[{"internalType":"uint256","name":"rewardPerTokenPaid","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"userSmoothedDecayFactor","outputs":[{"internalType":"uint256","name":"smoothedDecayFactor","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wrappedStakingToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]