Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00Latest 25 from a total of 28 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Purchase Members... | 35381114 | 12 days ago | IN | 0.01287 ETH | 0.00001679 | ||||
| Purchase Members... | 25756865 | 73 days ago | IN | 0.004875 ETH | 0.00001678 | ||||
| Purchase Members... | 25060535 | 77 days ago | IN | 0.004875 ETH | 0.00001678 | ||||
| Purchase Members... | 22379181 | 92 days ago | IN | 0.00507 ETH | 0.00001694 | ||||
| Purchase Members... | 21395323 | 104 days ago | IN | 0.195 ETH | 0.00001682 | ||||
| Purchase Members... | 21270974 | 106 days ago | IN | 0.14292525 ETH | 0.00001681 | ||||
| Purchase Members... | 20738940 | 115 days ago | IN | 0.004875 ETH | 0.00001765 | ||||
| Purchase Members... | 20670750 | 116 days ago | IN | 0.004875 ETH | 0.00001681 | ||||
| Purchase Members... | 20613100 | 116 days ago | IN | 0.004875 ETH | 0.0000168 | ||||
| Purchase Members... | 20054402 | 125 days ago | IN | 0.004875 ETH | 0.00001733 | ||||
| Purchase Members... | 18967706 | 142 days ago | IN | 0.004875 ETH | 0.00001738 | ||||
| Purchase Members... | 17797348 | 157 days ago | IN | 0.004875 ETH | 0.00001714 | ||||
| Purchase Members... | 17465649 | 161 days ago | IN | 0.04485 ETH | 0.00001763 | ||||
| Purchase Members... | 17464916 | 161 days ago | IN | 0.00454143 ETH | 0.00000445 | ||||
| Purchase Members... | 17464713 | 161 days ago | IN | 0.00454143 ETH | 0.00000771 | ||||
| Purchase Members... | 16796822 | 169 days ago | IN | 0.004875 ETH | 0.00001825 | ||||
| Purchase Members... | 16773921 | 169 days ago | IN | 0.004875 ETH | 0.00000957 | ||||
| Purchase Members... | 16773799 | 169 days ago | IN | 0.004875 ETH | 0.00001708 | ||||
| Set Allowed Addr... | 16770971 | 169 days ago | IN | 0 ETH | 0.00000537 | ||||
| Set Allowed Addr... | 8086778 | 273 days ago | IN | 0 ETH | 0.00000586 | ||||
| Set Allowed Addr... | 5835878 | 300 days ago | IN | 0 ETH | 0.00000443 | ||||
| Set Allowed Addr... | 5834578 | 300 days ago | IN | 0 ETH | 0.00000528 | ||||
| Set Allowed Addr... | 5831833 | 300 days ago | IN | 0 ETH | 0.00000443 | ||||
| Set Allowed Addr... | 5830363 | 300 days ago | IN | 0 ETH | 0.00000443 | ||||
| Set Allowed Addr... | 5824056 | 301 days ago | IN | 0 ETH | 0.00000444 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 35381114 | 12 days ago | 0.01287 ETH | ||||
| 35381114 | 12 days ago | 0.01287 ETH | ||||
| 25756865 | 73 days ago | 0.004875 ETH | ||||
| 25756865 | 73 days ago | 0.004875 ETH | ||||
| 25060535 | 77 days ago | 0.004875 ETH | ||||
| 25060535 | 77 days ago | 0.004875 ETH | ||||
| 22379181 | 92 days ago | 0.00507 ETH | ||||
| 22379181 | 92 days ago | 0.00507 ETH | ||||
| 21395323 | 104 days ago | 0.195 ETH | ||||
| 21395323 | 104 days ago | 0.195 ETH | ||||
| 21270974 | 106 days ago | 0.14292525 ETH | ||||
| 21270974 | 106 days ago | 0.14292525 ETH | ||||
| 20738940 | 115 days ago | 0.004875 ETH | ||||
| 20738940 | 115 days ago | 0.004875 ETH | ||||
| 20670750 | 116 days ago | 0.004875 ETH | ||||
| 20670750 | 116 days ago | 0.004875 ETH | ||||
| 20613100 | 116 days ago | 0.004875 ETH | ||||
| 20613100 | 116 days ago | 0.004875 ETH | ||||
| 20054402 | 125 days ago | 0.004875 ETH | ||||
| 20054402 | 125 days ago | 0.004875 ETH | ||||
| 18967706 | 142 days ago | 0.004875 ETH | ||||
| 18967706 | 142 days ago | 0.004875 ETH | ||||
| 17797348 | 157 days ago | 0.004875 ETH | ||||
| 17797348 | 157 days ago | 0.004875 ETH | ||||
| 17465649 | 161 days ago | 0.04485 ETH |
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
PurchaseProxy
Compiler Version
v0.8.22-1.0.1
ZkSolc Version
v1.5.12
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "../interfaces/network/IFairSideNetwork.sol";
import "../admin/IFairsideAdmin.sol";
contract PurchaseProxy {
IFairSideNetwork public network;
IFairsideAdmin public fairsideAdmin;
mapping(address => bool) public allowedAddresses;
error PurchaseProxy_InvalidAddress();
error PurchaseProxy_NotAllowedAddress();
error PurchaseProxy_InsufficientPrivilegesOnlyAdmin();
modifier onlyAllowedAddress() {
if (!allowedAddresses[msg.sender]) {
revert PurchaseProxy_NotAllowedAddress();
}
_;
}
modifier onlyAdmin() {
if (
!fairsideAdmin.hasFSRole(
IFairsideAdmin.FSRoles.FS_ADMIN_ROLE,
msg.sender
)
) {
revert PurchaseProxy_InsufficientPrivilegesOnlyAdmin();
}
_;
}
constructor(address _network, address _fairsideAdmin) {
if (_network == address(0) || _fairsideAdmin == address(0)) {
revert PurchaseProxy_InvalidAddress();
}
network = IFairSideNetwork(_network);
fairsideAdmin = IFairsideAdmin(_fairsideAdmin);
}
function setNetwork(address _network) external onlyAdmin {
if (_network == address(0)) {
revert PurchaseProxy_InvalidAddress();
}
network = IFairSideNetwork(_network);
}
function setFairsideAdmin(address _fairsideAdmin) external onlyAdmin {
if (_fairsideAdmin == address(0)) {
revert PurchaseProxy_InvalidAddress();
}
fairsideAdmin = IFairsideAdmin(_fairsideAdmin);
}
function purchaseMembershipFromProxy(
uint256 coverAmount,
uint256 membershipTypeId,
address primaryAddress,
address coverAddress
) external payable onlyAllowedAddress {
network.purchaseMembershipFromProxy{value: msg.value}(
primaryAddress,
coverAmount,
coverAddress,
membershipTypeId
);
}
function setAllowedAddress(
address primaryAddress,
bool allowed
) external onlyAdmin {
allowedAddresses[primaryAddress] = allowed;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.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 AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
StringsUpgradeable.toHexString(account),
" is missing role ",
StringsUpgradeable.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* May emit a {RoleGranted} event.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControlUpgradeable {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.0;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822ProxiableUpgradeable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)
pragma solidity ^0.8.0;
/**
* @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
*
* _Available since v4.8.3._
*/
interface IERC1967Upgradeable {
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.0;
interface IERC5267Upgradeable {
/**
* @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 v4.4.1 (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.0;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeaconUpgradeable {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {BeaconProxy} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)
pragma solidity ^0.8.2;
import "../beacon/IBeaconUpgradeable.sol";
import "../../interfaces/IERC1967Upgradeable.sol";
import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/StorageSlotUpgradeable.sol";
import {Initializable} from "../utils/Initializable.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*
* _Available since v4.1._
*/
abstract contract ERC1967UpgradeUpgradeable is Initializable, IERC1967Upgradeable {
// This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
function __ERC1967Upgrade_init() internal onlyInitializing {
}
function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
}
/**
* @dev Returns the current implementation address.
*/
function _getImplementation() internal view returns (address) {
return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Perform implementation upgrade
*
* Emits an {Upgraded} event.
*/
function _upgradeTo(address newImplementation) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
/**
* @dev Perform implementation upgrade with additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
_upgradeTo(newImplementation);
if (data.length > 0 || forceCall) {
AddressUpgradeable.functionDelegateCall(newImplementation, data);
}
}
/**
* @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
// Upgrades from old implementations will perform a rollback test. This test requires the new
// implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
// this special case will break upgrade paths from old UUPS implementation to new ones.
if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
_setImplementation(newImplementation);
} else {
try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
} catch {
revert("ERC1967Upgrade: new implementation is not UUPS");
}
_upgradeToAndCall(newImplementation, data, forceCall);
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*/
function _getAdmin() internal view returns (address) {
return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
require(newAdmin != address(0), "ERC1967: new admin is the zero address");
StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {AdminChanged} event.
*/
function _changeAdmin(address newAdmin) internal {
emit AdminChanged(_getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
*/
bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function _getBeacon() internal view returns (address) {
return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
require(
AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
"ERC1967: beacon implementation is not a contract"
);
StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
}
/**
* @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
* not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
*
* Emits a {BeaconUpgraded} event.
*/
function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0 || forceCall) {
AddressUpgradeable.functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
}
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) ||
(!AddressUpgradeable.isContract(address(this)) &&
_initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(
!_initializing && _initialized < version,
"Initializable: contract is already initialized"
);
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.0;
import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
import {Initializable} from "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*
* _Available since v4.1._
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
address private immutable __self = address(this);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
require(address(this) != __self, "Function must be called through delegatecall");
require(_getImplementation() == __self, "Function must be called through active proxy");
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
_;
}
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/**
* @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
return _IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeTo(address newImplementation) public virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data, true);
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeTo} and {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal override onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20Upgradeable.sol";
import "./extensions/IERC20MetadataUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
__ERC20_init_unchained(name_, symbol_);
}
function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[45] private __gap;
}// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/draft-ERC20Permit.sol) pragma solidity ^0.8.0; // EIP-2612 is Final as of 2022-11-01. This file is deprecated. import "./ERC20PermitUpgradeable.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)
pragma solidity ^0.8.0;
import "../ERC20Upgradeable.sol";
import "../../../utils/ContextUpgradeable.sol";
import {Initializable} from "../../../proxy/utils/Initializable.sol";
/**
* @dev Extension of {ERC20} that allows token holders to destroy both their own
* tokens and those that they have an allowance for, in a way that can be
* recognized off-chain (via event analysis).
*/
abstract contract ERC20BurnableUpgradeable is Initializable, ContextUpgradeable, ERC20Upgradeable {
function __ERC20Burnable_init() internal onlyInitializing {
}
function __ERC20Burnable_init_unchained() internal onlyInitializing {
}
/**
* @dev Destroys `amount` tokens from the caller.
*
* See {ERC20-_burn}.
*/
function burn(uint256 amount) public virtual {
_burn(_msgSender(), amount);
}
/**
* @dev Destroys `amount` tokens from `account`, deducting from the caller's
* allowance.
*
* See {ERC20-_burn} and {ERC20-allowance}.
*
* Requirements:
*
* - the caller must have allowance for ``accounts``'s tokens of at least
* `amount`.
*/
function burnFrom(address account, uint256 amount) public virtual {
_spendAllowance(account, _msgSender(), amount);
_burn(account, amount);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/ERC20Permit.sol)
pragma solidity ^0.8.0;
import "./IERC20PermitUpgradeable.sol";
import "../ERC20Upgradeable.sol";
import "../../../utils/cryptography/ECDSAUpgradeable.sol";
import "../../../utils/cryptography/EIP712Upgradeable.sol";
import "../../../utils/CountersUpgradeable.sol";
import {Initializable} from "../../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* _Available since v3.4._
*
* @custom:storage-size 51
*/
abstract contract ERC20PermitUpgradeable is Initializable, ERC20Upgradeable, IERC20PermitUpgradeable, EIP712Upgradeable {
using CountersUpgradeable for CountersUpgradeable.Counter;
mapping(address => CountersUpgradeable.Counter) private _nonces;
// solhint-disable-next-line var-name-mixedcase
bytes32 private constant _PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
/**
* @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
* However, to ensure consistency with the upgradeable transpiler, we will continue
* to reserve a slot.
* @custom:oz-renamed-from _PERMIT_TYPEHASH
*/
// solhint-disable-next-line var-name-mixedcase
bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;
/**
* @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
*
* It's a good idea to use the same `name` that is defined as the ERC20 token name.
*/
function __ERC20Permit_init(string memory name) internal onlyInitializing {
__EIP712_init_unchained(name, "1");
}
function __ERC20Permit_init_unchained(string memory) internal onlyInitializing {}
/**
* @inheritdoc IERC20PermitUpgradeable
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual override {
require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
bytes32 hash = _hashTypedDataV4(structHash);
address signer = ECDSAUpgradeable.recover(hash, v, r, s);
require(signer == owner, "ERC20Permit: invalid signature");
_approve(owner, spender, value);
}
/**
* @inheritdoc IERC20PermitUpgradeable
*/
function nonces(address owner) public view virtual override returns (uint256) {
return _nonces[owner].current();
}
/**
* @inheritdoc IERC20PermitUpgradeable
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view override returns (bytes32) {
return _domainSeparatorV4();
}
/**
* @dev "Consume a nonce": return the current value and increment.
*
* _Available since v4.1._
*/
function _useNonce(address owner) internal virtual returns (uint256 current) {
CountersUpgradeable.Counter storage nonce = _nonces[owner];
current = nonce.current();
nonce.increment();
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20PermitUpgradeable {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
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;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
pragma solidity ^0.8.0;
/**
* @title Counters
* @author Matt Condon (@shrugs)
* @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
* of elements in a mapping, issuing ERC721 ids, or counting request ids.
*
* Include with `using Counters for Counters.Counter;`
*/
library CountersUpgradeable {
struct Counter {
// This variable should never be directly accessed by users of the library: interactions must be restricted to
// the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
// this feature: see https://github.com/ethereum/solidity/issues/4637
uint256 _value; // default: 0
}
function current(Counter storage counter) internal view returns (uint256) {
return counter._value;
}
function increment(Counter storage counter) internal {
unchecked {
counter._value += 1;
}
}
function decrement(Counter storage counter) internal {
uint256 value = counter._value;
require(value > 0, "Counter: decrement overflow");
unchecked {
counter._value = value - 1;
}
}
function reset(Counter storage counter) internal {
counter._value = 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../StringsUpgradeable.sol";
/**
* @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 ECDSAUpgradeable {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode 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 {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]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
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.
/// @solidity memory-safe-assembly
assembly {
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);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode 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 {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
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[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
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.
*
* _Available since v4.2._
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
// 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);
}
// 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);
}
return (signer, RecoverError.NoError);
}
/**
* @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) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, hash)
message := keccak256(0x00, 0x3c)
}
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", StringsUpgradeable.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
data := keccak256(ptr, 0x42)
}
}
/**
* @dev Returns an Ethereum Signed Data with intended validator, created from a
* `validator` and `data` according to the version 0 of EIP-191.
*
* See {recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x00", validator, data));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.8;
import "./ECDSAUpgradeable.sol";
import "../../interfaces/IERC5267Upgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic 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 their contracts 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.
*
* _Available since v3.4._
*
* @custom:storage-size 52
*/
abstract contract EIP712Upgradeable is Initializable, IERC5267Upgradeable {
bytes32 private constant _TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/// @custom:oz-renamed-from _HASHED_NAME
bytes32 private _hashedName;
/// @custom:oz-renamed-from _HASHED_VERSION
bytes32 private _hashedVersion;
string private _name;
string private _version;
/**
* @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].
*/
function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
__EIP712_init_unchained(name, version);
}
function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
_name = name;
_version = version;
// Reset prior values in storage if upgrading
_hashedName = 0;
_hashedVersion = 0;
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
return _buildDomainSeparator();
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), 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 ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/**
* @dev See {EIP-5267}.
*
* _Available since v4.9._
*/
function eip712Domain()
public
view
virtual
override
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
// If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized
// and the EIP712 domain is not reliable, as it will be missing name and version.
require(_hashedName == 0 && _hashedVersion == 0, "EIP712: Uninitialized");
return (
hex"0f", // 01111
_EIP712Name(),
_EIP712Version(),
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
/**
* @dev The name parameter for the EIP712 domain.
*
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712Name() internal virtual view returns (string memory) {
return _name;
}
/**
* @dev The version parameter for the EIP712 domain.
*
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712Version() internal virtual view returns (string memory) {
return _version;
}
/**
* @dev The hash of the name parameter for the EIP712 domain.
*
* NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead.
*/
function _EIP712NameHash() internal view returns (bytes32) {
string memory name = _EIP712Name();
if (bytes(name).length > 0) {
return keccak256(bytes(name));
} else {
// If the name is empty, the contract may have been upgraded without initializing the new storage.
// We return the name hash in storage if non-zero, otherwise we assume the name is empty by design.
bytes32 hashedName = _hashedName;
if (hashedName != 0) {
return hashedName;
} else {
return keccak256("");
}
}
}
/**
* @dev The hash of the version parameter for the EIP712 domain.
*
* NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead.
*/
function _EIP712VersionHash() internal view returns (bytes32) {
string memory version = _EIP712Version();
if (bytes(version).length > 0) {
return keccak256(bytes(version));
} else {
// If the version is empty, the contract may have been upgraded without initializing the new storage.
// We return the version hash in storage if non-zero, otherwise we assume the version is empty by design.
bytes32 hashedVersion = _hashedVersion;
if (hashedVersion != 0) {
return hashedVersion;
} else {
return keccak256("");
}
}
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[48] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165Upgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165Upgradeable).interfaceId;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165Upgradeable {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library MathUpgradeable {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMathUpgradeable {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.0;
/**
* @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 ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
* _Available since v4.9 for `string`, `bytes`._
*/
library StorageSlotUpgradeable {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 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 `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
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/MathUpgradeable.sol";
import "./math/SignedMathUpgradeable.sol";
/**
* @dev String operations.
*/
library StringsUpgradeable {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = MathUpgradeable.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.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, MathUpgradeable.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
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].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)
pragma solidity ^0.8.0;
import "../ERC20.sol";
import "../../../utils/Context.sol";
/**
* @dev Extension of {ERC20} that allows token holders to destroy both their own
* tokens and those that they have an allowance for, in a way that can be
* recognized off-chain (via event analysis).
*/
abstract contract ERC20Burnable is Context, ERC20 {
/**
* @dev Destroys `amount` tokens from the caller.
*
* See {ERC20-_burn}.
*/
function burn(uint256 amount) public virtual {
_burn(_msgSender(), amount);
}
/**
* @dev Destroys `amount` tokens from `account`, deducting from the caller's
* allowance.
*
* See {ERC20-_burn} and {ERC20-allowance}.
*
* Requirements:
*
* - the caller must have allowance for ``accounts``'s tokens of at least
* `amount`.
*/
function burnFrom(address account, uint256 amount) public virtual {
_spendAllowance(account, _msgSender(), amount);
_burn(account, amount);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol)
pragma solidity ^0.8.0;
import "./Address.sol";
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* _Available since v4.1._
*/
abstract contract Multicall {
/**
* @dev Receives and executes a batch of function calls on this contract.
*/
function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
results[i] = Address.functionDelegateCall(address(this), data[i]);
}
return results;
}
}pragma solidity >=0.6.2;
interface IUniswapV2Router01 {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external payable returns (uint amountToken, uint amountETH, uint liquidity);
function removeLiquidity(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB);
function removeLiquidityETH(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountToken, uint amountETH);
function removeLiquidityWithPermit(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountA, uint amountB);
function removeLiquidityETHWithPermit(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountToken, uint amountETH);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}pragma solidity >=0.6.2;
import './IUniswapV2Router01.sol';
interface IUniswapV2Router02 is IUniswapV2Router01 {
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountETH);
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountETH);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
/// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
/// @dev In the implementation you must pay the pool tokens owed for the swap.
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
/// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
/// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
/// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';
/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter is IUniswapV3SwapCallback {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
/// @return amountOut The amount of the received token
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
/// @return amountOut The amount of the received token
function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another token
/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
/// @return amountIn The amount of the input token
function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.0;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
library TransferHelper {
/// @notice Transfers tokens from the targeted address to the given destination
/// @notice Errors with 'STF' if transfer fails
/// @param token The contract address of the token to be transferred
/// @param from The originating address from which the tokens will be transferred
/// @param to The destination address of the transfer
/// @param value The amount to be transferred
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
(bool success, bytes memory data) =
token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF');
}
/// @notice Transfers tokens from msg.sender to a recipient
/// @dev Errors with ST if transfer fails
/// @param token The contract address of the token which will be transferred
/// @param to The recipient of the transfer
/// @param value The value of the transfer
function safeTransfer(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST');
}
/// @notice Approves the stipulated contract to spend the given allowance in the given token
/// @dev Errors with 'SA' if transfer fails
/// @param token The contract address of the token to be approved
/// @param to The target of the approval
/// @param value The amount of the given token the target will be allowed to spend
function safeApprove(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA');
}
/// @notice Transfers ETH to the recipient address
/// @dev Fails with `STE`
/// @param to The destination of the transfer
/// @param value The value to be transferred
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, 'STE');
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/AccessManaged.sol)
pragma solidity ^0.8.22;
import {AuthorityUtils} from "./AuthorityUtils.sol";
import {IAccessManager} from "./IAccessManager.sol";
import {IAccessManaged} from "./IAccessManaged.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
/**
* @dev This contract module makes available a {restricted} modifier. Functions decorated with this modifier will be
* permissioned according to an "authority": a contract like {AccessManager} that follows the {IAuthority} interface,
* implementing a policy that allows certain callers to access certain functions.
*
* IMPORTANT: The `restricted` modifier should never be used on `internal` functions, judiciously used in `public`
* functions, and ideally only used in `external` functions. See {restricted}.
*/
abstract contract AccessManaged is Context, IAccessManaged {
address private _authority;
bool private _consumingSchedule;
/**
* @dev Initializes the contract connected to an initial authority.
*/
constructor(address initialAuthority) {
_setAuthority(initialAuthority);
}
/**
* @dev Restricts access to a function as defined by the connected Authority for this contract and the
* caller and selector of the function that entered the contract.
*
* [IMPORTANT]
* ====
* In general, this modifier should only be used on `external` functions. It is okay to use it on `public`
* functions that are used as external entry points and are not called internally. Unless you know what you're
* doing, it should never be used on `internal` functions. Failure to follow these rules can have critical security
* implications! This is because the permissions are determined by the function that entered the contract, i.e. the
* function at the bottom of the call stack, and not the function where the modifier is visible in the source code.
* ====
*
* [WARNING]
* ====
* Avoid adding this modifier to the https://docs.soliditylang.org/en/v0.8.20/contracts.html#receive-ether-function[`receive()`]
* function or the https://docs.soliditylang.org/en/v0.8.20/contracts.html#fallback-function[`fallback()`]. These
* functions are the only execution paths where a function selector cannot be unambiguously determined from the calldata
* since the selector defaults to `0x00000000` in the `receive()` function and similarly in the `fallback()` function
* if no calldata is provided. (See {_checkCanCall}).
*
* The `receive()` function will always panic whereas the `fallback()` may panic depending on the calldata length.
* ====
*/
modifier restricted() {
_checkCanCall(_msgSender(), _msgData());
_;
}
/// @inheritdoc IAccessManaged
function authority() public view virtual returns (address) {
return _authority;
}
/// @inheritdoc IAccessManaged
function setAuthority(address newAuthority) public virtual {
address caller = _msgSender();
if (caller != authority()) {
revert AccessManagedUnauthorized(caller);
}
if (newAuthority.code.length == 0) {
revert AccessManagedInvalidAuthority(newAuthority);
}
_setAuthority(newAuthority);
}
/// @inheritdoc IAccessManaged
function isConsumingScheduledOp() public view returns (bytes4) {
return
_consumingSchedule
? this.isConsumingScheduledOp.selector
: bytes4(0);
}
/**
* @dev Transfers control to a new authority. Internal function with no access restriction. Allows bypassing the
* permissions set by the current authority.
*/
function _setAuthority(address newAuthority) internal virtual {
_authority = newAuthority;
emit AuthorityUpdated(newAuthority);
}
/**
* @dev Reverts if the caller is not allowed to call the function identified by a selector. Panics if the calldata
* is less than 4 bytes long.
*/
function _checkCanCall(
address caller,
bytes calldata data
) internal virtual {
(bool immediate, uint32 delay) = AuthorityUtils.canCallWithDelay(
authority(),
caller,
address(this),
bytes4(data[0:4])
);
if (!immediate) {
if (delay > 0) {
_consumingSchedule = true;
IAccessManager(authority()).consumeScheduledOp(caller, data);
_consumingSchedule = false;
} else {
revert AccessManagedUnauthorized(caller);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/AccessManager.sol)
pragma solidity ^0.8.22;
import {IAccessManager} from "./IAccessManager.sol";
import {IAccessManaged} from "./IAccessManaged.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {Multicall} from "@openzeppelin/contracts/utils/Multicall.sol";
import {Address} from "./utils/Address.sol";
import {Math} from "./utils/math/Math.sol";
import {Time} from "./utils/types/Time.sol";
/**
* @dev AccessManager is a central contract to store the permissions of a system.
*
* A smart contract under the control of an AccessManager instance is known as a target, and will inherit from the
* {AccessManaged} contract, be connected to this contract as its manager and implement the {AccessManaged-restricted}
* modifier on a set of functions selected to be permissioned. Note that any function without this setup won't be
* effectively restricted.
*
* The restriction rules for such functions are defined in terms of "roles" identified by an `uint64` and scoped
* by target (`address`) and function selectors (`bytes4`). These roles are stored in this contract and can be
* configured by admins (`ADMIN_ROLE` members) after a delay (see {getTargetAdminDelay}).
*
* For each target contract, admins can configure the following without any delay:
*
* * The target's {AccessManaged-authority} via {updateAuthority}.
* * Close or open a target via {setTargetClosed} keeping the permissions intact.
* * The roles that are allowed (or disallowed) to call a given function (identified by its selector) through {setTargetFunctionRole}.
*
* By default every address is member of the `PUBLIC_ROLE` and every target function is restricted to the `ADMIN_ROLE` until configured otherwise.
* Additionally, each role has the following configuration options restricted to this manager's admins:
*
* * A role's admin role via {setRoleAdmin} who can grant or revoke roles.
* * A role's guardian role via {setRoleGuardian} who's allowed to cancel operations.
* * A delay in which a role takes effect after being granted through {setGrantDelay}.
* * A delay of any target's admin action via {setTargetAdminDelay}.
* * A role label for discoverability purposes with {labelRole}.
*
* Any account can be added and removed into any number of these roles by using the {grantRole} and {revokeRole} functions
* restricted to each role's admin (see {getRoleAdmin}).
*
* Since all the permissions of the managed system can be modified by the admins of this instance, it is expected that
* they will be highly secured (e.g., a multisig or a well-configured DAO).
*
* NOTE: This contract implements a form of the {IAuthority} interface, but {canCall} has additional return data so it
* doesn't inherit `IAuthority`. It is however compatible with the `IAuthority` interface since the first 32 bytes of
* the return data are a boolean as expected by that interface.
*
* NOTE: Systems that implement other access control mechanisms (for example using {Ownable}) can be paired with an
* {AccessManager} by transferring permissions (ownership in the case of {Ownable}) directly to the {AccessManager}.
* Users will be able to interact with these contracts through the {execute} function, following the access rules
* registered in the {AccessManager}. Keep in mind that in that context, the msg.sender seen by restricted functions
* will be {AccessManager} itself.
*
* WARNING: When granting permissions over an {Ownable} or {AccessControl} contract to an {AccessManager}, be very
* mindful of the danger associated with functions such as {{Ownable-renounceOwnership}} or
* {{AccessControl-renounceRole}}.
*/
contract AccessManager is Context, Multicall, IAccessManager {
using Time for *;
// Structure that stores the details for a target contract.
struct TargetConfig {
mapping(bytes4 selector => uint64 roleId) allowedRoles;
Time.Delay adminDelay;
bool closed;
}
// Structure that stores the details for a role/account pair. This structures fit into a single slot.
struct Access {
// Timepoint at which the user gets the permission.
// If this is either 0 or in the future, then the role permission is not available.
uint48 since;
// Delay for execution. Only applies to restricted() / execute() calls.
Time.Delay delay;
}
// Structure that stores the details of a role.
struct Role {
// Members of the role.
mapping(address user => Access access) members;
// Admin who can grant or revoke permissions.
uint64 admin;
// Guardian who can cancel operations targeting functions that need this role.
uint64 guardian;
// Delay in which the role takes effect after being granted.
Time.Delay grantDelay;
}
// Structure that stores the details for a scheduled operation. This structure fits into a single slot.
struct Schedule {
// Moment at which the operation can be executed.
uint48 timepoint;
// Operation nonce to allow third-party contracts to identify the operation.
uint32 nonce;
}
uint64 public constant ADMIN_ROLE = type(uint64).min; // 0
uint64 public constant PUBLIC_ROLE = type(uint64).max; // 2**64-1
mapping(address target => TargetConfig mode) private _targets;
mapping(uint64 roleId => Role) private _roles;
mapping(bytes32 operationId => Schedule) private _schedules;
// Used to identify operations that are currently being executed via {execute}.
// This should be transient storage when supported by the EVM.
bytes32 private _executionId;
/**
* @dev Check that the caller is authorized to perform the operation, following the restrictions encoded in
* {_getAdminRestrictions}.
*/
modifier onlyAuthorized() {
_checkAuthorized();
_;
}
constructor(address initialAdmin) {
if (initialAdmin == address(0)) {
revert AccessManagerInvalidInitialAdmin(address(0));
}
// admin is active immediately and without any execution delay.
_grantRole(ADMIN_ROLE, initialAdmin, 0, 0);
}
// =================================================== GETTERS ====================================================
/// @inheritdoc IAccessManager
function canCall(
address caller,
address target,
bytes4 selector
) public view virtual returns (bool immediate, uint32 delay) {
if (isTargetClosed(target)) {
return (false, 0);
} else if (caller == address(this)) {
// Caller is AccessManager, this means the call was sent through {execute} and it already checked
// permissions. We verify that the call "identifier", which is set during {execute}, is correct.
return (_isExecuting(target, selector), 0);
} else {
uint64 roleId = getTargetFunctionRole(target, selector);
(bool isMember, uint32 currentDelay) = hasRole(roleId, caller);
return isMember ? (currentDelay == 0, currentDelay) : (false, 0);
}
}
/// @inheritdoc IAccessManager
function expiration() public view virtual returns (uint32) {
return 1 weeks;
}
/// @inheritdoc IAccessManager
function minSetback() public view virtual returns (uint32) {
return 5 days;
}
/// @inheritdoc IAccessManager
function isTargetClosed(address target) public view virtual returns (bool) {
return _targets[target].closed;
}
/// @inheritdoc IAccessManager
function getTargetFunctionRole(
address target,
bytes4 selector
) public view virtual returns (uint64) {
return _targets[target].allowedRoles[selector];
}
/// @inheritdoc IAccessManager
function getTargetAdminDelay(
address target
) public view virtual returns (uint32) {
return _targets[target].adminDelay.get();
}
/// @inheritdoc IAccessManager
function getRoleAdmin(uint64 roleId) public view virtual returns (uint64) {
return _roles[roleId].admin;
}
/// @inheritdoc IAccessManager
function getRoleGuardian(
uint64 roleId
) public view virtual returns (uint64) {
return _roles[roleId].guardian;
}
/// @inheritdoc IAccessManager
function getRoleGrantDelay(
uint64 roleId
) public view virtual returns (uint32) {
return _roles[roleId].grantDelay.get();
}
/// @inheritdoc IAccessManager
function getAccess(
uint64 roleId,
address account
)
public
view
virtual
returns (
uint48 since,
uint32 currentDelay,
uint32 pendingDelay,
uint48 effect
)
{
Access storage access = _roles[roleId].members[account];
since = access.since;
(currentDelay, pendingDelay, effect) = access.delay.getFull();
return (since, currentDelay, pendingDelay, effect);
}
/// @inheritdoc IAccessManager
function hasRole(
uint64 roleId,
address account
) public view virtual returns (bool isMember, uint32 executionDelay) {
if (roleId == PUBLIC_ROLE) {
return (true, 0);
} else {
(uint48 hasRoleSince, uint32 currentDelay, , ) = getAccess(
roleId,
account
);
return (
hasRoleSince != 0 && hasRoleSince <= Time.timestamp(),
currentDelay
);
}
}
// =============================================== ROLE MANAGEMENT ===============================================
/// @inheritdoc IAccessManager
function labelRole(
uint64 roleId,
string calldata label
) public virtual onlyAuthorized {
if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
revert AccessManagerLockedRole(roleId);
}
emit RoleLabel(roleId, label);
}
/// @inheritdoc IAccessManager
function grantRole(
uint64 roleId,
address account,
uint32 executionDelay
) public virtual onlyAuthorized {
_grantRole(roleId, account, getRoleGrantDelay(roleId), executionDelay);
}
/// @inheritdoc IAccessManager
function revokeRole(
uint64 roleId,
address account
) public virtual onlyAuthorized {
_revokeRole(roleId, account);
}
/// @inheritdoc IAccessManager
function renounceRole(
uint64 roleId,
address callerConfirmation
) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessManagerBadConfirmation();
}
_revokeRole(roleId, callerConfirmation);
}
/// @inheritdoc IAccessManager
function setRoleAdmin(
uint64 roleId,
uint64 admin
) public virtual onlyAuthorized {
_setRoleAdmin(roleId, admin);
}
/// @inheritdoc IAccessManager
function setRoleGuardian(
uint64 roleId,
uint64 guardian
) public virtual onlyAuthorized {
_setRoleGuardian(roleId, guardian);
}
/// @inheritdoc IAccessManager
function setGrantDelay(
uint64 roleId,
uint32 newDelay
) public virtual onlyAuthorized {
_setGrantDelay(roleId, newDelay);
}
/**
* @dev Internal version of {grantRole} without access control. Returns true if the role was newly granted.
*
* Emits a {RoleGranted} event.
*/
function _grantRole(
uint64 roleId,
address account,
uint32 grantDelay,
uint32 executionDelay
) internal virtual returns (bool) {
if (roleId == PUBLIC_ROLE) {
revert AccessManagerLockedRole(roleId);
}
bool newMember = _roles[roleId].members[account].since == 0;
uint48 since;
if (newMember) {
since = Time.timestamp() + grantDelay;
_roles[roleId].members[account] = Access({
since: since,
delay: executionDelay.toDelay()
});
} else {
// No setback here. Value can be reset by doing revoke + grant, effectively allowing the admin to perform
// any change to the execution delay within the duration of the role admin delay.
(_roles[roleId].members[account].delay, since) = _roles[roleId]
.members[account]
.delay
.withUpdate(executionDelay, 0);
}
emit RoleGranted(roleId, account, executionDelay, since, newMember);
return newMember;
}
/**
* @dev Internal version of {revokeRole} without access control. This logic is also used by {renounceRole}.
* Returns true if the role was previously granted.
*
* Emits a {RoleRevoked} event if the account had the role.
*/
function _revokeRole(
uint64 roleId,
address account
) internal virtual returns (bool) {
if (roleId == PUBLIC_ROLE) {
revert AccessManagerLockedRole(roleId);
}
if (_roles[roleId].members[account].since == 0) {
return false;
}
delete _roles[roleId].members[account];
emit RoleRevoked(roleId, account);
return true;
}
/**
* @dev Internal version of {setRoleAdmin} without access control.
*
* Emits a {RoleAdminChanged} event.
*
* NOTE: Setting the admin role as the `PUBLIC_ROLE` is allowed, but it will effectively allow
* anyone to set grant or revoke such role.
*/
function _setRoleAdmin(uint64 roleId, uint64 admin) internal virtual {
if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
revert AccessManagerLockedRole(roleId);
}
_roles[roleId].admin = admin;
emit RoleAdminChanged(roleId, admin);
}
/**
* @dev Internal version of {setRoleGuardian} without access control.
*
* Emits a {RoleGuardianChanged} event.
*
* NOTE: Setting the guardian role as the `PUBLIC_ROLE` is allowed, but it will effectively allow
* anyone to cancel any scheduled operation for such role.
*/
function _setRoleGuardian(uint64 roleId, uint64 guardian) internal virtual {
if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
revert AccessManagerLockedRole(roleId);
}
_roles[roleId].guardian = guardian;
emit RoleGuardianChanged(roleId, guardian);
}
/**
* @dev Internal version of {setGrantDelay} without access control.
*
* Emits a {RoleGrantDelayChanged} event.
*/
function _setGrantDelay(uint64 roleId, uint32 newDelay) internal virtual {
if (roleId == PUBLIC_ROLE) {
revert AccessManagerLockedRole(roleId);
}
uint48 effect;
(_roles[roleId].grantDelay, effect) = _roles[roleId]
.grantDelay
.withUpdate(newDelay, minSetback());
emit RoleGrantDelayChanged(roleId, newDelay, effect);
}
// ============================================= FUNCTION MANAGEMENT ==============================================
/// @inheritdoc IAccessManager
function setTargetFunctionRole(
address target,
bytes4[] calldata selectors,
uint64 roleId
) public virtual onlyAuthorized {
for (uint256 i = 0; i < selectors.length; ++i) {
_setTargetFunctionRole(target, selectors[i], roleId);
}
}
/**
* @dev Internal version of {setTargetFunctionRole} without access control.
*
* Emits a {TargetFunctionRoleUpdated} event.
*/
function _setTargetFunctionRole(
address target,
bytes4 selector,
uint64 roleId
) internal virtual {
_targets[target].allowedRoles[selector] = roleId;
emit TargetFunctionRoleUpdated(target, selector, roleId);
}
/// @inheritdoc IAccessManager
function setTargetAdminDelay(
address target,
uint32 newDelay
) public virtual onlyAuthorized {
_setTargetAdminDelay(target, newDelay);
}
/**
* @dev Internal version of {setTargetAdminDelay} without access control.
*
* Emits a {TargetAdminDelayUpdated} event.
*/
function _setTargetAdminDelay(
address target,
uint32 newDelay
) internal virtual {
uint48 effect;
(_targets[target].adminDelay, effect) = _targets[target]
.adminDelay
.withUpdate(newDelay, minSetback());
emit TargetAdminDelayUpdated(target, newDelay, effect);
}
// =============================================== MODE MANAGEMENT ================================================
/// @inheritdoc IAccessManager
function setTargetClosed(
address target,
bool closed
) public virtual onlyAuthorized {
_setTargetClosed(target, closed);
}
/**
* @dev Set the closed flag for a contract. This is an internal setter with no access restrictions.
*
* Emits a {TargetClosed} event.
*/
function _setTargetClosed(address target, bool closed) internal virtual {
if (target == address(this)) {
revert AccessManagerLockedAccount(target);
}
_targets[target].closed = closed;
emit TargetClosed(target, closed);
}
// ============================================== DELAYED OPERATIONS ==============================================
/// @inheritdoc IAccessManager
function getSchedule(bytes32 id) public view virtual returns (uint48) {
uint48 timepoint = _schedules[id].timepoint;
return _isExpired(timepoint) ? 0 : timepoint;
}
/// @inheritdoc IAccessManager
function getNonce(bytes32 id) public view virtual returns (uint32) {
return _schedules[id].nonce;
}
/// @inheritdoc IAccessManager
function schedule(
address target,
bytes calldata data,
uint48 when
) public virtual returns (bytes32 operationId, uint32 nonce) {
address caller = _msgSender();
// Fetch restrictions that apply to the caller on the targeted function
(, uint32 setback) = _canCallExtended(caller, target, data);
uint48 minWhen = Time.timestamp() + setback;
// If call with delay is not authorized, or if requested timing is too soon, revert
if (setback == 0 || (when > 0 && when < minWhen)) {
revert AccessManagerUnauthorizedCall(
caller,
target,
_checkSelector(data)
);
}
// Reuse variable due to stack too deep
when = uint48(Math.max(when, minWhen)); // cast is safe: both inputs are uint48
// If caller is authorised, schedule operation
operationId = hashOperation(caller, target, data);
_checkNotScheduled(operationId);
unchecked {
// It's not feasible to overflow the nonce in less than 1000 years
nonce = _schedules[operationId].nonce + 1;
}
_schedules[operationId].timepoint = when;
_schedules[operationId].nonce = nonce;
emit OperationScheduled(operationId, nonce, when, caller, target, data);
// Using named return values because otherwise we get stack too deep
}
/**
* @dev Reverts if the operation is currently scheduled and has not expired.
* (Note: This function was introduced due to stack too deep errors in schedule.)
*/
function _checkNotScheduled(bytes32 operationId) private view {
uint48 prevTimepoint = _schedules[operationId].timepoint;
if (prevTimepoint != 0 && !_isExpired(prevTimepoint)) {
revert AccessManagerAlreadyScheduled(operationId);
}
}
/// @inheritdoc IAccessManager
// Reentrancy is not an issue because permissions are checked on msg.sender. Additionally,
// _consumeScheduledOp guarantees a scheduled operation is only executed once.
// slither-disable-next-line reentrancy-no-eth
function execute(
address target,
bytes calldata data
) public payable virtual returns (uint32) {
address caller = _msgSender();
// Fetch restrictions that apply to the caller on the targeted function
(bool immediate, uint32 setback) = _canCallExtended(
caller,
target,
data
);
// If call is not authorized, revert
if (!immediate && setback == 0) {
revert AccessManagerUnauthorizedCall(
caller,
target,
_checkSelector(data)
);
}
bytes32 operationId = hashOperation(caller, target, data);
uint32 nonce;
// If caller is authorised, check operation was scheduled early enough
// Consume an available schedule even if there is no currently enforced delay
if (setback != 0 || getSchedule(operationId) != 0) {
nonce = _consumeScheduledOp(operationId);
}
// Mark the target and selector as authorised
bytes32 executionIdBefore = _executionId;
_executionId = _hashExecutionId(target, _checkSelector(data));
// Perform call
Address.functionCallWithValue(target, data, msg.value);
// Reset execute identifier
_executionId = executionIdBefore;
return nonce;
}
/// @inheritdoc IAccessManager
function cancel(
address caller,
address target,
bytes calldata data
) public virtual returns (uint32) {
address msgsender = _msgSender();
bytes4 selector = _checkSelector(data);
bytes32 operationId = hashOperation(caller, target, data);
if (_schedules[operationId].timepoint == 0) {
revert AccessManagerNotScheduled(operationId);
} else if (caller != msgsender) {
// calls can only be canceled by the account that scheduled them, a global admin, or by a guardian of the required role.
(bool isAdmin, ) = hasRole(ADMIN_ROLE, msgsender);
(bool isGuardian, ) = hasRole(
getRoleGuardian(getTargetFunctionRole(target, selector)),
msgsender
);
if (!isAdmin && !isGuardian) {
revert AccessManagerUnauthorizedCancel(
msgsender,
caller,
target,
selector
);
}
}
delete _schedules[operationId].timepoint; // reset the timepoint, keep the nonce
uint32 nonce = _schedules[operationId].nonce;
emit OperationCanceled(operationId, nonce);
return nonce;
}
/// @inheritdoc IAccessManager
function consumeScheduledOp(
address caller,
bytes calldata data
) public virtual {
address target = _msgSender();
if (
IAccessManaged(target).isConsumingScheduledOp() !=
IAccessManaged.isConsumingScheduledOp.selector
) {
revert AccessManagerUnauthorizedConsume(target);
}
_consumeScheduledOp(hashOperation(caller, target, data));
}
/**
* @dev Internal variant of {consumeScheduledOp} that operates on bytes32 operationId.
*
* Returns the nonce of the scheduled operation that is consumed.
*/
function _consumeScheduledOp(
bytes32 operationId
) internal virtual returns (uint32) {
uint48 timepoint = _schedules[operationId].timepoint;
uint32 nonce = _schedules[operationId].nonce;
if (timepoint == 0) {
revert AccessManagerNotScheduled(operationId);
} else if (timepoint > Time.timestamp()) {
revert AccessManagerNotReady(operationId);
} else if (_isExpired(timepoint)) {
revert AccessManagerExpired(operationId);
}
delete _schedules[operationId].timepoint; // reset the timepoint, keep the nonce
emit OperationExecuted(operationId, nonce);
return nonce;
}
/// @inheritdoc IAccessManager
function hashOperation(
address caller,
address target,
bytes calldata data
) public view virtual returns (bytes32) {
return keccak256(abi.encode(caller, target, data));
}
// ==================================================== OTHERS ====================================================
/// @inheritdoc IAccessManager
function updateAuthority(
address target,
address newAuthority
) public virtual onlyAuthorized {
IAccessManaged(target).setAuthority(newAuthority);
}
// ================================================= ADMIN LOGIC ==================================================
/**
* @dev Check if the current call is authorized according to admin logic.
*/
function _checkAuthorized() private {
address caller = _msgSender();
(bool immediate, uint32 delay) = _canCallSelf(caller, _msgData());
if (!immediate) {
if (delay == 0) {
(, uint64 requiredRole, ) = _getAdminRestrictions(_msgData());
revert AccessManagerUnauthorizedAccount(caller, requiredRole);
} else {
_consumeScheduledOp(
hashOperation(caller, address(this), _msgData())
);
}
}
}
/**
* @dev Get the admin restrictions of a given function call based on the function and arguments involved.
*
* Returns:
* - bool restricted: does this data match a restricted operation
* - uint64: which role is this operation restricted to
* - uint32: minimum delay to enforce for that operation (max between operation's delay and admin's execution delay)
*/
function _getAdminRestrictions(
bytes calldata data
)
private
view
returns (bool restricted, uint64 roleAdminId, uint32 executionDelay)
{
if (data.length < 4) {
return (false, 0, 0);
}
bytes4 selector = _checkSelector(data);
// Restricted to ADMIN with no delay beside any execution delay the caller may have
if (
selector == this.labelRole.selector ||
selector == this.setRoleAdmin.selector ||
selector == this.setRoleGuardian.selector ||
selector == this.setGrantDelay.selector ||
selector == this.setTargetAdminDelay.selector
) {
return (true, ADMIN_ROLE, 0);
}
// Restricted to ADMIN with the admin delay corresponding to the target
if (
selector == this.updateAuthority.selector ||
selector == this.setTargetClosed.selector ||
selector == this.setTargetFunctionRole.selector
) {
// First argument is a target.
address target = abi.decode(data[0x04:0x24], (address));
uint32 delay = getTargetAdminDelay(target);
return (true, ADMIN_ROLE, delay);
}
// Restricted to that role's admin with no delay beside any execution delay the caller may have.
if (
selector == this.grantRole.selector ||
selector == this.revokeRole.selector
) {
// First argument is a roleId.
uint64 roleId = abi.decode(data[0x04:0x24], (uint64));
return (true, getRoleAdmin(roleId), 0);
}
return (false, 0, 0);
}
// =================================================== HELPERS ====================================================
/**
* @dev An extended version of {canCall} for internal usage that checks {_canCallSelf}
* when the target is this contract.
*
* Returns:
* - bool immediate: whether the operation can be executed immediately (with no delay)
* - uint32 delay: the execution delay
*/
function _canCallExtended(
address caller,
address target,
bytes calldata data
) private view returns (bool immediate, uint32 delay) {
if (target == address(this)) {
return _canCallSelf(caller, data);
} else {
return
data.length < 4
? (false, 0)
: canCall(caller, target, _checkSelector(data));
}
}
/**
* @dev A version of {canCall} that checks for admin restrictions in this contract.
*/
function _canCallSelf(
address caller,
bytes calldata data
) private view returns (bool immediate, uint32 delay) {
if (data.length < 4) {
return (false, 0);
}
if (caller == address(this)) {
// Caller is AccessManager, this means the call was sent through {execute} and it already checked
// permissions. We verify that the call "identifier", which is set during {execute}, is correct.
return (_isExecuting(address(this), _checkSelector(data)), 0);
}
(
bool enabled,
uint64 roleId,
uint32 operationDelay
) = _getAdminRestrictions(data);
if (!enabled) {
return (false, 0);
}
(bool inRole, uint32 executionDelay) = hasRole(roleId, caller);
if (!inRole) {
return (false, 0);
}
// downcast is safe because both options are uint32
delay = uint32(Math.max(operationDelay, executionDelay));
return (delay == 0, delay);
}
/**
* @dev Returns true if a call with `target` and `selector` is being executed via {executed}.
*/
function _isExecuting(
address target,
bytes4 selector
) private view returns (bool) {
return _executionId == _hashExecutionId(target, selector);
}
/**
* @dev Returns true if a schedule timepoint is past its expiration deadline.
*/
function _isExpired(uint48 timepoint) private view returns (bool) {
return timepoint + expiration() <= Time.timestamp();
}
/**
* @dev Extracts the selector from calldata. Panics if data is not at least 4 bytes
*/
function _checkSelector(bytes calldata data) private pure returns (bytes4) {
return bytes4(data[0:4]);
}
/**
* @dev Hashing function for execute protection
*/
function _hashExecutionId(
address target,
bytes4 selector
) private pure returns (bytes32) {
return keccak256(abi.encode(target, selector));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/AuthorityUtils.sol)
pragma solidity ^0.8.22;
import {IAuthority} from "./IAuthority.sol";
library AuthorityUtils {
/**
* @dev Since `AccessManager` implements an extended IAuthority interface, invoking `canCall` with backwards compatibility
* for the preexisting `IAuthority` interface requires special care to avoid reverting on insufficient return data.
* This helper function takes care of invoking `canCall` in a backwards compatible way without reverting.
*/
function canCallWithDelay(
address authority,
address caller,
address target,
bytes4 selector
) internal view returns (bool immediate, uint32 delay) {
(bool success, bytes memory data) = authority.staticcall(
abi.encodeCall(IAuthority.canCall, (caller, target, selector))
);
if (success) {
if (data.length >= 0x40) {
(immediate, delay) = abi.decode(data, (bool, uint32));
} else if (data.length >= 0x20) {
immediate = abi.decode(data, (bool));
}
}
return (immediate, delay);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/IAccessManaged.sol)
pragma solidity ^0.8.22;
interface IAccessManaged {
/**
* @dev Authority that manages this contract was updated.
*/
event AuthorityUpdated(address authority);
error AccessManagedUnauthorized(address caller);
error AccessManagedRequiredDelay(address caller, uint32 delay);
error AccessManagedInvalidAuthority(address authority);
/**
* @dev Returns the current authority.
*/
function authority() external view returns (address);
/**
* @dev Transfers control to a new authority. The caller must be the current authority.
*/
function setAuthority(address) external;
/**
* @dev Returns true only in the context of a delayed restricted call, at the moment that the scheduled operation is
* being consumed. Prevents denial of service for delayed restricted calls in the case that the contract performs
* attacker controlled calls.
*/
function isConsumingScheduledOp() external view returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/IAccessManager.sol)
pragma solidity ^0.8.22;
interface IAccessManager {
/**
* @dev A delayed operation was scheduled.
*/
event OperationScheduled(
bytes32 indexed operationId,
uint32 indexed nonce,
uint48 schedule,
address caller,
address target,
bytes data
);
/**
* @dev A scheduled operation was executed.
*/
event OperationExecuted(bytes32 indexed operationId, uint32 indexed nonce);
/**
* @dev A scheduled operation was canceled.
*/
event OperationCanceled(bytes32 indexed operationId, uint32 indexed nonce);
/**
* @dev Informational labelling for a roleId.
*/
event RoleLabel(uint64 indexed roleId, string label);
/**
* @dev Emitted when `account` is granted `roleId`.
*
* NOTE: The meaning of the `since` argument depends on the `newMember` argument.
* If the role is granted to a new member, the `since` argument indicates when the account becomes a member of the role,
* otherwise it indicates the execution delay for this account and roleId is updated.
*/
event RoleGranted(
uint64 indexed roleId,
address indexed account,
uint32 delay,
uint48 since,
bool newMember
);
/**
* @dev Emitted when `account` membership or `roleId` is revoked. Unlike granting, revoking is instantaneous.
*/
event RoleRevoked(uint64 indexed roleId, address indexed account);
/**
* @dev Role acting as admin over a given `roleId` is updated.
*/
event RoleAdminChanged(uint64 indexed roleId, uint64 indexed admin);
/**
* @dev Role acting as guardian over a given `roleId` is updated.
*/
event RoleGuardianChanged(uint64 indexed roleId, uint64 indexed guardian);
/**
* @dev Grant delay for a given `roleId` will be updated to `delay` when `since` is reached.
*/
event RoleGrantDelayChanged(
uint64 indexed roleId,
uint32 delay,
uint48 since
);
/**
* @dev Target mode is updated (true = closed, false = open).
*/
event TargetClosed(address indexed target, bool closed);
/**
* @dev Role required to invoke `selector` on `target` is updated to `roleId`.
*/
event TargetFunctionRoleUpdated(
address indexed target,
bytes4 selector,
uint64 indexed roleId
);
/**
* @dev Admin delay for a given `target` will be updated to `delay` when `since` is reached.
*/
event TargetAdminDelayUpdated(
address indexed target,
uint32 delay,
uint48 since
);
error AccessManagerAlreadyScheduled(bytes32 operationId);
error AccessManagerNotScheduled(bytes32 operationId);
error AccessManagerNotReady(bytes32 operationId);
error AccessManagerExpired(bytes32 operationId);
error AccessManagerLockedAccount(address account);
error AccessManagerLockedRole(uint64 roleId);
error AccessManagerBadConfirmation();
error AccessManagerUnauthorizedAccount(address msgsender, uint64 roleId);
error AccessManagerUnauthorizedCall(
address caller,
address target,
bytes4 selector
);
error AccessManagerUnauthorizedConsume(address target);
error AccessManagerUnauthorizedCancel(
address msgsender,
address caller,
address target,
bytes4 selector
);
error AccessManagerInvalidInitialAdmin(address initialAdmin);
/**
* @dev Check if an address (`caller`) is authorised to call a given function on a given contract directly (with
* no restriction). Additionally, it returns the delay needed to perform the call indirectly through the {schedule}
* & {execute} workflow.
*
* This function is usually called by the targeted contract to control immediate execution of restricted functions.
* Therefore we only return true if the call can be performed without any delay. If the call is subject to a
* previously set delay (not zero), then the function should return false and the caller should schedule the operation
* for future execution.
*
* If `immediate` is true, the delay can be disregarded and the operation can be immediately executed, otherwise
* the operation can be executed if and only if delay is greater than 0.
*
* NOTE: The IAuthority interface does not include the `uint32` delay. This is an extension of that interface that
* is backward compatible. Some contracts may thus ignore the second return argument. In that case they will fail
* to identify the indirect workflow, and will consider calls that require a delay to be forbidden.
*
* NOTE: This function does not report the permissions of this manager itself. These are defined by the
* {_canCallSelf} function instead.
*/
function canCall(
address caller,
address target,
bytes4 selector
) external view returns (bool allowed, uint32 delay);
/**
* @dev Expiration delay for scheduled proposals. Defaults to 1 week.
*
* IMPORTANT: Avoid overriding the expiration with 0. Otherwise every contract proposal will be expired immediately,
* disabling any scheduling usage.
*/
function expiration() external view returns (uint32);
/**
* @dev Minimum setback for all delay updates, with the exception of execution delays. It
* can be increased without setback (and reset via {revokeRole} in the case event of an
* accidental increase). Defaults to 5 days.
*/
function minSetback() external view returns (uint32);
/**
* @dev Get whether the contract is closed disabling any access. Otherwise role permissions are applied.
*/
function isTargetClosed(address target) external view returns (bool);
/**
* @dev Get the role required to call a function.
*/
function getTargetFunctionRole(
address target,
bytes4 selector
) external view returns (uint64);
/**
* @dev Get the admin delay for a target contract. Changes to contract configuration are subject to this delay.
*/
function getTargetAdminDelay(address target) external view returns (uint32);
/**
* @dev Get the id of the role that acts as an admin for the given role.
*
* The admin permission is required to grant the role, revoke the role and update the execution delay to execute
* an operation that is restricted to this role.
*/
function getRoleAdmin(uint64 roleId) external view returns (uint64);
/**
* @dev Get the role that acts as a guardian for a given role.
*
* The guardian permission allows canceling operations that have been scheduled under the role.
*/
function getRoleGuardian(uint64 roleId) external view returns (uint64);
/**
* @dev Get the role current grant delay.
*
* Its value may change at any point without an event emitted following a call to {setGrantDelay}.
* Changes to this value, including effect timepoint are notified in advance by the {RoleGrantDelayChanged} event.
*/
function getRoleGrantDelay(uint64 roleId) external view returns (uint32);
/**
* @dev Get the access details for a given account for a given role. These details include the timepoint at which
* membership becomes active, and the delay applied to all operation by this user that requires this permission
* level.
*
* Returns:
* [0] Timestamp at which the account membership becomes valid. 0 means role is not granted.
* [1] Current execution delay for the account.
* [2] Pending execution delay for the account.
* [3] Timestamp at which the pending execution delay will become active. 0 means no delay update is scheduled.
*/
function getAccess(
uint64 roleId,
address account
)
external
view
returns (
uint48 since,
uint32 currentDelay,
uint32 pendingDelay,
uint48 effect
);
/**
* @dev Check if a given account currently has the permission level corresponding to a given role. Note that this
* permission might be associated with an execution delay. {getAccess} can provide more details.
*/
function hasRole(
uint64 roleId,
address account
) external view returns (bool isMember, uint32 executionDelay);
/**
* @dev Give a label to a role, for improved role discoverability by UIs.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleLabel} event.
*/
function labelRole(uint64 roleId, string calldata label) external;
/**
* @dev Add `account` to `roleId`, or change its execution delay.
*
* This gives the account the authorization to call any function that is restricted to this role. An optional
* execution delay (in seconds) can be set. If that delay is non 0, the user is required to schedule any operation
* that is restricted to members of this role. The user will only be able to execute the operation after the delay has
* passed, before it has expired. During this period, admin and guardians can cancel the operation (see {cancel}).
*
* If the account has already been granted this role, the execution delay will be updated. This update is not
* immediate and follows the delay rules. For example, if a user currently has a delay of 3 hours, and this is
* called to reduce that delay to 1 hour, the new delay will take some time to take effect, enforcing that any
* operation executed in the 3 hours that follows this update was indeed scheduled before this update.
*
* Requirements:
*
* - the caller must be an admin for the role (see {getRoleAdmin})
* - granted role must not be the `PUBLIC_ROLE`
*
* Emits a {RoleGranted} event.
*/
function grantRole(
uint64 roleId,
address account,
uint32 executionDelay
) external;
/**
* @dev Remove an account from a role, with immediate effect. If the account does not have the role, this call has
* no effect.
*
* Requirements:
*
* - the caller must be an admin for the role (see {getRoleAdmin})
* - revoked role must not be the `PUBLIC_ROLE`
*
* Emits a {RoleRevoked} event if the account had the role.
*/
function revokeRole(uint64 roleId, address account) external;
/**
* @dev Renounce role permissions for the calling account with immediate effect. If the sender is not in
* the role this call has no effect.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* Emits a {RoleRevoked} event if the account had the role.
*/
function renounceRole(uint64 roleId, address callerConfirmation) external;
/**
* @dev Change admin role for a given role.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleAdminChanged} event
*/
function setRoleAdmin(uint64 roleId, uint64 admin) external;
/**
* @dev Change guardian role for a given role.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleGuardianChanged} event
*/
function setRoleGuardian(uint64 roleId, uint64 guardian) external;
/**
* @dev Update the delay for granting a `roleId`.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleGrantDelayChanged} event.
*/
function setGrantDelay(uint64 roleId, uint32 newDelay) external;
/**
* @dev Set the role required to call functions identified by the `selectors` in the `target` contract.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {TargetFunctionRoleUpdated} event per selector.
*/
function setTargetFunctionRole(
address target,
bytes4[] calldata selectors,
uint64 roleId
) external;
/**
* @dev Set the delay for changing the configuration of a given target contract.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {TargetAdminDelayUpdated} event.
*/
function setTargetAdminDelay(address target, uint32 newDelay) external;
/**
* @dev Set the closed flag for a contract.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {TargetClosed} event.
*/
function setTargetClosed(address target, bool closed) external;
/**
* @dev Return the timepoint at which a scheduled operation will be ready for execution. This returns 0 if the
* operation is not yet scheduled, has expired, was executed, or was canceled.
*/
function getSchedule(bytes32 id) external view returns (uint48);
/**
* @dev Return the nonce for the latest scheduled operation with a given id. Returns 0 if the operation has never
* been scheduled.
*/
function getNonce(bytes32 id) external view returns (uint32);
/**
* @dev Schedule a delayed operation for future execution, and return the operation identifier. It is possible to
* choose the timestamp at which the operation becomes executable as long as it satisfies the execution delays
* required for the caller. The special value zero will automatically set the earliest possible time.
*
* Returns the `operationId` that was scheduled. Since this value is a hash of the parameters, it can reoccur when
* the same parameters are used; if this is relevant, the returned `nonce` can be used to uniquely identify this
* scheduled operation from other occurrences of the same `operationId` in invocations of {execute} and {cancel}.
*
* Emits a {OperationScheduled} event.
*
* NOTE: It is not possible to concurrently schedule more than one operation with the same `target` and `data`. If
* this is necessary, a random byte can be appended to `data` to act as a salt that will be ignored by the target
* contract if it is using standard Solidity ABI encoding.
*/
function schedule(
address target,
bytes calldata data,
uint48 when
) external returns (bytes32 operationId, uint32 nonce);
/**
* @dev Execute a function that is delay restricted, provided it was properly scheduled beforehand, or the
* execution delay is 0.
*
* Returns the nonce that identifies the previously scheduled operation that is executed, or 0 if the
* operation wasn't previously scheduled (if the caller doesn't have an execution delay).
*
* Emits an {OperationExecuted} event only if the call was scheduled and delayed.
*/
function execute(
address target,
bytes calldata data
) external payable returns (uint32);
/**
* @dev Cancel a scheduled (delayed) operation. Returns the nonce that identifies the previously scheduled
* operation that is cancelled.
*
* Requirements:
*
* - the caller must be the proposer, a guardian of the targeted function, or a global admin
*
* Emits a {OperationCanceled} event.
*/
function cancel(
address caller,
address target,
bytes calldata data
) external returns (uint32);
/**
* @dev Consume a scheduled operation targeting the caller. If such an operation exists, mark it as consumed
* (emit an {OperationExecuted} event and clean the state). Otherwise, throw an error.
*
* This is useful for contract that want to enforce that calls targeting them were scheduled on the manager,
* with all the verifications that it implies.
*
* Emit a {OperationExecuted} event.
*/
function consumeScheduledOp(address caller, bytes calldata data) external;
/**
* @dev Hashing function for delayed operations.
*/
function hashOperation(
address caller,
address target,
bytes calldata data
) external view returns (bytes32);
/**
* @dev Changes the authority of a target managed by this manager instance.
*
* Requirements:
*
* - the caller must be a global admin
*/
function updateAuthority(address target, address newAuthority) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/IAuthority.sol)
pragma solidity ^0.8.22;
/**
* @dev Standard interface for permissioning originally defined in Dappsys.
*/
interface IAuthority {
/**
* @dev Returns true if the caller can invoke on a target the function identified by a function selector.
*/
function canCall(
address caller,
address target,
bytes4 selector
) external view returns (bool allowed);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.22;
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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
/**
* @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.
*/
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();
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.22;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an success flag (no overflow).
*/
function tryAdd(
uint256 a,
uint256 b
) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
*/
function trySub(
uint256 a,
uint256 b
) internal pure returns (bool success, uint256 result) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
*/
function tryMul(
uint256 a,
uint256 b
) internal pure returns (bool success, uint256 result) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(
uint256 a,
uint256 b
) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(
uint256 a,
uint256 b
) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return a == 0 ? 0 : (a - 1) / b + 1;
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 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²⁵⁶ + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= prod1) {
Panic.panic(
denominator == 0
? Panic.DIVISION_BY_ZERO
: Panic.UNDER_OVERFLOW
);
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
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⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// 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²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
return
mulDiv(x, y, denominator) +
SafeCast.toUint(
unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0
);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, expect 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Ferma's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return x < 0 ? (n - uint256(-x)) : uint256(x); // Wrap the result if it's negative.
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(
uint256 b,
uint256 e,
uint256 m
) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked has failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(
uint256 b,
uint256 e,
uint256 m
) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
/// @solidity memory-safe-assembly
assembly {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(
gas(),
0x05,
dataPtr,
mload(result),
dataPtr,
mLen
)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLen))
}
}
/**
* @dev Returns whether the provided byte array is zero.
*/
function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
for (uint256 i = 0; i < byteArray.length; ++i) {
if (byteArray[i] != 0) {
return false;
}
}
return true;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* This method is based on Newton's method for computing square roots; the algorithm is restricted to only
* using integer operations.
*/
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
// Take care of easy edge cases when a == 0 or a == 1
if (a <= 1) {
return a;
}
// In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
// sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
// the current value as `ε_n = | x_n - sqrt(a) |`.
//
// For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
// of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
// bigger than any uint256.
//
// By noticing that
// `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
// we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
// to the msb function.
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
// We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
//
// We can refine our estimation by noticing that the middle of that interval minimizes the error.
// If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
// This is going to be our x_0 (and ε_0)
xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
// From here, Newton's method give us:
// x_{n+1} = (x_n + a / x_n) / 2
//
// One should note that:
// x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
// = ((x_n² + a) / (2 * x_n))² - a
// = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
// = (x_n² - a)² / (2 * x_n)²
// = ((x_n² - a) / (2 * x_n))²
// ≥ 0
// Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
//
// This gives us the proof of quadratic convergence of the sequence:
// ε_{n+1} = | x_{n+1} - sqrt(a) |
// = | (x_n + a / x_n) / 2 - sqrt(a) |
// = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
// = | (x_n - sqrt(a))² / (2 * x_n) |
// = | ε_n² / (2 * x_n) |
// = ε_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// ε_1 = ε_0² / | (2 * x_0) |
// ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
// ≤ 2**(2*e-4) / (3 * 2**(e-1))
// ≤ 2**(e-3) / 3
// ≤ 2**(e-3-log2(3))
// ≤ 2**(e-4.5)
//
// For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
// ε_{n+1} = ε_n² / | (2 * x_n) |
// ≤ (2**(e-k))² / (2 * 2**(e-1))
// ≤ 2**(2*e-2*k) / 2**e
// ≤ 2**(e-2*k)
xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72
// Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
// ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
// sqrt(a) or sqrt(a) + 1.
return xn - SafeCast.toUint(xn > a / xn);
}
}
/**
* @dev Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(
uint256 a,
Rounding rounding
) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return
result +
SafeCast.toUint(
unsignedRoundsUp(rounding) && result * result < a
);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 exp;
unchecked {
exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
value >>= exp;
result += exp;
exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
value >>= exp;
result += exp;
exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
value >>= exp;
result += exp;
exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
value >>= exp;
result += exp;
exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
value >>= exp;
result += exp;
exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
value >>= exp;
result += exp;
exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
value >>= exp;
result += exp;
result += SafeCast.toUint(value > 1);
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(
uint256 value,
Rounding rounding
) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return
result +
SafeCast.toUint(
unsignedRoundsUp(rounding) && 1 << result < value
);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(
uint256 value,
Rounding rounding
) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return
result +
SafeCast.toUint(
unsignedRoundsUp(rounding) && 10 ** result < value
);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 isGt;
unchecked {
isGt = SafeCast.toUint(value > (1 << 128) - 1);
value >>= isGt * 128;
result += isGt * 16;
isGt = SafeCast.toUint(value > (1 << 64) - 1);
value >>= isGt * 64;
result += isGt * 8;
isGt = SafeCast.toUint(value > (1 << 32) - 1);
value >>= isGt * 32;
result += isGt * 4;
isGt = SafeCast.toUint(value > (1 << 16) - 1);
value >>= isGt * 16;
result += isGt * 2;
result += SafeCast.toUint(value > (1 << 8) - 1);
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(
uint256 value,
Rounding rounding
) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return
result +
SafeCast.toUint(
unsignedRoundsUp(rounding) && 1 << (result << 3) < value
);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// 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.22;
/**
* @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) {
/// @solidity memory-safe-assembly
assembly {
u := iszero(iszero(b))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
/**
* @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].
*/
// 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 {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/types/Time.sol)
pragma solidity ^0.8.22;
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, uint32, uint48) {
(uint32 valueBefore, uint32 valueAfter, uint48 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, uint32, uint48) {
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.22;
import "../AccessManager/AccessManager.sol";
import "./IFairsideAdmin.sol";
contract FairSideAdmin is AccessManager, IFairsideAdmin {
constructor() AccessManager(msg.sender) {
_grantRole(uint64(FSRoles.FS_ADMIN_ROLE), msg.sender, 0, 0);
_grantRole(uint64(FSRoles.FS_CLAIMS_OWNER_ROLE), msg.sender, 0, 0);
_grantRole(uint64(FSRoles.FS_FAIR_OWNER_ROLE), msg.sender, 0, 0);
_grantRole(uint64(FSRoles.FS_CLAIMS_GUARDIAN_ROLE), msg.sender, 0, 0);
_grantRole(uint64(FSRoles.FS_NETWORK_GUARDIAN_ROLE), msg.sender, 0, 0);
_grantRole(uint64(FSRoles.FS_FAIR_GUARDIAN_ROLE), msg.sender, 0, 0);
_grantRole(uint64(FSRoles.FS_NETWORK_OWNER_ROLE), msg.sender, 0, 0);
_grantRole(uint64(FSRoles.FS_CLAIMS_ISSUER), msg.sender, 0, 0);
_grantRole(uint64(FSRoles.FS_BOUNTY_POOL_OWNER_ROLE), msg.sender, 0, 0);
_grantRole(uint64(FSRoles.FS_CLAIMS_VERIFIER), msg.sender, 0, 0);
}
function hasFSRole(
FSRoles role,
address account
) external view returns (bool) {
(bool isMember, ) = AccessManager.hasRole(uint64(role), account);
return isMember;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
interface IFairsideAdmin {
enum FSRoles {
VOID,
FS_ADMIN_ROLE,
FS_CLAIMS_GUARDIAN_ROLE,
FS_CLAIMS_OWNER_ROLE,
FS_NETWORK_GUARDIAN_ROLE,
FS_NETWORK_OWNER_ROLE,
FS_FAIR_GUARDIAN_ROLE,
FS_FAIR_OWNER_ROLE,
FS_CLAIMS_ISSUER,
FS_BOUNTY_POOL_OWNER_ROLE,
FS_CLAIMS_VERIFIER
}
function hasFSRole(
FSRoles role,
address account
) external view returns (bool);
}// SPDX-License-Identifier: BSD-4-Clause /* * ABDK Math Quad Smart Contract Library. Copyright © 2019 by ABDK Consulting. * Author: Mikhail Vladimirov <[email protected]> */ pragma solidity ^0.8.22; /** * Smart contract library of mathematical functions operating with IEEE 754 * quadruple-precision binary floating-point numbers (quadruple precision * numbers). As long as quadruple precision numbers are 16-bytes long, they are * represented by bytes16 type. */ library ABDKMathQuad { /* * 0. */ bytes16 private constant POSITIVE_ZERO = 0x00000000000000000000000000000000; /* * -0. */ bytes16 private constant NEGATIVE_ZERO = 0x80000000000000000000000000000000; /* * +Infinity. */ bytes16 private constant POSITIVE_INFINITY = 0x7FFF0000000000000000000000000000; /* * -Infinity. */ bytes16 private constant NEGATIVE_INFINITY = 0xFFFF0000000000000000000000000000; /* * Canonical NaN value. */ bytes16 private constant NaN = 0x7FFF8000000000000000000000000000; /** * Convert signed 256-bit integer number into quadruple precision number. * * @param x signed 256-bit integer number * @return quadruple precision number */ function fromInt(int256 x) internal pure returns (bytes16) { unchecked { if (x == 0) return bytes16(0); else { // We rely on overflow behavior here // we get the absolute value of x to put into signifier uint256 result = uint256(x > 0 ? x : -x); uint256 msb = mostSignificantBit(result); // multiplied by 2^n to put most significant bit to the position 112 if (msb < 112) result <<= 112 - msb; // if the signifier is too large then we cut off the least significant bits after 112 else if (msb > 112) result >>= msb - 112; // 16383 = 0x3FFF // based of this table of precedence of operators https://docs.soliditylang.org/en/latest/cheatsheet.html // then +, <<, &, | // to calculate the exponent we use the offset 16383, i.e. numbers greater than 16383 represent positive exponents // numbers lesser than 16383 represent negative exponents result = (result & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF) | ((16383 + msb) << 112); // if x is negative then we put the first bit of the 16bytes to be 1 if (x < 0) result |= 0x80000000000000000000000000000000; return bytes16(uint128(result)); } } } /** * Convert quadruple precision number into signed 256-bit integer number * rounding towards zero. Revert on overflow. * * @param x quadruple precision number * @return signed 256-bit integer number */ function toInt(bytes16 x) internal pure returns (int256) { unchecked { // first we cut off the last 112 bits representing the significand // then we cut off the first bit representing the sign // we are then left with the exponent uint256 exponent = (uint128(x) >> 112) & 0x7FFF; // 16638 = 0x40fe // if exponent is at least 16638, then the real exponent is at least 16638 - 16383 = 255, which exceeds the range for int256 require(exponent <= 16638); // Overflow // if the exponent is less than 16383, then the resulting number is less than 1, so rounded to 0, since the result is an integer if (exponent < 16383) return 0; // Underflow // we get the significand then append the 1 from the left // reminder that for a number 1.00101 * 2^8, significand is 00101 // and the result below in this example would be 1.00101 * 2^112 uint256 result = (uint256(uint128(x)) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF) | 0x10000000000000000000000000000; // if the exponent is less than 16495, i.e. the real exponent is less than 16495 - 16383 = 112 // then we need to trim out some digits from the result calculated above if (exponent < 16495) result >>= 16495 - exponent; // else we need to add more zeros else if (exponent > 16495) result <<= exponent - 16495; // we add the negative sign if necessary if (uint128(x) >= 0x80000000000000000000000000000000) { // Negative // in the negative case we require that the esult is at most 2^255 require( result <= 0x8000000000000000000000000000000000000000000000000000000000000000 ); return -int256(result); // We rely on overflow behavior here } else { // in the positive case we require that the result is at most 2^256 - 1 require( result <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ); return int256(result); } } } /** * Convert unsigned 256-bit integer number into quadruple precision number. * * @param x unsigned 256-bit integer number * @return quadruple precision number */ function fromUInt(uint256 x) internal pure returns (bytes16) { unchecked { // the process is similar to fromInt if (x == 0) return bytes16(0); else { uint256 result = x; uint256 msb = mostSignificantBit(result); if (msb < 112) result <<= 112 - msb; else if (msb > 112) result >>= msb - 112; result = (result & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF) | ((16383 + msb) << 112); return bytes16(uint128(result)); } } } /** * Convert quadruple precision number into unsigned 256-bit integer number * rounding towards zero. Revert on underflow. Note, that negative floating * point numbers in range (-1.0 .. 0.0) may be converted to unsigned integer * without error, because they are rounded to zero. * * @param x quadruple precision number * @return unsigned 256-bit integer number */ function toUInt(bytes16 x) internal pure returns (uint256) { unchecked { uint256 exponent = (uint128(x) >> 112) & 0x7FFF; if (exponent < 16383) return 0; // Underflow require(uint128(x) < 0x80000000000000000000000000000000); // Negative require(exponent <= 16638); // Overflow uint256 result = (uint256(uint128(x)) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF) | 0x10000000000000000000000000000; if (exponent < 16495) result >>= 16495 - exponent; else if (exponent > 16495) result <<= exponent - 16495; return result; } } /** * Convert signed 128.128 bit fixed point number into quadruple precision * number. * * @param x signed 128.128 bit fixed point number * @return quadruple precision number */ function from128x128(int256 x) internal pure returns (bytes16) { unchecked { if (x == 0) return bytes16(0); else { // We rely on overflow behavior here uint256 result = uint256(x > 0 ? x : -x); uint256 msb = mostSignificantBit(result); if (msb < 112) result <<= 112 - msb; else if (msb > 112) result >>= msb - 112; result = (result & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF) | ((16255 + msb) << 112); if (x < 0) result |= 0x80000000000000000000000000000000; return bytes16(uint128(result)); } } } /** * Convert quadruple precision number into signed 128.128 bit fixed point * number. Revert on overflow. * * @param x quadruple precision number * @return signed 128.128 bit fixed point number */ function to128x128(bytes16 x) internal pure returns (int256) { unchecked { uint256 exponent = (uint128(x) >> 112) & 0x7FFF; require(exponent <= 16510); // Overflow if (exponent < 16255) return 0; // Underflow uint256 result = (uint256(uint128(x)) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF) | 0x10000000000000000000000000000; if (exponent < 16367) result >>= 16367 - exponent; else if (exponent > 16367) result <<= exponent - 16367; if (uint128(x) >= 0x80000000000000000000000000000000) { // Negative require( result <= 0x8000000000000000000000000000000000000000000000000000000000000000 ); return -int256(result); // We rely on overflow behavior here } else { require( result <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ); return int256(result); } } } /** * Convert signed 64.64 bit fixed point number into quadruple precision * number. * * @param x signed 64.64 bit fixed point number * @return quadruple precision number */ function from64x64(int128 x) internal pure returns (bytes16) { unchecked { if (x == 0) return bytes16(0); else { // We rely on overflow behavior here uint256 result = uint128(x > 0 ? x : -x); uint256 msb = mostSignificantBit(result); if (msb < 112) result <<= 112 - msb; else if (msb > 112) result >>= msb - 112; result = (result & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF) | ((16319 + msb) << 112); if (x < 0) result |= 0x80000000000000000000000000000000; return bytes16(uint128(result)); } } } /** * Convert quadruple precision number into signed 64.64 bit fixed point * number. Revert on overflow. * * @param x quadruple precision number * @return signed 64.64 bit fixed point number */ function to64x64(bytes16 x) internal pure returns (int128) { unchecked { uint256 exponent = (uint128(x) >> 112) & 0x7FFF; require(exponent <= 16446); // Overflow if (exponent < 16319) return 0; // Underflow uint256 result = (uint256(uint128(x)) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF) | 0x10000000000000000000000000000; if (exponent < 16431) result >>= 16431 - exponent; else if (exponent > 16431) result <<= exponent - 16431; if (uint128(x) >= 0x80000000000000000000000000000000) { // Negative require(result <= 0x80000000000000000000000000000000); return -int128(int256(result)); // We rely on overflow behavior here } else { require(result <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return int128(int256(result)); } } } /** * Convert octuple precision number into quadruple precision number. * * @param x octuple precision number * @return quadruple precision number */ function fromOctuple(bytes32 x) internal pure returns (bytes16) { unchecked { bool negative = x & 0x8000000000000000000000000000000000000000000000000000000000000000 > 0; uint256 exponent = (uint256(x) >> 236) & 0x7FFFF; uint256 significand = uint256(x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; if (exponent == 0x7FFFF) { if (significand > 0) return NaN; else return negative ? NEGATIVE_INFINITY : POSITIVE_INFINITY; } if (exponent > 278526) return negative ? NEGATIVE_INFINITY : POSITIVE_INFINITY; else if (exponent < 245649) return negative ? NEGATIVE_ZERO : POSITIVE_ZERO; else if (exponent < 245761) { significand = (significand | 0x100000000000000000000000000000000000000000000000000000000000) >> (245885 - exponent); exponent = 0; } else { significand >>= 124; exponent -= 245760; } uint128 result = uint128(significand | (exponent << 112)); if (negative) result |= 0x80000000000000000000000000000000; return bytes16(result); } } /** * Convert quadruple precision number into octuple precision number. * * @param x quadruple precision number * @return octuple precision number */ function toOctuple(bytes16 x) internal pure returns (bytes32) { unchecked { uint256 exponent = (uint128(x) >> 112) & 0x7FFF; uint256 result = uint128(x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; if (exponent == 0x7FFF) exponent = 0x7FFFF; // Infinity or NaN else if (exponent == 0) { if (result > 0) { uint256 msb = mostSignificantBit(result); result = (result << (236 - msb)) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; exponent = 245649 + msb; } } else { result <<= 124; exponent += 245760; } result |= exponent << 236; if (uint128(x) >= 0x80000000000000000000000000000000) result |= 0x8000000000000000000000000000000000000000000000000000000000000000; return bytes32(result); } } /** * Convert double precision number into quadruple precision number. * * @param x double precision number * @return quadruple precision number */ function fromDouble(bytes8 x) internal pure returns (bytes16) { unchecked { uint256 exponent = (uint64(x) >> 52) & 0x7FF; uint256 result = uint64(x) & 0xFFFFFFFFFFFFF; if (exponent == 0x7FF) exponent = 0x7FFF; // Infinity or NaN else if (exponent == 0) { if (result > 0) { uint256 msb = mostSignificantBit(result); result = (result << (112 - msb)) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; exponent = 15309 + msb; } } else { result <<= 60; exponent += 15360; } result |= exponent << 112; if (x & 0x8000000000000000 > 0) result |= 0x80000000000000000000000000000000; return bytes16(uint128(result)); } } /** * Convert quadruple precision number into double precision number. * * @param x quadruple precision number * @return double precision number */ function toDouble(bytes16 x) internal pure returns (bytes8) { unchecked { bool negative = uint128(x) >= 0x80000000000000000000000000000000; uint256 exponent = (uint128(x) >> 112) & 0x7FFF; uint256 significand = uint128(x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; if (exponent == 0x7FFF) { if (significand > 0) return 0x7FF8000000000000; // NaN else return negative ? bytes8(0xFFF0000000000000) // -Infinity : bytes8(0x7FF0000000000000); // Infinity } if (exponent > 17406) return negative ? bytes8(0xFFF0000000000000) // -Infinity : bytes8(0x7FF0000000000000); // Infinity else if (exponent < 15309) return negative ? bytes8(0x8000000000000000) // -0 : bytes8(0x0000000000000000); // 0 else if (exponent < 15361) { significand = (significand | 0x10000000000000000000000000000) >> (15421 - exponent); exponent = 0; } else { significand >>= 60; exponent -= 15360; } uint64 result = uint64(significand | (exponent << 52)); if (negative) result |= 0x8000000000000000; return bytes8(result); } } /** * Test whether given quadruple precision number is NaN. * * @param x quadruple precision number * @return true if x is NaN, false otherwise */ function isNaN(bytes16 x) internal pure returns (bool) { unchecked { return uint128(x) & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF > 0x7FFF0000000000000000000000000000; } } /** * Test whether given quadruple precision number is positive or negative * infinity. * * @param x quadruple precision number * @return true if x is positive or negative infinity, false otherwise */ function isInfinity(bytes16 x) internal pure returns (bool) { unchecked { return uint128(x) & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x7FFF0000000000000000000000000000; } } /** * Calculate sign of x, i.e. -1 if x is negative, 0 if x if zero, and 1 if x * is positive. Note that sign (-0) is zero. Revert if x is NaN. * * @param x quadruple precision number * @return sign of x */ function sign(bytes16 x) internal pure returns (int8) { unchecked { uint128 absoluteX = uint128(x) & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; require(absoluteX <= 0x7FFF0000000000000000000000000000); // Not NaN if (absoluteX == 0) return 0; else if (uint128(x) >= 0x80000000000000000000000000000000) return -1; else return 1; } } /** * Calculate sign (x - y). Revert if either argument is NaN, or both * arguments are infinities of the same sign. * * @param x quadruple precision number * @param y quadruple precision number * @return sign (x - y) */ function cmp(bytes16 x, bytes16 y) internal pure returns (int8) { unchecked { uint128 absoluteX = uint128(x) & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; require(absoluteX <= 0x7FFF0000000000000000000000000000); // Not NaN uint128 absoluteY = uint128(y) & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; require(absoluteY <= 0x7FFF0000000000000000000000000000); // Not NaN // Not infinities of the same sign require(x != y || absoluteX < 0x7FFF0000000000000000000000000000); if (x == y) return 0; else { bool negativeX = uint128(x) >= 0x80000000000000000000000000000000; bool negativeY = uint128(y) >= 0x80000000000000000000000000000000; if (negativeX) { if (negativeY) return absoluteX > absoluteY ? -1 : int8(1); else return -1; } else { if (negativeY) return 1; else return absoluteX > absoluteY ? int8(1) : -1; } } } } /** * Test whether x equals y. NaN, infinity, and -infinity are not equal to * anything. * * @param x quadruple precision number * @param y quadruple precision number * @return true if x equals to y, false otherwise */ function eq(bytes16 x, bytes16 y) internal pure returns (bool) { unchecked { if (x == y) { return uint128(x) & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF < 0x7FFF0000000000000000000000000000; } else return false; } } /** * Calculate x + y. Special values behave in the following way: * * NaN + x = NaN for any x. * Infinity + x = Infinity for any finite x. * -Infinity + x = -Infinity for any finite x. * Infinity + Infinity = Infinity. * -Infinity + -Infinity = -Infinity. * Infinity + -Infinity = -Infinity + Infinity = NaN. * * @param x quadruple precision number * @param y quadruple precision number * @return quadruple precision number */ function add(bytes16 x, bytes16 y) internal pure returns (bytes16) { unchecked { // we get rid of the last 112 bits (significand) and the first bit (sign) to get the exponent uint256 xExponent = (uint128(x) >> 112) & 0x7FFF; uint256 yExponent = (uint128(y) >> 112) & 0x7FFF; // if the xExponent is 0x7FFF then x is positive infinity, negative infinity or NaN if (xExponent == 0x7FFF) { // if yExponent is 0x7FFF then y is positive infinity, negative of NaN if (yExponent == 0x7FFF) { // if they are equal then they are both infinity of the same sign or NaN and their addition keeps the value if (x == y) return x; // else the addition gives undefined else return NaN; // else if y is a normal number, then their addition keeps the value of x } else return x; // else if x is a normal number and y is an edge case, then their addition keeps the value of y } else if (yExponent == 0x7FFF) return y; // now we deal with the case when x and y are both normal numbers else { // the sign of x is its first bit bool xSign = uint128(x) >= 0x80000000000000000000000000000000; // the significand (or signifier) is the last 112 bits uint256 xSignifier = uint128(x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // if the exponent is 0 then the number should already be 0 (can the significand be anything other than 0 if the exponent is 0?). What is the purpose of setting the exponent to 1 here? // If the exponent is 0 then the number doesn't necessarily be 0, they are called subnormal numbers. In this case the exponent is 1, because the real exponent is -16382, and we don't need to append 1 to the front if (xExponent == 0) xExponent = 1; // we add 1 to the front of the significand else xSignifier |= 0x10000000000000000000000000000; bool ySign = uint128(y) >= 0x80000000000000000000000000000000; uint256 ySignifier = uint128(y) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; if (yExponent == 0) yExponent = 1; else ySignifier |= 0x10000000000000000000000000000; // if the xSignificand is 0, can only happen when the xExponent was 0 // then the result is equal to y if (xSignifier == 0) return y == NEGATIVE_ZERO ? POSITIVE_ZERO : y; else if (ySignifier == 0) return x == NEGATIVE_ZERO ? POSITIVE_ZERO : x; // now we deal with the case when both x and y are two normal nonzero numbers else { int256 delta = int256(xExponent) - int256(yExponent); if (xSign == ySign) { // y is too small compared to x so considered precision error if (delta > 112) return x; // we shift y accordingly, the common exponent is xExponent else if (delta > 0) ySignifier >>= uint256(delta); // x is too small compared to y so considered precision error else if (delta < -112) return y; // we shift x accordingly, the common exponent is yExponent else if (delta < 0) { xSignifier >>= uint256(-delta); xExponent = yExponent; } // we add the two significand together xSignifier += ySignifier; // we the addition creates one additional bit then we shift to the right by 1 then increase the exponent by 1 if (xSignifier >= 0x20000000000000000000000000000) { xSignifier >>= 1; xExponent += 1; } // overflowing makes the result become infinity if (xExponent == 0x7FFF) return xSign ? NEGATIVE_INFINITY : POSITIVE_INFINITY; else { // I fail to see when this case can ever happen if (xSignifier < 0x10000000000000000000000000000) xExponent = 0; // we get rid of the bit one in the beginning else xSignifier &= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; return bytes16( uint128( ( xSign ? 0x80000000000000000000000000000000 : 0 ) | (xExponent << 112) | xSignifier ) ); } } else { // case when x and y have different signs if (delta > 0) { xSignifier <<= 1; xExponent -= 1; } else if (delta < 0) { ySignifier <<= 1; xExponent = yExponent - 1; } // if y is too small compared to x then the effect of the subtraction after rounding is the same as subtraction of 1 if (delta > 112) ySignifier = 1; // if delta is at least 2 then y can only reduce the most significant bit of x by 1, so here we shift ySignifier accordingly else if (delta > 1) ySignifier = ((ySignifier - 1) >> uint256(delta - 1)) + 1; else if (delta < -112) xSignifier = 1; else if (delta < -1) xSignifier = ((xSignifier - 1) >> uint256(-delta - 1)) + 1; if (xSignifier >= ySignifier) xSignifier -= ySignifier; else { xSignifier = ySignifier - xSignifier; xSign = ySign; } if (xSignifier == 0) return POSITIVE_ZERO; uint256 msb = mostSignificantBit(xSignifier); if (msb == 113) { // if the difference is represented by 113 bit then we cut off the last bit and increase the exponent accordingly // we also get rid of the number one in the beginning xSignifier = (xSignifier >> 1) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; xExponent += 1; } else if (msb < 112) { // if there are less than 112 precision bits then we shift accordingly uint256 shift = 112 - msb; if (xExponent > shift) { xSignifier = (xSignifier << shift) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; xExponent -= shift; } else { xSignifier <<= xExponent - 1; xExponent = 0; } } else xSignifier &= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // returns infinity for overflow/underflow if (xExponent == 0x7FFF) return xSign ? NEGATIVE_INFINITY : POSITIVE_INFINITY; else return bytes16( uint128( ( xSign ? 0x80000000000000000000000000000000 : 0 ) | (xExponent << 112) | xSignifier ) ); } } } } } /** * Calculate x - y. Special values behave in the following way: * * NaN - x = NaN for any x. * Infinity - x = Infinity for any finite x. * -Infinity - x = -Infinity for any finite x. * Infinity - -Infinity = Infinity. * -Infinity - Infinity = -Infinity. * Infinity - Infinity = -Infinity - -Infinity = NaN. * * @param x quadruple precision number * @param y quadruple precision number * @return quadruple precision number */ function sub(bytes16 x, bytes16 y) internal pure returns (bytes16) { unchecked { // x + y.neg() return add(x, y ^ 0x80000000000000000000000000000000); } } /** * Calculate x * y. Special values behave in the following way: * * NaN * x = NaN for any x. * Infinity * x = Infinity for any finite positive x. * Infinity * x = -Infinity for any finite negative x. * -Infinity * x = -Infinity for any finite positive x. * -Infinity * x = Infinity for any finite negative x. * Infinity * 0 = NaN. * -Infinity * 0 = NaN. * Infinity * Infinity = Infinity. * Infinity * -Infinity = -Infinity. * -Infinity * Infinity = -Infinity. * -Infinity * -Infinity = Infinity. * * @param x quadruple precision number * @param y quadruple precision number * @return quadruple precision number */ function mul(bytes16 x, bytes16 y) internal pure returns (bytes16) { unchecked { uint256 xExponent = (uint128(x) >> 112) & 0x7FFF; uint256 yExponent = (uint128(y) >> 112) & 0x7FFF; // firts we deal with edge cases if (xExponent == 0x7FFF) { // when both x and y are special numbers if (yExponent == 0x7FFF) { // & has higher priority than ^ https://docs.soliditylang.org/en/latest/cheatsheet.html // if x and y are both equal then the product is either infinity or Nan if (x == y) return x ^ (y & 0x80000000000000000000000000000000); // if x and y are infinities of different signs then return minus infinity else if (x ^ y == 0x80000000000000000000000000000000) return x | y; else return NaN; } else { // if y is 0 then its product with infinity is undefined if (y & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0) return NaN; // else return the infinity or Nan accordingly else return x ^ (y & 0x80000000000000000000000000000000); } // the case when y is a special number and x is a normal number } else if (yExponent == 0x7FFF) { if (x & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0) return NaN; else return y ^ (x & 0x80000000000000000000000000000000); // the case when both x and y are normal numbers } else { // we get rid of the sign and exponent uint256 xSignifier = uint128(x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // still not sure what the purpose of this line is if (xExponent == 0) xExponent = 1; // we add 1 to the front else xSignifier |= 0x10000000000000000000000000000; uint256 ySignifier = uint128(y) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; if (yExponent == 0) yExponent = 1; else ySignifier |= 0x10000000000000000000000000000; // we multiply the significand together xSignifier *= ySignifier; if (xSignifier == 0) return (x ^ y) & 0x80000000000000000000000000000000 > 0 ? NEGATIVE_ZERO : POSITIVE_ZERO; // we add the exponent together xExponent += yExponent; // why don't we just simply use the function mostSignificantBit // but it seems to me that xSignifier always has either 224 or 225 bits, so the last branch is not needed? uint256 msb = xSignifier >= 0x200000000000000000000000000000000000000000000000000000000 ? 225 : xSignifier >= 0x100000000000000000000000000000000000000000000000000000000 ? 224 : mostSignificantBit(xSignifier); // why is 16496 chosen? if (xExponent + msb < 16496) { // Underflow xExponent = 0; xSignifier = 0; } else if (xExponent + msb < 16608) { // Subnormal if (xExponent < 16496) xSignifier >>= 16496 - xExponent; else if (xExponent > 16496) xSignifier <<= xExponent - 16496; xExponent = 0; } else if (xExponent + msb > 49373) { xExponent = 0x7FFF; xSignifier = 0; } else { if (msb > 112) xSignifier >>= msb - 112; else if (msb < 112) xSignifier <<= 112 - msb; xSignifier &= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; xExponent = xExponent + msb - 16607; } return bytes16( uint128( uint128( (x ^ y) & 0x80000000000000000000000000000000 ) | (xExponent << 112) | xSignifier ) ); } } } /** * Calculate x / y. Special values behave in the following way: * * NaN / x = NaN for any x. * x / NaN = NaN for any x. * Infinity / x = Infinity for any finite non-negative x. * Infinity / x = -Infinity for any finite negative x including -0. * -Infinity / x = -Infinity for any finite non-negative x. * -Infinity / x = Infinity for any finite negative x including -0. * x / Infinity = 0 for any finite non-negative x. * x / -Infinity = -0 for any finite non-negative x. * x / Infinity = -0 for any finite non-negative x including -0. * x / -Infinity = 0 for any finite non-negative x including -0. * * Infinity / Infinity = NaN. * Infinity / -Infinity = -NaN. * -Infinity / Infinity = -NaN. * -Infinity / -Infinity = NaN. * * Division by zero behaves in the following way: * * x / 0 = Infinity for any finite positive x. * x / -0 = -Infinity for any finite positive x. * x / 0 = -Infinity for any finite negative x. * x / -0 = Infinity for any finite negative x. * 0 / 0 = NaN. * 0 / -0 = NaN. * -0 / 0 = NaN. * -0 / -0 = NaN. * * @param x quadruple precision number * @param y quadruple precision number * @return quadruple precision number */ function div(bytes16 x, bytes16 y) internal pure returns (bytes16) { unchecked { uint256 xExponent = (uint128(x) >> 112) & 0x7FFF; uint256 yExponent = (uint128(y) >> 112) & 0x7FFF; if (xExponent == 0x7FFF) { if (yExponent == 0x7FFF) return NaN; else return x ^ (y & 0x80000000000000000000000000000000); } else if (yExponent == 0x7FFF) { if (y & 0x0000FFFFFFFFFFFFFFFFFFFFFFFFFFFF != 0) return NaN; else return POSITIVE_ZERO | ((x ^ y) & 0x80000000000000000000000000000000); } else if (y & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0) { if (x & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0) return NaN; else return POSITIVE_INFINITY | ((x ^ y) & 0x80000000000000000000000000000000); } else { uint256 ySignifier = uint128(y) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; if (yExponent == 0) yExponent = 1; else ySignifier |= 0x10000000000000000000000000000; uint256 xSignifier = uint128(x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; if (xExponent == 0) { if (xSignifier != 0) { uint256 shift = 226 - mostSignificantBit(xSignifier); xSignifier <<= shift; xExponent = 1; yExponent += shift - 114; } } else { xSignifier = (xSignifier | 0x10000000000000000000000000000) << 114; } xSignifier = xSignifier / ySignifier; if (xSignifier == 0) return (x ^ y) & 0x80000000000000000000000000000000 > 0 ? NEGATIVE_ZERO : POSITIVE_ZERO; assert(xSignifier >= 0x1000000000000000000000000000); uint256 msb = xSignifier >= 0x80000000000000000000000000000 ? mostSignificantBit(xSignifier) : xSignifier >= 0x40000000000000000000000000000 ? 114 : xSignifier >= 0x20000000000000000000000000000 ? 113 : 112; if (xExponent + msb > yExponent + 16497) { // Overflow xExponent = 0x7FFF; xSignifier = 0; } else if (xExponent + msb + 16380 < yExponent) { // Underflow xExponent = 0; xSignifier = 0; } else if (xExponent + msb + 16268 < yExponent) { // Subnormal if (xExponent + 16380 > yExponent) xSignifier <<= xExponent + 16380 - yExponent; else if (xExponent + 16380 < yExponent) xSignifier >>= yExponent - xExponent - 16380; xExponent = 0; } else { // Normal if (msb > 112) xSignifier >>= msb - 112; xSignifier &= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; xExponent = xExponent + msb + 16269 - yExponent; } return bytes16( uint128( uint128( (x ^ y) & 0x80000000000000000000000000000000 ) | (xExponent << 112) | xSignifier ) ); } } } /** * Calculate -x. * * @param x quadruple precision number * @return quadruple precision number */ function neg(bytes16 x) internal pure returns (bytes16) { unchecked { // using xor to switch the bit representing sign return x ^ 0x80000000000000000000000000000000; } } /** * Calculate |x|. * * @param x quadruple precision number * @return quadruple precision number */ function abs(bytes16 x) internal pure returns (bytes16) { unchecked { return x & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; } } /** * Calculate square root of x. Return NaN on negative x excluding -0. * * @param x quadruple precision number * @return quadruple precision number */ function sqrt(bytes16 x) internal pure returns (bytes16) { unchecked { if (uint128(x) > 0x80000000000000000000000000000000) return NaN; else { uint256 xExponent = (uint128(x) >> 112) & 0x7FFF; if (xExponent == 0x7FFF) return x; else { uint256 xSignifier = uint128(x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; if (xExponent == 0) xExponent = 1; else xSignifier |= 0x10000000000000000000000000000; if (xSignifier == 0) return POSITIVE_ZERO; bool oddExponent = xExponent & 0x1 == 0; xExponent = (xExponent + 16383) >> 1; if (oddExponent) { if (xSignifier >= 0x10000000000000000000000000000) xSignifier <<= 113; else { uint256 msb = mostSignificantBit(xSignifier); uint256 shift = (226 - msb) & 0xFE; xSignifier <<= shift; xExponent -= (shift - 112) >> 1; } } else { if (xSignifier >= 0x10000000000000000000000000000) xSignifier <<= 112; else { uint256 msb = mostSignificantBit(xSignifier); uint256 shift = (225 - msb) & 0xFE; xSignifier <<= shift; xExponent -= (shift - 112) >> 1; } } uint256 r = 0x10000000000000000000000000000; r = (r + xSignifier / r) >> 1; r = (r + xSignifier / r) >> 1; r = (r + xSignifier / r) >> 1; r = (r + xSignifier / r) >> 1; r = (r + xSignifier / r) >> 1; r = (r + xSignifier / r) >> 1; r = (r + xSignifier / r) >> 1; // Seven iterations should be enough uint256 r1 = xSignifier / r; if (r1 < r) r = r1; return bytes16( uint128( (xExponent << 112) | (r & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF) ) ); } } } } /** * Calculate binary logarithm of x. Return NaN on negative x excluding -0. * * @param x quadruple precision number * @return quadruple precision number */ function log_2(bytes16 x) internal pure returns (bytes16) { unchecked { if (uint128(x) > 0x80000000000000000000000000000000) return NaN; else if (x == 0x3FFF0000000000000000000000000000) return POSITIVE_ZERO; else { uint256 xExponent = (uint128(x) >> 112) & 0x7FFF; if (xExponent == 0x7FFF) return x; else { uint256 xSignifier = uint128(x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; if (xExponent == 0) xExponent = 1; else xSignifier |= 0x10000000000000000000000000000; if (xSignifier == 0) return NEGATIVE_INFINITY; bool resultNegative; uint256 resultExponent = 16495; uint256 resultSignifier; if (xExponent >= 0x3FFF) { resultNegative = false; resultSignifier = xExponent - 0x3FFF; xSignifier <<= 15; } else { resultNegative = true; if (xSignifier >= 0x10000000000000000000000000000) { resultSignifier = 0x3FFE - xExponent; xSignifier <<= 15; } else { uint256 msb = mostSignificantBit(xSignifier); resultSignifier = 16493 - msb; xSignifier <<= 127 - msb; } } if (xSignifier == 0x80000000000000000000000000000000) { if (resultNegative) resultSignifier += 1; uint256 shift = 112 - mostSignificantBit(resultSignifier); resultSignifier <<= shift; resultExponent -= shift; } else { uint256 bb = resultNegative ? 1 : 0; while ( resultSignifier < 0x10000000000000000000000000000 ) { resultSignifier <<= 1; resultExponent -= 1; xSignifier *= xSignifier; uint256 b = xSignifier >> 255; resultSignifier += b ^ bb; xSignifier >>= 127 + b; } } return bytes16( uint128( ( resultNegative ? 0x80000000000000000000000000000000 : 0 ) | (resultExponent << 112) | (resultSignifier & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF) ) ); } } } } /** * Calculate natural logarithm of x. Return NaN on negative x excluding -0. * * @param x quadruple precision number * @return quadruple precision number */ function ln(bytes16 x) internal pure returns (bytes16) { unchecked { return mul(log_2(x), 0x3FFE62E42FEFA39EF35793C7673007E5); } } /** * Calculate 2^x. * * @param x quadruple precision number * @return quadruple precision number */ function pow_2(bytes16 x) internal pure returns (bytes16) { unchecked { bool xNegative = uint128(x) > 0x80000000000000000000000000000000; uint256 xExponent = (uint128(x) >> 112) & 0x7FFF; uint256 xSignifier = uint128(x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; if (xExponent == 0x7FFF && xSignifier != 0) return NaN; else if (xExponent > 16397) return xNegative ? POSITIVE_ZERO : POSITIVE_INFINITY; else if (xExponent < 16255) return 0x3FFF0000000000000000000000000000; else { if (xExponent == 0) xExponent = 1; else xSignifier |= 0x10000000000000000000000000000; if (xExponent > 16367) xSignifier <<= xExponent - 16367; else if (xExponent < 16367) xSignifier >>= 16367 - xExponent; if ( xNegative && xSignifier > 0x406E00000000000000000000000000000000 ) return POSITIVE_ZERO; if ( !xNegative && xSignifier > 0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ) return POSITIVE_INFINITY; uint256 resultExponent = xSignifier >> 128; xSignifier &= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; if (xNegative && xSignifier != 0) { xSignifier = ~xSignifier; resultExponent += 1; } uint256 resultSignifier = 0x80000000000000000000000000000000; if (xSignifier & 0x80000000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x16A09E667F3BCC908B2FB1366EA957D3E) >> 128; if (xSignifier & 0x40000000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1306FE0A31B7152DE8D5A46305C85EDEC) >> 128; if (xSignifier & 0x20000000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1172B83C7D517ADCDF7C8C50EB14A791F) >> 128; if (xSignifier & 0x10000000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10B5586CF9890F6298B92B71842A98363) >> 128; if (xSignifier & 0x8000000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1059B0D31585743AE7C548EB68CA417FD) >> 128; if (xSignifier & 0x4000000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x102C9A3E778060EE6F7CACA4F7A29BDE8) >> 128; if (xSignifier & 0x2000000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10163DA9FB33356D84A66AE336DCDFA3F) >> 128; if (xSignifier & 0x1000000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100B1AFA5ABCBED6129AB13EC11DC9543) >> 128; if (xSignifier & 0x800000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10058C86DA1C09EA1FF19D294CF2F679B) >> 128; if (xSignifier & 0x400000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1002C605E2E8CEC506D21BFC89A23A00F) >> 128; if (xSignifier & 0x200000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100162F3904051FA128BCA9C55C31E5DF) >> 128; if (xSignifier & 0x100000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000B175EFFDC76BA38E31671CA939725) >> 128; if (xSignifier & 0x80000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100058BA01FB9F96D6CACD4B180917C3D) >> 128; if (xSignifier & 0x40000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10002C5CC37DA9491D0985C348C68E7B3) >> 128; if (xSignifier & 0x20000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000162E525EE054754457D5995292026) >> 128; if (xSignifier & 0x10000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000B17255775C040618BF4A4ADE83FC) >> 128; if (xSignifier & 0x8000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB) >> 128; if (xSignifier & 0x4000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9) >> 128; if (xSignifier & 0x2000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000162E43F4F831060E02D839A9D16D) >> 128; if (xSignifier & 0x1000000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000B1721BCFC99D9F890EA06911763) >> 128; if (xSignifier & 0x800000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000058B90CF1E6D97F9CA14DBCC1628) >> 128; if (xSignifier & 0x400000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000002C5C863B73F016468F6BAC5CA2B) >> 128; if (xSignifier & 0x200000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000162E430E5A18F6119E3C02282A5) >> 128; if (xSignifier & 0x100000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000B1721835514B86E6D96EFD1BFE) >> 128; if (xSignifier & 0x80000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000058B90C0B48C6BE5DF846C5B2EF) >> 128; if (xSignifier & 0x40000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000002C5C8601CC6B9E94213C72737A) >> 128; if (xSignifier & 0x20000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000162E42FFF037DF38AA2B219F06) >> 128; if (xSignifier & 0x10000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000B17217FBA9C739AA5819F44F9) >> 128; if (xSignifier & 0x8000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000058B90BFCDEE5ACD3C1CEDC823) >> 128; if (xSignifier & 0x4000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000002C5C85FE31F35A6A30DA1BE50) >> 128; if (xSignifier & 0x2000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000162E42FF0999CE3541B9FFFCF) >> 128; if (xSignifier & 0x1000000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000B17217F80F4EF5AADDA45554) >> 128; if (xSignifier & 0x800000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000058B90BFBF8479BD5A81B51AD) >> 128; if (xSignifier & 0x400000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000002C5C85FDF84BD62AE30A74CC) >> 128; if (xSignifier & 0x200000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000162E42FEFB2FED257559BDAA) >> 128; if (xSignifier & 0x100000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000B17217F7D5A7716BBA4A9AE) >> 128; if (xSignifier & 0x80000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000058B90BFBE9DDBAC5E109CCE) >> 128; if (xSignifier & 0x40000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000002C5C85FDF4B15DE6F17EB0D) >> 128; if (xSignifier & 0x20000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000162E42FEFA494F1478FDE05) >> 128; if (xSignifier & 0x10000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000B17217F7D20CF927C8E94C) >> 128; if (xSignifier & 0x8000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000058B90BFBE8F71CB4E4B33D) >> 128; if (xSignifier & 0x4000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000002C5C85FDF477B662B26945) >> 128; if (xSignifier & 0x2000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000162E42FEFA3AE53369388C) >> 128; if (xSignifier & 0x1000000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000B17217F7D1D351A389D40) >> 128; if (xSignifier & 0x800000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000058B90BFBE8E8B2D3D4EDE) >> 128; if (xSignifier & 0x400000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000002C5C85FDF4741BEA6E77E) >> 128; if (xSignifier & 0x200000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000162E42FEFA39FE95583C2) >> 128; if (xSignifier & 0x100000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000B17217F7D1CFB72B45E1) >> 128; if (xSignifier & 0x80000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000058B90BFBE8E7CC35C3F0) >> 128; if (xSignifier & 0x40000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000002C5C85FDF473E242EA38) >> 128; if (xSignifier & 0x20000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000162E42FEFA39F02B772C) >> 128; if (xSignifier & 0x10000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000B17217F7D1CF7D83C1A) >> 128; if (xSignifier & 0x8000000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000058B90BFBE8E7BDCBE2E) >> 128; if (xSignifier & 0x4000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000002C5C85FDF473DEA871F) >> 128; if (xSignifier & 0x2000000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000162E42FEFA39EF44D91) >> 128; if (xSignifier & 0x1000000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000B17217F7D1CF79E949) >> 128; if (xSignifier & 0x800000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000058B90BFBE8E7BCE544) >> 128; if (xSignifier & 0x400000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000002C5C85FDF473DE6ECA) >> 128; if (xSignifier & 0x200000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000162E42FEFA39EF366F) >> 128; if (xSignifier & 0x100000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000B17217F7D1CF79AFA) >> 128; if (xSignifier & 0x80000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000058B90BFBE8E7BCD6D) >> 128; if (xSignifier & 0x40000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000002C5C85FDF473DE6B2) >> 128; if (xSignifier & 0x20000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000162E42FEFA39EF358) >> 128; if (xSignifier & 0x10000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000B17217F7D1CF79AB) >> 128; if (xSignifier & 0x8000000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000058B90BFBE8E7BCD5) >> 128; if (xSignifier & 0x4000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000002C5C85FDF473DE6A) >> 128; if (xSignifier & 0x2000000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000162E42FEFA39EF34) >> 128; if (xSignifier & 0x1000000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000B17217F7D1CF799) >> 128; if (xSignifier & 0x800000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000058B90BFBE8E7BCC) >> 128; if (xSignifier & 0x400000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000002C5C85FDF473DE5) >> 128; if (xSignifier & 0x200000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000162E42FEFA39EF2) >> 128; if (xSignifier & 0x100000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000B17217F7D1CF78) >> 128; if (xSignifier & 0x80000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000058B90BFBE8E7BB) >> 128; if (xSignifier & 0x40000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000002C5C85FDF473DD) >> 128; if (xSignifier & 0x20000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000162E42FEFA39EE) >> 128; if (xSignifier & 0x10000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000B17217F7D1CF6) >> 128; if (xSignifier & 0x8000000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000058B90BFBE8E7A) >> 128; if (xSignifier & 0x4000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000002C5C85FDF473C) >> 128; if (xSignifier & 0x2000000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000162E42FEFA39D) >> 128; if (xSignifier & 0x1000000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000B17217F7D1CE) >> 128; if (xSignifier & 0x800000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000058B90BFBE8E6) >> 128; if (xSignifier & 0x400000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000002C5C85FDF472) >> 128; if (xSignifier & 0x200000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000162E42FEFA38) >> 128; if (xSignifier & 0x100000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000000B17217F7D1B) >> 128; if (xSignifier & 0x80000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000058B90BFBE8D) >> 128; if (xSignifier & 0x40000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000002C5C85FDF46) >> 128; if (xSignifier & 0x20000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000000162E42FEFA2) >> 128; if (xSignifier & 0x10000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000000B17217F7D0) >> 128; if (xSignifier & 0x8000000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000000058B90BFBE7) >> 128; if (xSignifier & 0x4000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000002C5C85FDF3) >> 128; if (xSignifier & 0x2000000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000000162E42FEF9) >> 128; if (xSignifier & 0x1000000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000000B17217F7C) >> 128; if (xSignifier & 0x800000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000000058B90BFBD) >> 128; if (xSignifier & 0x400000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000000002C5C85FDE) >> 128; if (xSignifier & 0x200000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000000162E42FEE) >> 128; if (xSignifier & 0x100000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000000000B17217F6) >> 128; if (xSignifier & 0x80000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000000058B90BFA) >> 128; if (xSignifier & 0x40000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000000002C5C85FC) >> 128; if (xSignifier & 0x20000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000000000162E42FD) >> 128; if (xSignifier & 0x10000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000000000B17217E) >> 128; if (xSignifier & 0x8000000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000000000058B90BE) >> 128; if (xSignifier & 0x4000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000000002C5C85E) >> 128; if (xSignifier & 0x2000000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000000000162E42E) >> 128; if (xSignifier & 0x1000000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000000000B17216) >> 128; if (xSignifier & 0x800000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000000000058B90A) >> 128; if (xSignifier & 0x400000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000000000002C5C84) >> 128; if (xSignifier & 0x200000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000000000162E41) >> 128; if (xSignifier & 0x100000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000000000000B1720) >> 128; if (xSignifier & 0x80000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000000000058B8F) >> 128; if (xSignifier & 0x40000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000000000002C5C7) >> 128; if (xSignifier & 0x20000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000000000000162E3) >> 128; if (xSignifier & 0x10000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000000000000B171) >> 128; if (xSignifier & 0x8000 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000000000000058B8) >> 128; if (xSignifier & 0x4000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000000000002C5B) >> 128; if (xSignifier & 0x2000 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000000000000162D) >> 128; if (xSignifier & 0x1000 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000000000000B16) >> 128; if (xSignifier & 0x800 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000000000000058A) >> 128; if (xSignifier & 0x400 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000000000000002C4) >> 128; if (xSignifier & 0x200 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000000000000161) >> 128; if (xSignifier & 0x100 > 0) resultSignifier = (resultSignifier * 0x1000000000000000000000000000000B0) >> 128; if (xSignifier & 0x80 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000000000000057) >> 128; if (xSignifier & 0x40 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000000000000002B) >> 128; if (xSignifier & 0x20 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000000000000015) >> 128; if (xSignifier & 0x10 > 0) resultSignifier = (resultSignifier * 0x10000000000000000000000000000000A) >> 128; if (xSignifier & 0x8 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000000000000004) >> 128; if (xSignifier & 0x4 > 0) resultSignifier = (resultSignifier * 0x100000000000000000000000000000001) >> 128; if (!xNegative) { resultSignifier = (resultSignifier >> 15) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; resultExponent += 0x3FFF; } else if (resultExponent <= 0x3FFE) { resultSignifier = (resultSignifier >> 15) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF; resultExponent = 0x3FFF - resultExponent; } else { resultSignifier = resultSignifier >> (resultExponent - 16367); resultExponent = 0; } return bytes16(uint128((resultExponent << 112) | resultSignifier)); } } } /** * Calculate e^x. * * @param x quadruple precision number * @return quadruple precision number */ function exp(bytes16 x) internal pure returns (bytes16) { unchecked { return pow_2(mul(x, 0x3FFF71547652B82FE1777D0FFDA0D23A)); } } /** * Get index of the most significant non-zero bit in binary representation of * x. Reverts if x is zero. * * @return index of the most significant non-zero bit in binary representation * of x */ function mostSignificantBit(uint256 x) internal pure returns (uint256) { unchecked { require(x > 0); uint256 result = 0; if (x >= 0x100000000000000000000000000000000) { x >>= 128; result += 128; } if (x >= 0x10000000000000000) { x >>= 64; result += 64; } if (x >= 0x100000000) { x >>= 32; result += 32; } if (x >= 0x10000) { x >>= 16; result += 16; } if (x >= 0x100) { x >>= 8; result += 8; } if (x >= 0x10) { x >>= 4; result += 4; } if (x >= 0x4) { x >>= 2; result += 2; } if (x >= 0x2) result += 1; // No need to shift x anymore return result; } } }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import "./ABDKMathQuad.sol";
library ABDKMathQuadUInt256 {
function mul(uint256 x, uint256 y) internal pure returns (uint256) {
return
ABDKMathQuad.toUInt(
ABDKMathQuad.mul(
ABDKMathQuad.fromUInt(x),
ABDKMathQuad.fromUInt(y)
)
) / 1e18;
}
function div(uint256 x, uint256 y) internal pure returns (uint256) {
return
ABDKMathQuad.toUInt(
ABDKMathQuad.div(
ABDKMathQuad.fromUInt(x * 1e18),
ABDKMathQuad.fromUInt(y)
)
);
}
}// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.22;
import "./ABDKMathQuadUInt256.sol";
import "./FairSideFormula2.sol";
import "../interfaces/token/IFair.sol";
// solhint-disable not-rely-on-time, var-name-mixedcase, reason-string /*
library EstimateEthInputLib {
using ABDKMathQuadUInt256 for uint256;
// estimate eth search iteration
uint256 private constant SEARCH_ITERATION = 200;
// estimate eth deviation
uint256 private constant ESTIMATION_DEVIATION = 0.00001 ether; // 0.001%
/**
* @dev : estimates the amount of eth to be received
*/
function estimateReceivedEth(
IFair fair,
uint256 capitalPool,
uint256 networkFShare,
uint256 fairAmount
) external view returns (uint256 fairInput, uint256 ethOut) {
ethOut = _estimateReceivedEth(capitalPool, networkFShare, fairAmount);
fairInput = fair.getTokensBurned(ethOut);
}
// @dev : estimates the amount of eth to be received, using binary search
// @param capitalPool : capital pool
// @param fShare : fshare
// @param fairAmount : fair amount
function _estimateReceivedEth(
uint256 capitalPool,
uint256 fShare,
uint256 fairAmount
) private pure returns (uint256) {
uint256 approximatePrice = FairSideFormula2.f(capitalPool, fShare);
uint256 ethOut = fairAmount.mul(approximatePrice);
// half decay to reduce search scope
uint256 decay = ethOut / 2;
uint256 fairInput = 0;
uint256 count = 0;
// max iteration to 200 due to gas limit
while (count < SEARCH_ITERATION) {
fairInput = FairSideFormula2.getMintedAmount(
capitalPool - ethOut,
ethOut,
fShare
);
// ESTIMATION_DEVIATION is due to the approximation of the curve, and it should be below the fairAmount
// to prevent overestimate
if (
fairAmount > fairInput &&
fairAmount - fairInput < fairAmount.mul(ESTIMATION_DEVIATION)
) {
return ethOut;
}
if (fairInput > fairAmount) {
ethOut -= decay;
} else if (fairInput < fairAmount) {
ethOut += decay;
}
decay /= 2;
count += 1;
}
return ethOut;
}
}// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.22;
import "./ABDKMathQuad.sol";
// solhint-disable not-rely-on-time, var-name-mixedcase, reason-string /*
library FairSideFormula2 {
using ABDKMathQuad for bytes16;
// A constant (adjusted before launch, in precalculated values below assumed to be 0.00015)
bytes16 public constant A = 0x3ff23a92a305532617c1bda5119ce075;
// C constant (adjusted before launch, in precalculated values below assumed to be 55,000,000)
bytes16 public constant C = 0x4018a39de00000000000000000000000;
// 0: in quadruple precision form
bytes16 public constant ZERO = 0x00000000000000000000000000000000;
// 1: in quadruple precision form
bytes16 public constant ONE = 0x3fff0000000000000000000000000000;
// 2: in quadruple precision form
bytes16 public constant TWO = 0x40000000000000000000000000000000;
// 3: in quadruple precision form
bytes16 public constant THREE = 0x40008000000000000000000000000000;
// 1e18
bytes16 private constant NORMALIZER = 0x403abc16d674ec800000000000000000;
function _pow3(bytes16 x) private pure returns (bytes16) {
return x.mul(x).mul(x);
}
function _f(bytes16 x, bytes16 fShare) private pure returns (bytes16) {
return A.add(_pow3(x).mul(x).div(_pow3(fShare).mul(C)));
}
// f represents the relation between capital and token price
function f(uint256 x, uint256 fShare) public pure returns (uint256) {
bytes16 _x = denormalize(x);
bytes16 _fShare = denormalize(fShare);
return normalize(_f(_x, _fShare));
}
// calculate the integral hypothetically assuming A is 0
function _gAdjusted(
bytes16 x,
bytes16 fShare
) private pure returns (bytes16) {
bytes16 fraction = fShare.div(x);
return C.neg().mul(fraction).mul(fraction).mul(fraction).div(THREE);
}
// calculate the average price hypothetically assuming A is 0
function _getAdjustedPrice(
bytes16 x,
bytes16 deposit,
bytes16 fShare
) private pure returns (bytes16) {
bytes16 initialSupply = _gAdjusted(x, fShare);
bytes16 finalSupply = _gAdjusted(x.add(deposit), fShare);
return deposit.div(finalSupply.sub(initialSupply));
}
// calculate the minted amount by dividing the deposit by the approximated price, i.e. adding A back to the adjusted price
function _getMintedAmount(
bytes16 x,
bytes16 deposit,
bytes16 fShare
) private pure returns (bytes16) {
bytes16 approximatedPrice = _getAdjustedPrice(x, deposit, fShare).add(
A
);
return deposit.div(approximatedPrice);
}
function getMintedAmount(
uint256 x,
uint256 deposit,
uint256 fShare
) public pure returns (uint256) {
return
normalize(
_getMintedAmount(
denormalize(x),
denormalize(deposit),
denormalize(fShare)
)
);
}
function normalize(bytes16 x) public pure returns (uint256) {
return x.mul(NORMALIZER).toUInt();
}
function denormalize(uint256 a) public pure returns (bytes16) {
return ABDKMathQuad.fromUInt(a).div(NORMALIZER);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
// solhint-disable not-rely-on-time, var-name-mixedcase, reason-string /*
abstract contract FSOwnable {
/* ========== STATE VARIABLES ========== */
address private _owner;
/* ========== EVENTS ========== */
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/* ========== ERRORS ========== */
error CallerNotOwner();
error NewOwnerAddressZero();
/* ========== CONSTRUCTOR ========== */
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/* ========== VIEWS ========== */
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/* ========== MUTATIVE FUNCTIONS ========== */
/**
* @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 NewOwnerAddressZero();
}
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
if (owner() != msg.sender) {
revert CallerNotOwner();
}
_;
}
}// SPDX-License-Identifier: Unlicense
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
pragma solidity ^0.8.22;
interface IERC20ConvictionScore is IERC20 {
function governanceConvictionThreshold() external view returns (uint256);
function getConvictionForDuration() external view returns (uint256);
function minimumBalance() external view returns (uint256);
function tokenizeConviction(
uint256 tokensToBeLocked,
uint256 convictionToBeLocked
) external returns (uint256);
function getGovernanceMinimumBalance() external view returns (uint256);
function getVestingAmount(address account) external view returns (uint256);
}
interface ConvictionScore {
function getConvictionScore(address user) external view returns (uint256);
function isGovernance(address member) external view returns (bool);
function getTotalAvailableConviction() external view returns (uint256);
}
interface ConvictionScorePoints {
function burn(address from, uint256 amount) external;
function mint(address from, uint256 amount) external;
function transfer(address from, address to, uint256 amount) external;
function syncConviction(address account) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
// getRoundData and latestRoundData should both raise "No data present"
// if they do not have data to report, instead of returning unset values
// which could be misinterpreted as actual reported values.
function getRoundData(
uint80 _roundId
)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface I1inchAggregatorV5 {
struct SwapDescription {
IERC20 srcToken;
IERC20 dstToken;
address payable srcReceiver;
address payable dstReceiver;
uint256 amount;
uint256 minReturnAmount;
uint256 flags;
bytes permit;
}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface ITributeAccrual is IERC20 {
function totalAvailableReward()
external
view
returns (uint256, uint256, uint256);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
import "@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol";
interface IV3SwapRouter is IUniswapV3SwapCallback {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance,
/// and swap the entire amount, enabling contracts to send tokens before calling this function.
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
/// @return amountOut The amount of the received token
function exactInputSingle(
ExactInputSingleParams calldata params
) external payable returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
/// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance,
/// and swap the entire amount, enabling contracts to send tokens before calling this function.
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
/// @return amountOut The amount of the received token
function exactInput(
ExactInputParams calldata params
) external payable returns (uint256 amountOut);
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another token
/// that may remain in the router after the swap.
/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
/// @return amountIn The amount of the input token
function exactOutputSingle(
ExactOutputSingleParams calldata params
) external payable returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 amountOut;
uint256 amountInMaximum;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// that may remain in the router after the swap.
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(
ExactOutputParams calldata params
) external payable returns (uint256 amountIn);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
interface IWETH {
function deposit() external payable;
function transfer(address to, uint value) external returns (bool);
function withdraw(uint) external;
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "./IFairSideSchema.sol";
interface IFairSideClaims is IFairSideSchema {
/**
* @dev {CostShareRequest} struct contains parameters representing a Cross Share Request (CSR).
* creation: CSR creation date.
* initiator: Creator of CSR.
* coverId: Cover ID associated with the claim.
* csrType: CSR type.
* availableBenefits: Available benefits of the membership locked during the claim, its also the maximum amount that can be paid.
* claimAmount: Claim amount of CSR in ETH.
* payoutAmount: payout amount of CSR in ETH set by Guardian.
* evidence: Evidence associated with CSR.
* originalClaimId: original claim ID associated with an appeal, if its bigger than 0 then its an appeal.
* fairBounty: Bounty associated with CSR (0.4 % of the member's available cross share benefits).
* status: Status of CSR.
*/
struct CostShareRequest {
uint80 creation;
address initiator;
uint256 coverId;
uint256 csrType;
uint256 availableBenefits;
uint256 claimAmount;
uint256 payoutAmount;
bytes32 evidence;
uint256 originalClaimId;
uint256 appealId;
ClaimStatus status;
}
/**
* @dev Actions to perform on Cost Share Request (CSR).
* ApproveClaim: Action for acceptance of CSR.
* DenyClaim: Action for rejection of CSR.
*/
enum Action {
APPROVE_CLAIM,
DENY_CLAIM
}
/**
* @dev Claim status on Cost Share Request (CSR).
* IN_PROGRESS: in progress
* Approved: approved
* DENIED: denied
* PAID: paid
* VERIFICATION_APPROVED: verification approved
* VERIFICATION_DENIED: verification denied
*/
enum ClaimStatus {
VOID,
IN_PROGRESS,
APPROVED,
DENIED,
PAID,
VERIFICATION_APPROVED,
VERIFICATION_DENIED
}
struct ClaimCheck {
uint256 claimId;
uint256 count;
}
struct ClaimStatistics {
uint256 opened;
uint256 claimed;
}
function totalOpenRequests() external view returns (uint256);
/* ========== EVENTS ========== */
// An event emitted when a CSR is submitted
event CreateCSR(
uint256 id,
address beneficiary,
uint256 availableBenefit,
uint256 payoutAmount,
uint256 csrType,
uint256 timestamp,
uint256 orignalClaimId
);
// An event emitted when a CSR is accepted
event ApproveCSR(
uint256 indexed id,
uint256 indexed csrType,
address assessor,
uint256 timestamp
);
// An event emitted when a CSR is rejected
event DenyCSR(
uint256 indexed id,
uint256 indexed csrType,
address assessor,
bytes reason,
uint256 timestamp
);
// An event emitted when a CSR is verified
event VerifyCSR(uint256 indexed id, bool appoved, uint256 timestamp);
//An event emitted when a new csrType is approved or disabled
event Event(uint256 indexed csrType, bool status);
//An event emitted when a new csrType is approved or disabled
event PaidCSR(
uint256 indexed claimId,
uint256 indexed amount,
address indexed payoutAddress
);
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "./IFairSideSchema.sol";
interface IFairSideNetwork is IFairSideSchema {
function PREMIUM_REWARD_ADDRESS() external view returns (address);
function FUNDING_POOL() external view returns (address);
function getCapitalPool() external view returns (uint256);
function getTotalOpenRequests() external view returns (uint256);
function getFairPrice() external view returns (uint256);
function getNetworkFShare() external view returns (uint256);
function hasPassedGracePeriod(uint256 coverId) external view returns (bool);
function getCoverCost(
uint256 membershipTypeId
) external view returns (uint256);
function estimateCost(
uint256 coverAmount,
uint256 expirationDate,
uint256 membershipTypeId
) external view returns (uint256);
function purchaseMembershipFromProxy(
address primaryAddress,
uint256 costShareBenefit,
address coverAddress,
uint256 membershipType
) external payable;
function getMembership(
uint256 coverId
) external view returns (Membership memory);
function increaseOrDecreaseCSB(
uint256 amount,
address account,
uint256 coverId,
bool increase
) external;
function blockMembership(uint256 coverId, bool blocked) external;
function decrementTotalPWPCSB(uint256 amount) external;
function removeExpiredMembershipCSB(uint256 coverId) external;
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
interface IFairSideSchema {
/**
* @dev CoverTypes in the Fairside Network ecosystem
* - Personal wallet protection
* - Defi Cover
* - Exchange cover
*/
struct Membership {
address wallet;
uint80 creation;
bool blocked;
address owner;
uint80 expirationDate;
uint256 availableCostShareBenefits;
uint256 paidCostShareBenefits;
uint256 membershipTypeId;
uint256 coverCost;
}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
interface IFair {
/**
* @dev Phases of the Fair token.
* Premine: Token pre-mine
* KOL: KOL token pre-mine
* VCWL: Venture Capital white-list
* CWL: Community white-list ( Deprecated)
* Final: Curve indefinitely open
*/
enum Phase {
Premine,
KOL,
VCWL,
Final
}
struct AccountUnbonding {
uint256 lastUnbondTimestamp;
uint256 amountUnbonded;
}
function currentPhase() external view returns (Phase);
function getTokensMinted(
uint256 investment
) external view returns (uint256);
function payClaim(address beneficiary, uint256 amount) external;
function bond(uint256 tokenMinimum) external payable returns (uint256);
function bondTo(
address to,
uint256 tokenMinimum
) external payable returns (uint256);
function bondAndBurn() external payable;
function bondNoEmission() external payable;
function mintPremineAdmin(
address[] calldata adminMultisigs,
uint256[] calldata amounts
) external;
function currentDailyUnbondableAmount() external view returns (uint256);
function capitalPoolAvailableFund() external view returns (uint256);
function getAvailableETHForWithdraw() external view returns (uint256);
function getTokensBurned(
uint256 investment
) external view returns (uint256);
function estimateReceivedEth(
uint256 fairAmount
) external view returns (uint256 fairInput, uint256 ethOut);
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
interface IFairVestingFactory {
function createVestingPRE(
address beneficiary,
uint256 amount
) external returns (address);
function createVestingVC(
address beneficiary,
uint256 amount
) external returns (address);
function createVestingKOL(
address beneficiary,
uint256 amount
) external returns (address);
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
interface IFairVesting {
function claimVestedTokens() external;
function createVesting(address account, uint256 _amount) external;
function increaseAccountVesting(address account, uint256 amount) external;
function decreaseAccountVesting(address account, uint256 amount) external;
function getCurrentVestingAmount(
address account
) external view returns (uint256);
function getVestedAmount(address account) external view returns (uint256);
function batchCreateVesting(
address[] calldata accounts,
uint256[] calldata _amounts
) external;
function getTotalVestedTokens() external view returns (uint256);
function getTotalClaimedTokens() external view returns (uint256);
function getUnclaimedTokens() external view returns (int256);
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
interface IFairVestingRegistry {
function getCurrentVesting(address account) external view returns (uint256);
function getTotalVestedAmount(
address account
) external view returns (uint256);
function getTotalUnclaimedVesting() external view returns (int256);
function getTotalVested() external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
interface IFairSidePurchaseProxy {
function purchaseMembershipFromProxy(
uint256 coverAmount,
uint256 membershipTypeId,
address primaryAddress,
address coverAddress
) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
interface AggregatorV3Interface {
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
contract DummyPriceOracle is AggregatorV3Interface {
int256 public value = 300000000000;
constructor() {
set(value);
}
function set(int256 _value) public {
value = _value;
}
function latestRoundData()
external
view
override
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
{
return (
110680464442257314889,
value,
1693886147,
1693886147,
110680464442257314889
);
}
}//SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
interface IWETH {
function deposit() external payable;
function withdraw(uint256) external;
function approve(address, uint256) external returns (bool);
function transfer(address, uint256) external returns (bool);
function transferFrom(address, address, uint256) external returns (bool);
function balanceOf(address) external view returns (uint256);
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract CharlesToken is ERC20, Ownable {
constructor() ERC20("CharlesToken", "MTK") {
_mint(msg.sender, 100_000_000 * 10 ** decimals());
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MockToken is ERC20, Ownable {
constructor(
string memory _symbol,
string memory _name,
uint8 _decimals
) ERC20(_symbol, _name) {}
function mint(address account, uint256 amount) public onlyOwner {
_mint(account, amount);
}
function burn(address account, uint256 amount) public onlyOwner {
_burn(account, amount);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import "../../network/FairSideClaims.sol";
contract FairSideClaimsV2 is FairSideClaims {
/**
* @notice shows the version of the contract being used
* @dev the value represents the curreent version of the contract should be updated and overriden with new implementations
* @return version -the current version of the contract
*/
function version() external pure override returns (string memory) {
return "1.1.0";
}
}// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.22;
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "../../dependencies/FairSideFormula2.sol";
import "../../dependencies/ABDKMathQuadUInt256.sol";
import "../../interfaces/network/IFairSideClaims.sol";
import "../../interfaces/network/IFairSideNetwork.sol";
import "../../interfaces/token/IFair.sol";
import "../../admin/IFairsideAdmin.sol";
interface IFairSideNetworkErrors {
/**
* @dev FairSideNetwork-related custom errors
*/
error FSNetwork_ActiveMembershipRequired();
error FSNetwork_ChainlinkPriceStale();
error FSNetwork_ChainlinkMalfunction();
error FSNetwork_ExceedsCSBLimitPerAccount();
error FSNetwork_InvalidCostShareBenefitSpecified();
error FSNetwork_MembershipTopupDisabled();
error FSNetwork_ExceedsCostShareBenefitLimitPerAccount();
error FSNetwork_MembershipNotExpired();
error FSNetwork_MembershipExpired();
error FSNetwork_PremiumFeeSentIsLessThanRequired();
error FSNetwork_NotEnoughFair();
error FSNetwork_InsufficientApproval();
error FSNetwork_InvalidCoverIdForAccount();
error FSNetwork_IncorrectValueSpecified();
error FSNetwork_CannotChange();
error FSNetwork_IncorrectSlippageSpecified();
error FSNetwork_InsufficientPrivileges();
error FSNetwork_CurveIsClosedUseETH();
error FSNetwork_OnlyPremiumPoolCanCall();
error FSNetwork_OnlyFairSideClaimsCanCall();
error FSNetwork_ExceedsMaxCostShareBenefitLimit();
error FSNetwork_IncorrectLossRatioSpecified();
error FSNetwork_OnlyMembershipPurchaseProxyCanCall();
error FSNetwork_FairPurchaseDisabled();
error FSNetwork_InsufficientPrivilegesOnlyGuardian();
error FSNetwork_InsufficientPrivilegesOnlyAdmin();
error FSNetwork_MembershipBlocked();
error FSNetwork_OnlyFairSideBountyPoolCanCall();
error FSNetwork_MembershipTypeDisabled();
}
/**
* @dev Implementation of {FairSideNetwork}.
*
* The FairSideNetwork contract allows purchasing of network membership using
* ETH/Fair. The Fair tokens collected in fees are distribute among the contract,
* staking tribute, governance tribute and funding pool in specific percentages.
*
* Allows opening, updating and processing of Cross Share Requests.
*
* Attributes:
* - Supports the full workflow of a cost share request
* - Handles Fair membership
* - Handles governance rewards
* - Retrieves ETH price via Chainlink
* - Calculates Fair price via Uniswap using Time-Weighted Price Averages (TWAP)
*/
// solhint-disable not-rely-on-time, var-name-mixedcase, reason-string /*
contract FairSideNetworkV2 is
IFairSideNetwork,
IFairSideNetworkErrors,
Initializable,
UUPSUpgradeable,
ReentrancyGuardUpgradeable
{
/* ========== LIBRARIES ========== */
using AddressUpgradeable for address payable;
using ABDKMathQuadUInt256 for uint256;
uint256 private constant MAXIMUM_GRACE_PERIOD = 100 * 365 days; // 100 years
struct MembershipType {
uint256 duration;
uint256 gracePeriod;
uint256 topupDisabledPeriod;
uint256 minimumPurchaseAmount;
uint256 maximumBenefitPerUser;
uint256 cost;
bool active;
}
/* ========== STATE VARIABLES ========== */
//mapping of id -> Memberships
mapping(uint256 => Membership) private membership;
//mapping of account (address) -> memberships
mapping(address => uint256[]) internal userMembership;
//mapping of addresses covered by an address / account
mapping(address => address[]) internal userCoveredAddress;
mapping(uint256 => MembershipType) public membershipTypes;
uint256 public numMembershipTypes;
// Tracking the number of cover ids
uint256 public membershipCount;
// Cost share benefits of the entire personal wallet protection cover in ETH
uint256 public totalPWPCSB;
// total cover cost received from users
uint256 public totalCoverCost;
//network gearing factor used for membership calculation
uint256 public networkGearingFactor;
// Risk based capital
uint256 public riskBasedCapital;
// estimated loss ratio
uint256 public lossRatio;
// Supported tokens for membership purchase, top up
enum TokenType {
ETH,
Fair
}
// Fair Token contract Address
IFair private fair;
//Fair Network
IFairSideClaims public fairSideClaims;
// Funding Pool Address
address public override FUNDING_POOL;
// Premiums Pool Address
address public PREMIUMS_POOL;
//patners pool address address
address public PARTNERS_ADDRESS;
// Premium Reward multisign Address
address public override PREMIUM_REWARD_ADDRESS;
// membership purchase proxy
address public membershipPurchaseProxy;
// Bountry pool contract address
address public FairSIDE_BOUNTY_POOL;
// Timelock Address, owned by Governance
address public TIMELOCK;
// 20% as staking rewards
uint256 public STAKING_REWARDS;
// 15% patner pool rewards
uint256 public PARTNERS_POOL_ALLOCATION;
//7.5% as funding pool
uint256 public FUNDING_POOL_ALLOCATION;
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
// enable or disable Fair purchase
bool public fairPurchaseEnabled;
// Admin contract
IFairsideAdmin private fairsideAdmin;
/* ========== EVENTS ========== */
// An event emitted when a membership is purchased (either new or an extension)
event NewMembership(
address indexed member,
uint256 coverId,
uint256 costshareBenefit,
address indexed coverAddress,
TokenType tokenType,
uint256 membershipTypeId
);
// An event emitted when a membership is topped up
event TopUpCover(
address indexed member,
uint256 coverId,
uint256 costshareBenefit,
TokenType tokenType
);
// An event for setting Fair Premium Reward contract address
event SetFairPremiumReward(address indexed premiumRewardContract);
// An event for setting ETH Premium Reward contract address
event SetETHPremiumReward(address indexed premiumRewardContract);
// An event for setting Network gearing factor
event SetNetworkGearingFactor(uint256 networkGearingFactor);
// An event for setting Risk based capital
event SetRiskBasedCapital(uint256 riskBasedCapital);
// An event for setting Loss ratio
event SetLossRatio(uint256 lossRatio);
event SetFeeDistributionPercenages(
uint256 fundingPoolAllocation,
uint256 partnersPoolAllocation,
uint256 stakingRewards,
uint256 lossRatio
);
// An event for sending Staking rewards on Fair
event PremiumFairDistributed(
uint256 fundingPremium,
uint256 parntersPremium,
uint256 stakingRewards,
uint256 curveReserve
);
// An event for sending Staking rewards on ETH
event PremiumEthDistributed(
uint256 fundingPremium,
uint256 parntersPremium,
uint256 stakingRewards,
uint256 curveReserve
);
// An event for setting the token gearing factor
event TokenGearingFactorSet(uint256 tokenGearingFactor);
// An event for setting the Fair purchase enabled
event SetFairPurchaseEnabled(bool fairPurchaseEnabled);
constructor() {
_disableInitializers();
}
/**
* @dev Initialises the contract's state setting fair, FUNDING_POOL
* and TIMELOCK addresses.
*/
function initialize(
IFair _fair,
address fundingPool,
address premiumsPool,
IFairsideAdmin _fairsideAdmin,
address timelock,
address patnersPool,
address premiumReward
) public initializer {
__UUPSUpgradeable_init();
__ReentrancyGuard_init();
fair = _fair;
FUNDING_POOL = fundingPool;
PREMIUMS_POOL = premiumsPool;
fairsideAdmin = _fairsideAdmin;
TIMELOCK = timelock;
PARTNERS_ADDRESS = patnersPool;
PREMIUM_REWARD_ADDRESS = premiumReward;
membershipPurchaseProxy = msg.sender;
fairPurchaseEnabled = false;
// 20% staking rewards
STAKING_REWARDS = 0.20 ether;
// 15% patner pool rewards
PARTNERS_POOL_ALLOCATION = 0.15 ether;
// 7.5% as funding pool
FUNDING_POOL_ALLOCATION = 0.075 ether;
riskBasedCapital = 2500 ether;
lossRatio = 0.575 ether; //57.5%
networkGearingFactor = 50;
_addMembershipType(
365 days,
60 days,
182 days,
1 ether,
100 ether,
0.0195 ether
);
}
function addMembershipType(
uint256 duration,
uint256 gracePeriod,
uint256 topupDisabledPeriod,
uint256 minimumPurchaseAmount,
uint256 maximumBenefitPerUser,
uint256 cost
) external onlyAdmin {
_addMembershipType(
duration,
gracePeriod,
topupDisabledPeriod,
minimumPurchaseAmount,
maximumBenefitPerUser,
cost
);
}
function _addMembershipType(
uint256 duration,
uint256 gracePeriod,
uint256 topupDisabledPeriod,
uint256 minimumPurchaseAmount,
uint256 maximumBenefitPerUser,
uint256 cost
) private {
// duration should be divisible by 1 day
if (
duration == 0 ||
(duration % 1 days != 0) ||
cost == 0 ||
topupDisabledPeriod > duration ||
gracePeriod > MAXIMUM_GRACE_PERIOD
) {
revert FSNetwork_IncorrectValueSpecified();
}
membershipTypes[numMembershipTypes] = MembershipType({
duration: duration,
gracePeriod: gracePeriod,
topupDisabledPeriod: topupDisabledPeriod,
minimumPurchaseAmount: minimumPurchaseAmount,
maximumBenefitPerUser: maximumBenefitPerUser,
cost: cost,
active: true
});
unchecked {
numMembershipTypes += 1;
}
}
function disableMembershipType(uint256 index) external onlyAdmin {
membershipTypes[index].active = false;
}
/**
* @dev Setting the fairside claims contract
*/
function setFairSideClaims(
IFairSideClaims _fairSideClaims
) external onlyAdmin {
fairSideClaims = _fairSideClaims;
}
/*
* @dev Setting the fairside bounty pool contract
*/
function setFairSideBountyPool(
address _fairSideBountyPool
) external onlyAdmin {
FairSIDE_BOUNTY_POOL = _fairSideBountyPool;
}
/**
* @dev returns the list of cover ids purchased by an address
*/
function getAccountMembership(
address account
) external view returns (uint256[] memory) {
return userMembership[account];
}
/**
* @dev returns the list of covered addresses covered by an address
*/
function getAccountCoveredWallets(
address account
) external view returns (address[] memory) {
return userCoveredAddress[account];
}
/**
* @notice returns Membership state containing
* (availableCostShareBenefits, duration, creation, owner account, covered wallet)
*/
function getMembership(
uint256 coverId
) external view override returns (Membership memory) {
return membership[coverId];
}
/**
* @dev Token price evaluated as spot price directly on curve
*/
function getFairPrice() public view override returns (uint256) {
uint256 fShare = getNetworkFShare();
uint256 capitalPool = getCapitalPool();
return FairSideFormula2.f(capitalPool, fShare);
}
/**
* @dev : checks if membership still valid using grace period
*/
function hasPassedGracePeriod(
uint256 coverId
) external view override returns (bool) {
Membership memory _membership = membership[coverId];
if (_membership.availableCostShareBenefits <= 0) {
revert FSNetwork_ActiveMembershipRequired();
}
return
block.timestamp >
_membership.expirationDate +
membershipTypes[_membership.membershipTypeId].gracePeriod;
}
/**
* @notice : returns the maximum CSB based on RSB and network gearing factor
*/
function getMaxTotalCostShareBenefits() public view returns (uint256) {
return riskBasedCapital * networkGearingFactor;
}
/**
* @notice get amount in capital pool (ETH)
* @dev : Capital Pool = Total Funds held in ETH – Open Cost Share Requests
* Open Cost Share Request = Cost share request awaiting assessor consensus
*/
function getCapitalPool() public view override returns (uint256) {
return address(fair).balance - getTotalOpenRequests();
}
/**
* @notice : returns cover cost of a cover
*/
function getCoverCost(
uint256 membershipTypeId
) external view override returns (uint256 coverCost) {
return membershipTypes[membershipTypeId].cost;
}
/**
* @notice Allows purchasing of membership of Fair Network with ETH and Fair
*
* @dev It accepts ETH to allocate the available cross share benefits
* for a member and also determines membership purchase cost.
*
* The membership is purchased using ETH, 57.5% of which remains in the curve,
* 20% is allocated with staking rewards, 7.5% is allocated for the {PREMIUMS_POOL}
* and 7.5% is sent to {FUNDING_POOL}.
*
*/
function purchaseMembership(
uint256 costShareBenefit,
address coverAddress,
uint256 membershipType
) external payable validateOpenCurve(TokenType.ETH) {
_purchaseMembership(
msg.sender,
costShareBenefit,
coverAddress,
membershipType,
TokenType.ETH
);
}
/*
* @notice : Allows purchasing of membership of Fair Network with ETH from a membershipPurchaseProxy
*/
function purchaseMembershipFromProxy(
address primaryAddress,
uint256 costShareBenefit,
address coverAddress,
uint256 membershipType
)
external
payable
validateOpenCurve(TokenType.ETH)
onlyMembershipPurchaseProxy
{
_purchaseMembership(
primaryAddress,
costShareBenefit,
coverAddress,
membershipType,
TokenType.ETH
);
}
/**
* @notice Allows purchasing of membership with Fair
*/
function purchaseMembershipWithFair(
uint256 costShareBenefit,
address coverAddress,
uint256 membershipType
) external onlyFairPurchaseEnabled validateOpenCurve(TokenType.Fair) {
_purchaseMembership(
msg.sender,
costShareBenefit,
coverAddress,
membershipType,
TokenType.Fair
);
}
/*
* @notice : Allows purchasing of membership of Fair Network with Fair from a membershipPurchaseProxy
*/
function purchaseMembershipWithFairFromProxy(
address primaryAddress,
uint256 costShareBenefit,
address coverAddress,
uint256 membershipType
)
external
onlyFairPurchaseEnabled
validateOpenCurve(TokenType.Fair)
onlyMembershipPurchaseProxy
{
_purchaseMembership(
primaryAddress,
costShareBenefit,
coverAddress,
membershipType,
TokenType.Fair
);
}
/**
* @notice : Top up a current exisiting purchased cover with ETH
* @dev : the prorated cost is charged { cost on time of purchase 360 days = full % of cover}
*/
function topupMembership(
uint256 coverId,
uint256 costshareBenefit
) external payable validateOpenCurve(TokenType.ETH) {
_topupMembership(coverId, costshareBenefit, TokenType.ETH);
}
/**
* @notice : Top up a current exisiting purchased cover with Fair
* @dev : the prorated cost is charged { cost on time of purchase 360 days = full % of cover}
*/
function topupMembershipWithFair(
uint256 coverId,
uint256 costshareBenefit
) external onlyFairPurchaseEnabled validateOpenCurve(TokenType.Fair) {
_topupMembership(coverId, costshareBenefit, TokenType.Fair);
}
/**
* @dev : handles premium membership purchase, for PWP applies the PWP gearing factor {PWPGearing} and PWPfshareRation for PWP cover purchase
* and uses the default fshare and fshareRatio for other covers
* the prorated cost is charged { cost on time of purchase 360 days = full % of cover}
*/
function _purchaseMembership(
address primaryAddress,
uint256 costShareBenefit,
address coverAddress,
uint256 membershipTypeId,
TokenType tokenType
) private nonReentrant {
validateCapitalPool(costShareBenefit, membershipTypeId);
MembershipType memory membershipType = membershipTypes[
membershipTypeId
];
if (!membershipType.active) {
revert FSNetwork_MembershipTypeDisabled();
}
if (costShareBenefit > _getMaximumBenefitPerUser(membershipTypeId)) {
revert FSNetwork_ExceedsCSBLimitPerAccount();
}
//calculate membership cost
uint256 coverCostETH = calculateCoverCost(
costShareBenefit,
0,
membershipType.duration,
membershipType.cost
);
distributePremium(coverCostETH, tokenType);
unchecked {
membershipCount += 1;
}
uint256 coverId = membershipCount;
Membership storage membershipId = membership[coverId];
//update storages
totalPWPCSB += costShareBenefit;
totalCoverCost += coverCostETH;
membershipId.availableCostShareBenefits = costShareBenefit;
membershipId.creation = uint80(block.timestamp);
membershipId.expirationDate = uint80(
block.timestamp + membershipType.duration
);
membershipId.owner = primaryAddress;
membershipId.wallet = coverAddress;
membershipId.membershipTypeId = membershipTypeId;
membershipId.coverCost += coverCostETH;
userMembership[primaryAddress].push(coverId);
userCoveredAddress[primaryAddress].push(coverAddress);
emit NewMembership(
primaryAddress,
coverId,
costShareBenefit,
coverAddress,
tokenType,
membershipTypeId
);
}
/**
* @dev : validate if the cost share benefit meets the required threshold
* and the capital pool has enough funds to cover the membership
*/
function validateCapitalPool(
uint256 costShareBenefit,
uint256 membershipTypeId
) private view {
//mimimun CSB
if (
costShareBenefit <
membershipTypes[membershipTypeId].minimumPurchaseAmount
) {
revert FSNetwork_InvalidCostShareBenefitSpecified();
}
if (totalPWPCSB + costShareBenefit > getMaxTotalCostShareBenefits()) {
revert FSNetwork_ExceedsMaxCostShareBenefitLimit();
}
}
/**
* @dev : handles premium membership purchase, for PWP applies the PWP gearing factor {PWPGearing} and PWPfshareRation for PWP cover purchase
* and uses the default fshare and fshareRatio for other covers
* the prorated cost is charged { cost on time of purchase 360 days = full % of cover}
*/
function _topupMembership(
uint256 coverId,
uint256 costShareBenefit,
TokenType tokenType
) private onlyNotBlocked(coverId) nonReentrant {
Membership storage membershipId = membership[coverId];
validateCapitalPool(costShareBenefit, membershipId.membershipTypeId);
MembershipType memory membershipType = membershipTypes[
membershipId.membershipTypeId
];
uint256 membershipExpirationDate = membershipId.expirationDate;
if (
block.timestamp >=
membershipExpirationDate - membershipType.topupDisabledPeriod
) {
revert FSNetwork_MembershipTopupDisabled();
}
uint256 userMembershipCSB = membershipId.availableCostShareBenefits +
costShareBenefit;
if (
userMembershipCSB >
_getMaximumBenefitPerUser(membershipId.membershipTypeId)
) {
revert FSNetwork_ExceedsCostShareBenefitLimitPerAccount();
}
uint256 coverCostETH = calculateCoverCost(
costShareBenefit,
membershipExpirationDate,
membershipType.duration,
membershipType.cost
);
distributePremium(coverCostETH, tokenType);
totalPWPCSB += costShareBenefit;
totalCoverCost += coverCostETH;
membershipId.availableCostShareBenefits = userMembershipCSB;
membershipId.coverCost += coverCostETH;
emit TopUpCover(
membershipId.wallet,
coverId,
costShareBenefit,
tokenType
);
}
/**
* @dev : checks if the cover cost is sufficient
* and distributes premiums to pools based on the token type provided
*/
function distributePremium(
uint256 membershipFeeETH,
TokenType tokenType
) internal {
if (tokenType == TokenType.ETH) {
if (msg.value < membershipFeeETH) {
revert FSNetwork_PremiumFeeSentIsLessThanRequired();
}
premiumDistributionETH(membershipFeeETH);
} else {
//convert to Fair
uint256 membershipFeeFair = membershipFeeETH.div(getFairPrice());
IERC20 _fairToken = IERC20(address(fair));
if (_fairToken.balanceOf(msg.sender) < membershipFeeFair) {
revert FSNetwork_NotEnoughFair();
}
//receive fair from user
_fairToken.transferFrom(
msg.sender,
address(this),
membershipFeeFair
);
//distribute premium
premiumDistributionFair(membershipFeeFair);
}
}
/*
* @dev : remove expired membership's csb from the network
*/
function removeExpiredMembershipCSB(
uint256 coverId
) external override onlyFairSideBountyPool {
Membership storage membershipId = membership[coverId];
if (membershipId.availableCostShareBenefits <= 0) {
revert FSNetwork_ActiveMembershipRequired();
}
MembershipType memory membershipType = membershipTypes[
membershipId.membershipTypeId
];
uint256 gracePeriod = membershipId.expirationDate +
membershipType.gracePeriod;
if (block.timestamp < gracePeriod) {
revert FSNetwork_MembershipNotExpired();
}
totalPWPCSB -= membershipId.availableCostShareBenefits;
totalCoverCost -= membershipId.coverCost;
membershipId.availableCostShareBenefits = 0;
membershipId.coverCost = 0;
}
/**
* @dev : estimates the cost of a cover with given amount
*/
function estimateCost(
uint256 costShareBenefit,
uint256 expirationDate,
uint256 membershipTypeId
) external view returns (uint256) {
MembershipType memory membershipType = membershipTypes[
membershipTypeId
];
return
calculateCoverCost(
costShareBenefit,
expirationDate,
membershipType.duration,
membershipType.cost
);
}
/**
* @dev changes the premiums pool address to a new address
*/
function setPremiumsPool(address _newPremiumsPool) external onlyAdmin {
PREMIUMS_POOL = _newPremiumsPool;
}
/*
* @dev changes the funding pool address to a new address
*/
function setFundingPool(
address payable _newFundingPool
) external onlyAdmin {
FUNDING_POOL = _newFundingPool;
}
/**
* @dev changes the partners pool address to a new address
*/
function setPartnersPool(address _newPartnersPool) external onlyAdmin {
PARTNERS_ADDRESS = _newPartnersPool;
}
/**
* @dev changes the premium reward address to a new address
*/
function setPremiumRewardAddress(
address _newPremiumRewardAddress
) external onlyAdmin {
PREMIUM_REWARD_ADDRESS = _newPremiumRewardAddress;
}
/**
* @dev changes the membership purchase proxy address to a new address
*/
function setMembershipPurchaseProxy(
address _newMembershipPurchaseProxy
) external onlyAdmin {
membershipPurchaseProxy = _newMembershipPurchaseProxy;
}
/**
* @notice This method handles the distribution of Fair to different pools
* @dev it's called after a membership is purchased, topped up by Fair
* Once the Fair is recieved smart staking calculation is done
* DISTRIBUTION IS HANDLED AS
* Staking Rewards 20% sent to rewardsContract
* Gov. 7.5% = sent to funding pool
* Partners Pool 15% sent to Premiums Pool
* 57.5% burnt
*/
function premiumDistributionFair(uint256 fairToDistribute) private {
if (IERC20(address(fair)).balanceOf(address(this)) < fairToDistribute) {
revert FSNetwork_NotEnoughFair();
}
//Calculate funding pool rewards
uint256 fundingPremium = fairToDistribute.mul(FUNDING_POOL_ALLOCATION);
uint256 parntersPremium = fairToDistribute.mul(
PARTNERS_POOL_ALLOCATION
);
uint256 stakingRewards = fairToDistribute.mul(STAKING_REWARDS);
uint256 curveReserve = fairToDistribute.mul(lossRatio);
ERC20Burnable _fairToken = ERC20Burnable(address(fair));
// 7,5% sent to governance
_fairToken.transfer(FUNDING_POOL, fundingPremium);
//patners pool 15%
_fairToken.transfer(PARTNERS_ADDRESS, parntersPremium);
// 20% staking rewards
_fairToken.transfer(PREMIUM_REWARD_ADDRESS, stakingRewards);
// burn remaining 57.5%
_fairToken.burn(curveReserve);
//register premium distribution in Fair
emit PremiumFairDistributed(
fundingPremium,
parntersPremium,
stakingRewards,
curveReserve
);
}
/**
* @notice This method handles the distribution of ETH to different pools
* @dev it's called after a membership is purchased, topped up by ETH
* Once the ETH is recieved smart staking calculation is done
* DISTRIBUTION IS HANDLED AS
* Staking Rewards 20% sent to rewardsContract
* Gov. 7.5% = sent to funding pool
* Partners Pool 15% sent to Premiums Pool
* 57.5% sent to curve with no emission of FS
*/
function premiumDistributionETH(uint256 ethToDistribute) private {
//Calculate funding pool rewards
uint256 fundingPremium = ethToDistribute.mul(FUNDING_POOL_ALLOCATION);
uint256 parntersPremium = ethToDistribute.mul(PARTNERS_POOL_ALLOCATION);
uint256 stakingRewards = ethToDistribute.mul(STAKING_REWARDS);
uint256 curveReserve = ethToDistribute.mul(lossRatio);
// 7,5% sent to governance
payable(FUNDING_POOL).sendValue(fundingPremium);
//patners pool 15%
payable(PARTNERS_ADDRESS).sendValue(parntersPremium);
//add staking reward in ETH to be distributed
payable(PREMIUM_REWARD_ADDRESS).sendValue(stakingRewards);
//send remaining 57.5% to the curve
fair.bondNoEmission{value: curveReserve}();
//register premium distribution in ETH
emit PremiumEthDistributed(
fundingPremium,
parntersPremium,
stakingRewards,
curveReserve
);
// send back excess ETH
if (msg.value > ethToDistribute) {
payable(msg.sender).sendValue(msg.value - ethToDistribute);
}
}
/* ========== RESTRICTED FUNCTIONS ========== */
/**
* @notice : increases or decreases the CSB when a claims requested is created.
* @dev Can only be called by the fairside claims contract
*/
function increaseOrDecreaseCSB(
uint256 amount,
address account,
uint256 coverId,
bool increase
) external override onlyFairSideClaims {
Membership storage membershipId = membership[coverId];
if (membershipId.owner != account) {
revert FSNetwork_InvalidCoverIdForAccount();
}
if (increase) {
membershipId.availableCostShareBenefits += amount;
} else {
membershipId.availableCostShareBenefits -= amount;
}
}
/**
* @notice : block cover when a claim is created, unblock when claim is resolved
* @dev Can only be called by the fairside claims contract
*/
function blockMembership(
uint256 coverId,
bool blocked
) external override onlyFairSideClaims {
Membership storage membershipId = membership[coverId];
membershipId.blocked = blocked;
}
/**
* @notice This method flushes out assets mistakenly sent to the contract
* @dev Can only be called by the premium pool multisig
*/
function flushAsset(
address tokenContractAddress,
address payable destination
) external payable onlyPremiumPool {
if (tokenContractAddress == address(0)) {
destination.sendValue(address(this).balance);
} else {
IERC20 tokenContract = ERC20(tokenContractAddress);
tokenContract.transfer(
destination,
tokenContract.balanceOf(address(this))
);
}
}
/**
* @dev Sets the gearing factor used for signing new memberships
*
* Requirements:
* - only callable by governance or timelock contracts.
*/
function setNetworkGearingFactor(
uint256 _gearingFactor
) external onlyTimelock {
if (_gearingFactor == 0) {
revert FSNetwork_IncorrectValueSpecified();
}
networkGearingFactor = _gearingFactor;
emit SetNetworkGearingFactor(_gearingFactor);
}
/**
* @dev Sets the risk based capital used for calculating the network FSHARE
*
* Requirements:
* - only callable by governance or timelock contracts.
*/
function setRiskBasedCapital(
uint256 _riskBasedCapital
) external onlyTimelock {
riskBasedCapital = _riskBasedCapital;
emit SetRiskBasedCapital(_riskBasedCapital);
}
/**
* @dev Sets the distribution percentages
*
* Requirements:
* - only callable by governance or timelock contracts.
*/
function setFeeDistributionPercentages(
uint256 _fundingPoolAllocation,
uint256 _partnersPoolAllocation,
uint256 _stakingRewards,
uint256 _lossRatio
) external onlyTimelock {
if (
_fundingPoolAllocation +
_partnersPoolAllocation +
_stakingRewards +
_lossRatio !=
1 ether
) {
revert FSNetwork_IncorrectValueSpecified();
}
FUNDING_POOL_ALLOCATION = _fundingPoolAllocation;
PARTNERS_POOL_ALLOCATION = _partnersPoolAllocation;
STAKING_REWARDS = _stakingRewards;
lossRatio = _lossRatio;
emit SetFeeDistributionPercenages(
_fundingPoolAllocation,
_partnersPoolAllocation,
_stakingRewards,
_lossRatio
);
}
/**
* @dev Set flag for enabling or disabling Fair purchase
*
* Requirements:
* - only callable by governance or timelock contracts.
*/
function setEnableFairPurchase(
bool _fairPurchaseEnabled
) external onlyTimelock {
fairPurchaseEnabled = _fairPurchaseEnabled;
emit SetFairPurchaseEnabled(_fairPurchaseEnabled);
}
/* ========== INTERNAL FUNCTIONS ========== */
/*
* @dev : decrement total cost share benefits after a claim is paid out
*/
function decrementTotalPWPCSB(
uint256 costShareBenefit
) external override onlyFairSideClaims {
totalPWPCSB -= costShareBenefit;
}
/**
* @dev Calculates the prorated cover cost of a given cover
* @return cover cost (uint)
*/
function calculateCoverCost(
uint256 costShareBenefit,
uint256 coverPeriod,
uint256 duration,
uint256 coverCost
) internal view returns (uint256) {
uint256 fee = costShareBenefit.mul(coverCost);
if (block.timestamp > coverPeriod) {
return fee;
} else {
uint256 dailycost = coverCost / (duration / 1 days);
uint256 coverDuration = coverPeriod - block.timestamp;
uint256 daysRemaining = coverDuration / 1 days;
// round up to the nearest day
if (coverDuration % 1 days != 0) {
daysRemaining += 1;
}
uint256 rate = daysRemaining * dailycost;
uint256 proratedCostETH = costShareBenefit.mul(rate);
return proratedCostETH;
}
}
function _authorizeUpgrade(
address newImplementation
) internal override onlyGuardian {}
/**
* @notice Return Open cost share requests in ETH
*/
function getTotalOpenRequests() public view returns (uint256) {
return fairSideClaims.totalOpenRequests();
}
function isOpenCurve() public view returns (bool) {
return fair.currentPhase() == IFair.Phase.Final;
}
/**
* @notice : calculates FSHARE risk based capital and loss ratio over
* the total cost share benefits
*/
function getNetworkFShare() public view override returns (uint256) {
return riskBasedCapital + lossRatio.mul(totalCoverCost);
}
/**
* @dev Returns maximum cross share benefit allowed per user.
*/
function _getMaximumBenefitPerUser(
uint256 membershipTypeId
) internal view returns (uint256) {
return membershipTypes[membershipTypeId].maximumBenefitPerUser;
}
/**
* @notice shows the version of the contract being used
* @dev the value represents the current version of the contract should be updated and overriden with new implementations
* @return version -the current version of the contract
*/
function version() external pure virtual returns (string memory) {
return "1.1.0";
}
/* ========== MODIFIERS ========== */
modifier onlyTimelock() {
if (msg.sender != TIMELOCK) {
revert FSNetwork_InsufficientPrivileges();
}
_;
}
modifier validateOpenCurve(TokenType tokenType) {
if (tokenType != TokenType.ETH) {
if (!isOpenCurve()) {
revert FSNetwork_CurveIsClosedUseETH();
}
}
_;
}
modifier onlyPremiumPool() {
if (msg.sender != PREMIUMS_POOL) {
revert FSNetwork_OnlyPremiumPoolCanCall();
}
_;
}
modifier onlyFairSideClaims() {
if (msg.sender != address(fairSideClaims)) {
revert FSNetwork_OnlyFairSideClaimsCanCall();
}
_;
}
modifier onlyNetworkOrClaims() {
if (
msg.sender != address(fairSideClaims) && msg.sender != address(this)
) revert FSNetwork_OnlyFairSideClaimsCanCall();
_;
}
modifier onlyFairSideBountyPool() {
if (msg.sender != FairSIDE_BOUNTY_POOL) {
revert FSNetwork_OnlyFairSideBountyPoolCanCall();
}
_;
}
modifier onlyMembershipPurchaseProxy() {
if (msg.sender != membershipPurchaseProxy) {
revert FSNetwork_OnlyMembershipPurchaseProxyCanCall();
}
_;
}
modifier onlyFairPurchaseEnabled() {
if (!fairPurchaseEnabled) {
revert FSNetwork_FairPurchaseDisabled();
}
_;
}
modifier onlyGuardian() {
if (
!fairsideAdmin.hasFSRole(
IFairsideAdmin.FSRoles.FS_NETWORK_GUARDIAN_ROLE,
msg.sender
)
) {
revert FSNetwork_InsufficientPrivilegesOnlyGuardian();
}
_;
}
modifier onlyAdmin() {
if (
!fairsideAdmin.hasFSRole(
IFairsideAdmin.FSRoles.FS_ADMIN_ROLE,
msg.sender
)
) {
revert FSNetwork_InsufficientPrivilegesOnlyAdmin();
}
_;
}
modifier onlyNotBlocked(uint256 coverId) {
if (membership[coverId].blocked) {
revert FSNetwork_MembershipBlocked();
}
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import "../../vesting/FairVestingRegistry.sol";
contract FairVestingRegistryV2 is FairVestingRegistry {
/**
* @notice shows the version of the contract being used
* @dev the value represents the curreent version of the contract should be updated and overriden with new implementations
* @return version -the current version of the contract
*/
function version() external pure override returns (string memory) {
return "1.1.0";
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "../interfaces/network/IFairSideNetwork.sol";
import "../interfaces/network/IFairSideClaims.sol";
import "../dependencies/ABDKMathQuadUInt256.sol";
import "../token/Fair.sol";
import "../admin/IFairsideAdmin.sol";
interface IFairSideBountyPoolErrors {
/**
* @dev FairSideBountyPool-related custom errors
*/
error FSBountyPool_InsufficientPrivilegesOnlyGuardian();
error FSBountyPool_InsufficientPrivilegesOnlyAdmin();
}
/**
* @dev Bounty pool contract to handle expired membership by network participants providing an incentive
*/
// solhint-disable not-rely-on-time, var-name-mixedcase, reason-string /*
contract FairSideBountyPool is
IFairSideBountyPoolErrors,
Initializable,
UUPSUpgradeable,
ReentrancyGuardUpgradeable,
PausableUpgradeable
{
/* ========== LIBRARIES ========== */
using AddressUpgradeable for address payable;
/* ========== STATE VARIABLES ========== */
uint256 bountyAmount;
// fairNetwork contract address
IFairSideNetwork private fairSideNetwork;
// Admin contract
IFairsideAdmin private fairsideAdmin;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
receive() external payable {}
/**
* @dev Set bounty amount
*/
function setBountyAmount(uint256 _bountyAmount) external onlyAdmin {
bountyAmount = _bountyAmount;
}
/**
* @dev Initialises the contract's upgrade state setting fair, FUNDING_POOL and GOVERNANCE_ADDRESS
*/
function initialize(
IFairSideNetwork _fairSideNetwork,
IFairsideAdmin _fairsideAdmin
) public initializer {
__UUPSUpgradeable_init();
__ReentrancyGuard_init();
__Pausable_init();
fairSideNetwork = _fairSideNetwork;
fairsideAdmin = _fairsideAdmin;
}
/**
* @notice ETH amount of total open requests in the network
*/
function removeExpiredMembershipCSB(
uint256 coverId
) external whenNotPaused nonReentrant {
fairSideNetwork.removeExpiredMembershipCSB(coverId);
if (bountyAmount > 0) {
payable(msg.sender).sendValue(bountyAmount);
}
}
function pause() external onlyAdmin {
_pause();
}
function unpause() external onlyAdmin {
_unpause();
}
function withdrawFund(uint256 amount) external onlyAdmin {
payable(msg.sender).sendValue(amount);
}
modifier onlyGuardian() {
if (
!fairsideAdmin.hasFSRole(
IFairsideAdmin.FSRoles.FS_BOUNTY_POOL_OWNER_ROLE,
msg.sender
)
) {
revert FSBountyPool_InsufficientPrivilegesOnlyGuardian();
}
_;
}
modifier onlyAdmin() {
if (
!fairsideAdmin.hasFSRole(
IFairsideAdmin.FSRoles.FS_ADMIN_ROLE,
msg.sender
)
) {
revert FSBountyPool_InsufficientPrivilegesOnlyAdmin();
}
_;
}
function _authorizeUpgrade(
address newImplementation
) internal override onlyGuardian {}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "../interfaces/network/IFairSideNetwork.sol";
import "../interfaces/network/IFairSideClaims.sol";
import "../interfaces/token/IFair.sol";
import "../dependencies/ABDKMathQuadUInt256.sol";
import "../admin/IFairsideAdmin.sol";
interface IFairSideClaimsErrors {
/**
* @dev FairSideClaims-related custom errors
*/
error FSClaims_CSRAlreadyProcessed();
error FSClaims_ActiveMembershipRequired();
error FSClaims_ClaimNotApproved();
error FSClaims_FairNotEnoughForBounty();
error FSClaims_ETHNotEnoughForBounty();
error FSClaims_IneligibleForCostShareRequest();
error FSClaims_GracePeriodPassed();
error FSClaims_CostRequestExceedsAvailableCostShareBenefits();
error FSClaims_InvalidMembershipOwner();
error FSClaims_InsufficientPrivilegesOnlyGuardian();
error FSClaims_EthRequired();
error FSClaims_InsufficientPrivilegesOnlyAdmin();
error FSClaims_ClaimAlreadyProcessed();
error FairAssessmentFeePaymentDisabled();
error FSClaims_InsufficientPrivilegesOnlyClaimIssuer();
error FSClaims_CSRAppealAlreadyCreated();
error FSClaims_CSRStatesNotFinal();
error FSClaims_CoverIdMismatch();
error FSClaims_PayoutAmountExceedsAvailableCostShareBenefits();
error FSClaims_CostshareBenefitsNotAvailable();
error FSClaims_AppealToAppealNotAllowed();
error FSClaims_CSRVerdictAlreadySeeded();
error FSClaims_InsufficientPrivilegesOnlyClaimVerifier();
error FSClaims_CSRNotCreated();
error FSClaims_CSRNotVerified();
}
/**
* @dev Handles everything relating to claims / costshare Requests
*/
// solhint-disable not-rely-on-time, var-name-mixedcase, reason-string /*
contract FairSideClaims is
IFairSideClaims,
IFairSideClaimsErrors,
Initializable,
UUPSUpgradeable,
ReentrancyGuardUpgradeable
{
/* ========== LIBRARIES ========== */
using AddressUpgradeable for address payable;
using ABDKMathQuadUInt256 for uint256;
/* ========== STATE VARIABLES ========== */
// Cost Share Requests mapps claimId -> request
mapping(uint256 => CostShareRequest) public costShareRequests;
// member address - open cost share benefits opened by the account / claimed by the account
mapping(address => ClaimStatistics) public openCostShareBenefits;
// Data entry proposed by the DAO
mapping(uint256 => bool) public approvedCsrTypes;
// claimID -> reasons by assesor
mapping(uint256 => bytes) public claimsVerdict;
// all cost share request created by a user
mapping(address => uint256[]) internal accountCSRs;
// csrType -> count ( can only open with approvedCsrTypes max of twice)
mapping(uint256 => mapping(address => ClaimCheck)) internal requestCount;
CostShareRequest[] internal allCSRs;
uint256[] internal totalPWPRequests;
// Cost Share Request IDs ( claim Id)
uint256 public claimID;
// Cost share benefits of the entire personal wallet protection cover in ETH
uint256 public openPWPRequestAmount;
// % cost of assement when opening CSR
uint256 public assesmentCostPercentage;
// assesment wait time;
uint256 public assesmentWaitTime;
// is Fair assesment fee enabled
bool public isFairAssesmentFeeEnabled;
// Fair Token contract Address
IFair private fair;
// fairNetwork contract address
IFairSideNetwork private fairSideNetwork;
address public CLAIMS_ISSUER;
IFairsideAdmin private fairsideAdmin;
// Structure that stores claim votes info
struct ClaimPoll {
uint256 claimId;
uint256 forVotes;
uint256 againstVotes;
}
// Claim poll data associated with claim ID
mapping(uint256 => ClaimPoll) public claimPolls;
event ClaimVoteCast(
uint256 claimId,
uint256 forVotes,
uint256 againstVotes
);
event ETHAssessmentFeePaid(
address indexed user,
address to,
uint256 bounty
);
event FairAssessmentFeePaid(
address indexed user,
address to,
uint256 bounty
);
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/**
* @dev Initialises the contract's upgrade state setting fair, FUNDING_POOL and GOVERNANCE_ADDRESS
*/
function initialize(
IFair _fair,
IFairSideNetwork _fairSideNetwork,
IFairsideAdmin _fairsideAdmin,
address claimPayer
) public initializer {
__UUPSUpgradeable_init();
__ReentrancyGuard_init();
fair = _fair;
fairSideNetwork = _fairSideNetwork;
CLAIMS_ISSUER = claimPayer;
assesmentCostPercentage = 0.1e18; // 10%
assesmentWaitTime = 10 hours;
fairsideAdmin = _fairsideAdmin;
}
/**
* @notice ETH amount of total open requests in the network
*/
function totalOpenRequests() external view override returns (uint256) {
return openPWPRequestAmount;
}
/**
* @notice Returns count of all CSRs
*/
function getAllCSRCount() external view returns (uint256) {
return allCSRs.length;
}
/**
* @notice Returns all costshare request by index
*/
function getCostshareByIndex(
uint256 index
) external view returns (CostShareRequest memory) {
return allCSRs[index];
}
/**
* @notice Returns all Claims with claimId performed by a user
*/
function getAllClaimsByAddress(
address _member
) external view returns (uint256[] memory) {
return accountCSRs[_member];
}
/**
* @dev . Returns ClaimIds awaiting payment from multisig
* @return uint256[] .
*/
function getClaims() external view returns (uint256[] memory) {
return totalPWPRequests;
}
/**
* @dev Allows the Governance Guardian to move the PWP votes and reasons on chain
* only works for PWP requests
* @param _claimId claim id
* @param payoutAmount final payment amount
* @param action action to perform, approve or deny
* @param _csrTypeCore core type of the claim
* @param reason reason for the rejectionapproval/approval/
*/
function seedPWPVerdict(
uint256 _claimId,
uint256 payoutAmount,
Action action,
uint256 _csrTypeCore,
bytes calldata reason
) external onlyGuardian {
CostShareRequest storage csr = costShareRequests[_claimId];
if (csr.status != ClaimStatus.VERIFICATION_APPROVED) {
revert FSClaims_CSRNotVerified();
}
if (action == Action.DENY_CLAIM) {
csr.status = ClaimStatus.DENIED;
csr.csrType = _csrTypeCore;
setVerdict(_claimId, reason);
decrementCounts(csr.availableBenefits);
blockMembership(csr.coverId, false);
openCostShareBenefits[csr.initiator].opened -= csr
.availableBenefits;
setAvailableBenefits(
csr.availableBenefits,
csr.initiator,
csr.coverId,
true
);
emit DenyCSR(
_claimId,
csr.csrType,
msg.sender,
reason,
block.timestamp
);
} else {
if (payoutAmount > csr.availableBenefits) {
revert FSClaims_PayoutAmountExceedsAvailableCostShareBenefits();
}
csr.status = ClaimStatus.APPROVED;
csr.csrType = _csrTypeCore;
csr.payoutAmount = payoutAmount;
setVerdict(_claimId, reason);
emit ApproveCSR(_claimId, csr.csrType, msg.sender, block.timestamp);
}
}
/**
* @dev Allows opening of PWP Cross Share Request (CSR) by a member.
*
* It accepts parameter {claimAmount} representing claim amount
*
* Updates the {totalOpenRequests} (if the payout is in ETH) and {openCostShareBenefits} of the member.
* block membership for topup
*
* Determines {bounty} as 10% of the user's available cross share benefits, of which
* is sent to the funding pool.
* if referenceClaimId is specified, the claim is then a appeal.
*/
function openPWPRequest(
uint256 claimAmount,
uint256 coverId,
bool inETH,
uint256 referenceClaimId
)
external
payable
onlyFairAssessmentFeeEnabled(inETH)
onlyCoverIdOwner(coverId)
nonReentrant
{
Membership memory membership = fairSideNetwork.getMembership(coverId);
if (membership.availableCostShareBenefits <= 0) {
revert FSClaims_ActiveMembershipRequired();
}
uint256 cost = fairSideNetwork.getCoverCost(
membership.membershipTypeId
);
uint256 availableCostShareBenefits = validateCSR(coverId);
if (referenceClaimId == 0) {
// if its a new claim we charge the assesment fee
if (isOpenCurve()) {
if (inETH) {
chargeAssesmentETH(availableCostShareBenefits, cost);
} else {
chargeAssesment(availableCostShareBenefits, cost);
}
} else {
if (!inETH) {
revert FSClaims_EthRequired();
}
// closed bonding
chargeAssesmentETH(availableCostShareBenefits, cost);
}
}
createCostshareRequest(
availableCostShareBenefits,
claimAmount,
0,
coverId,
referenceClaimId
);
}
/**
* @dev Processes a CSR associated with parameter {id}.
*
* It performs the payout to user in ETH if CSR is approved.
* unblock membership for topup if the claim is denied
*
* Updates the available and open cross share benefits of the user.
*/
function processCostShareRequest(
uint256 _claimId,
address payoutAddress
) external onlyClaimIssuer {
CostShareRequest storage csr = costShareRequests[_claimId];
if (csr.status != ClaimStatus.APPROVED) {
revert FSClaims_ClaimNotApproved();
}
csr.status = ClaimStatus.PAID;
// update user CSR
openCostShareBenefits[csr.initiator].opened -= csr.availableBenefits;
openCostShareBenefits[csr.initiator].claimed += csr.payoutAmount;
decrementCounts(csr.availableBenefits);
// if its approved, the difference between the payout amount and the available benefits is added to the available benefits
setAvailableBenefits(
csr.availableBenefits - csr.payoutAmount,
csr.initiator,
csr.coverId,
true
);
blockMembership(csr.coverId, false);
//pay claims
fair.payClaim(payoutAddress, csr.payoutAmount);
// reduce the total csb from the network
decrementTotalPWPCSB(csr.payoutAmount);
emit PaidCSR(_claimId, csr.payoutAmount, payoutAddress);
}
/**
* @dev creates the costsharerequest for all covers emits {CreatCSR} event
*/
function createCostshareRequest(
uint256 availableBenefits,
uint256 claimAmount,
uint256 _csrType,
uint256 coverId,
uint256 referenceClaimId
) private {
unchecked {
claimID += 1;
}
uint256 nextClaimId = claimID;
// ClaimStatus status = ClaimStatus.IN_PROGRESS;
CostShareRequest memory csr = CostShareRequest(
uint80(block.timestamp),
msg.sender,
coverId,
_csrType,
availableBenefits,
claimAmount,
0,
bytes32(0),
referenceClaimId,
0,
ClaimStatus.IN_PROGRESS
);
// if its an appeal we set the appeal id to the original claim
if (referenceClaimId != 0) {
// validation for appeal creation
CostShareRequest storage originalCsr = costShareRequests[
referenceClaimId
];
if (originalCsr.appealId > 0) {
revert FSClaims_CSRAppealAlreadyCreated();
}
if (
originalCsr.status != ClaimStatus.VERIFICATION_DENIED &&
originalCsr.status != ClaimStatus.DENIED &&
originalCsr.status != ClaimStatus.PAID
) {
revert FSClaims_CSRStatesNotFinal();
}
if (originalCsr.coverId != coverId) {
revert FSClaims_CoverIdMismatch();
}
if (originalCsr.originalClaimId > 0) {
revert FSClaims_AppealToAppealNotAllowed();
}
originalCsr.appealId = nextClaimId;
}
costShareRequests[nextClaimId] = csr;
allCSRs.push(csr);
accountCSRs[msg.sender].push(nextClaimId);
totalPWPRequests.push(nextClaimId);
requestCount[_csrType][msg.sender].count += 1;
requestCount[_csrType][msg.sender].claimId = nextClaimId;
openCostShareBenefits[msg.sender].opened += availableBenefits;
setAvailableBenefits(availableBenefits, msg.sender, coverId, false);
blockMembership(coverId, true);
// set appeal id to original claim
emit CreateCSR(
nextClaimId,
msg.sender,
availableBenefits,
claimAmount,
_csrType,
block.timestamp,
referenceClaimId
);
}
function approveCostShareRequest(
uint256 claimId,
bool approved
) external onlyClaimVerifier {
CostShareRequest storage csr = costShareRequests[claimId];
if (csr.status != ClaimStatus.IN_PROGRESS) {
revert FSClaims_CSRNotCreated();
}
if (approved) {
csr.status = ClaimStatus.VERIFICATION_APPROVED;
incrementCounts(csr.availableBenefits);
} else {
csr.status = ClaimStatus.VERIFICATION_DENIED;
blockMembership(csr.coverId, false);
openCostShareBenefits[csr.initiator].opened -= csr
.availableBenefits;
setAvailableBenefits(
csr.availableBenefits,
csr.initiator,
csr.coverId,
true
);
}
emit VerifyCSR(claimId, approved, block.timestamp);
}
/**
* @dev : handles charging the assesment cost for opening a request {10% of cover cost}
*/
function chargeAssesment(uint256 availableCSB, uint256 cost) internal {
uint256 fairSpotPrice = fairSideNetwork.getFairPrice();
// We want 10% of cover cost fee as bounty
uint256 bounty = availableCSB
.mul(cost.mul(assesmentCostPercentage))
.div(fairSpotPrice);
IERC20 _fairToken = IERC20(address(fair));
if (_fairToken.allowance(msg.sender, address(this)) <= bounty) {
revert FSClaims_FairNotEnoughForBounty();
}
address fundingPoolAddress = fairSideNetwork.FUNDING_POOL();
// 100% sent to Fair to funding pool
_fairToken.transferFrom(msg.sender, fundingPoolAddress, bounty);
emit FairAssessmentFeePaid(msg.sender, fundingPoolAddress, bounty);
}
/**
* @dev Allows an admin to cast a vote for a specific claim.
* @param claimId The ID of the claim.
* @param totalForVotes The total number of votes in favor of the claim.
* @param totalAgainstVotes The total number of votes against the claim.
*/
function castClaimVote(
uint256 claimId,
uint256 totalForVotes,
uint256 totalAgainstVotes
) external onlyAdmin {
CostShareRequest storage claim = costShareRequests[claimId];
ClaimPoll storage claimPoll = claimPolls[claimId];
if (
claim.status == ClaimStatus.PAID ||
claim.status == ClaimStatus.VERIFICATION_DENIED ||
claim.status == ClaimStatus.DENIED
) {
revert FSClaims_ClaimAlreadyProcessed();
}
claimPoll.claimId = claimId;
claimPoll.forVotes = totalForVotes;
claimPoll.againstVotes = totalAgainstVotes;
emit ClaimVoteCast(claimId, totalForVotes, totalAgainstVotes);
}
/**
* @dev : handles charging the assesment cost for opening a request {10% of cover cost} in ETH
*/
function chargeAssesmentETH(uint256 availableCSB, uint256 cost) internal {
//10% of cover cost as bounty fees
uint256 bounty = availableCSB.mul(cost.mul(assesmentCostPercentage));
if (msg.value < bounty) {
revert FSClaims_ETHNotEnoughForBounty();
}
//sending 100% fee to funding pool
address fundingPoolAddress = fairSideNetwork.FUNDING_POOL();
payable(fundingPoolAddress).sendValue(bounty);
if (msg.value > bounty) {
payable(msg.sender).sendValue(msg.value - bounty);
}
emit ETHAssessmentFeePaid(msg.sender, fundingPoolAddress, bounty);
}
/**
* @notice calculate the bounty cost in Fair or ETH
* @param availableCSB available costshare benefits
* @param inETH if the bounty is in ETH
* @return bounty cost of assesment bounty
*/
function calculateBountyCost(
uint256 availableCSB,
bool inETH,
uint256 membershipTypeId
) external view returns (uint256 bounty) {
uint256 cost = fairSideNetwork.getCoverCost(membershipTypeId);
if (inETH) {
bounty = availableCSB.mul(cost.mul(assesmentCostPercentage));
} else {
uint256 fairSpotPrice = fairSideNetwork.getFairPrice();
bounty = availableCSB.mul(cost.mul(assesmentCostPercentage)).div(
fairSpotPrice
);
}
}
function isOpenCurve() internal view returns (bool curveState) {
curveState = fair.currentPhase() == IFair.Phase.Final;
}
/**
* @notice : increment counts on the data for CSB
*/
function incrementCounts(uint256 avaialbleBenefits) internal {
openPWPRequestAmount += avaialbleBenefits;
}
/**
* @notice : decrements counts on the data for CSB
*/
function decrementCounts(uint256 avaialbleBenefits) internal {
openPWPRequestAmount -= avaialbleBenefits;
}
/*
* @dev : decrements the total costshare benefits in the network
*/
function decrementTotalPWPCSB(uint256 avaialbleBenefits) internal {
fairSideNetwork.decrementTotalPWPCSB(avaialbleBenefits);
}
/**
* @dev Validates the costshareRequest reverts if conditions not met
* - if cover is created too soon to be claimed
* - if the grace period has passed
* - if the account has no available costshare benefits
*/
function validateCSR(uint256 coverId) private view returns (uint256) {
Membership memory account = fairSideNetwork.getMembership(coverId);
if ((account.creation + assesmentWaitTime) > block.timestamp) {
revert FSClaims_IneligibleForCostShareRequest();
}
if (fairSideNetwork.hasPassedGracePeriod(coverId)) {
revert FSClaims_GracePeriodPassed();
}
if (account.availableCostShareBenefits == 0) {
revert FSClaims_CostshareBenefitsNotAvailable();
}
return account.availableCostShareBenefits;
}
/**
* @dev : updates the membership available CSB in network contract
*/
function setAvailableBenefits(
uint256 availableBenefits,
address account,
uint256 coverId,
bool increase
) internal {
if (increase) {
fairSideNetwork.increaseOrDecreaseCSB(
availableBenefits,
account,
coverId,
true
);
} else {
fairSideNetwork.increaseOrDecreaseCSB(
availableBenefits,
account,
coverId,
false
);
}
}
/**
* @dev block membership from topup if a claim is in progress
*/
function blockMembership(uint256 coverId, bool blocked) internal {
fairSideNetwork.blockMembership(coverId, blocked);
}
/**
* @notice Sets cost share data entry.
* @dev - only callable by governance.
*/
function setCsrTypes(
uint256 _csrType,
bool _isApproved
) external onlyGuardian {
approvedCsrTypes[_csrType] = _isApproved;
emit Event(_csrType, _isApproved);
}
/**
* @notice shows the version of the contract being used
* @dev the value represents the current version of the contract should be updated and overriden with new implementations
* @return version the current version of the contract
*/
function version() external pure virtual returns (string memory) {
return "1.0.0";
}
function setClaimsIssuer(address newIssuer) external onlyGuardian {
CLAIMS_ISSUER = newIssuer;
}
/**
* @dev Changes the cost of assement { default 10%}
*/
function setAssesmentCostPercentage(uint256 newCost) external onlyAdmin {
assesmentCostPercentage = newCost;
}
/**
* @dev assesment wait time in {hours}
*/
function setAssesmentWaitTime(uint256 durationInHour) external onlyAdmin {
assesmentWaitTime = durationInHour * 60 * 60;
}
/**
* @dev sets the verdict { decision } of a claim with reason
*/
function setVerdict(uint256 _claimId, bytes calldata reason) internal {
claimsVerdict[_claimId] = reason;
}
/**
* @dev : sets the Fair assesment fee enabled status
*/
function setFairAssesmentFeeEnabled(bool status) external onlyAdmin {
isFairAssesmentFeeEnabled = status;
}
/**
* @dev Handles upgrading of contract
*/
function _authorizeUpgrade(
address newImplementation
) internal override onlyGuardian {}
/* ========== MODIFIERS ========== */
modifier onlyCoverIdOwner(uint256 coverId) {
if (fairSideNetwork.getMembership(coverId).owner != msg.sender) {
revert FSClaims_InvalidMembershipOwner();
}
_;
}
modifier onlyGuardian() {
if (
!fairsideAdmin.hasFSRole(
IFairsideAdmin.FSRoles.FS_CLAIMS_GUARDIAN_ROLE,
msg.sender
)
) {
revert FSClaims_InsufficientPrivilegesOnlyGuardian();
}
_;
}
modifier onlyAdmin() {
if (
!fairsideAdmin.hasFSRole(
IFairsideAdmin.FSRoles.FS_ADMIN_ROLE,
msg.sender
)
) {
revert FSClaims_InsufficientPrivilegesOnlyAdmin();
}
_;
}
modifier onlyClaimIssuer() {
if (
!fairsideAdmin.hasFSRole(
IFairsideAdmin.FSRoles.FS_CLAIMS_ISSUER,
msg.sender
)
) {
revert FSClaims_InsufficientPrivilegesOnlyClaimIssuer();
}
_;
}
modifier onlyClaimVerifier() {
if (
!fairsideAdmin.hasFSRole(
IFairsideAdmin.FSRoles.FS_CLAIMS_VERIFIER,
msg.sender
)
) {
revert FSClaims_InsufficientPrivilegesOnlyClaimVerifier();
}
_;
}
modifier onlyFairAssessmentFeeEnabled(bool inETH) {
if (!inETH && !isFairAssesmentFeeEnabled) {
revert FairAssessmentFeePaymentDisabled();
}
_;
}
}// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.22;
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "../dependencies/FairSideFormula2.sol";
import "../dependencies/ABDKMathQuadUInt256.sol";
import "../interfaces/network/IFairSideClaims.sol";
import "../interfaces/network/IFairSideNetwork.sol";
import "../interfaces/token/IFair.sol";
import "../admin/IFairsideAdmin.sol";
interface IFairSideNetworkErrors {
/**
* @dev FairSideNetwork-related custom errors
*/
error FSNetwork_ActiveMembershipRequired();
error FSNetwork_ChainlinkPriceStale();
error FSNetwork_ChainlinkMalfunction();
error FSNetwork_ExceedsCSBLimitPerAccount();
error FSNetwork_InvalidCostShareBenefitSpecified();
error FSNetwork_MembershipTopupDisabled();
error FSNetwork_ExceedsCostShareBenefitLimitPerAccount();
error FSNetwork_MembershipNotExpired();
error FSNetwork_MembershipExpired();
error FSNetwork_PremiumFeeSentIsLessThanRequired();
error FSNetwork_NotEnoughFair();
error FSNetwork_InsufficientApproval();
error FSNetwork_InvalidCoverIdForAccount();
error FSNetwork_IncorrectValueSpecified();
error FSNetwork_CannotChange();
error FSNetwork_IncorrectSlippageSpecified();
error FSNetwork_InsufficientPrivileges();
error FSNetwork_CurveIsClosedUseETH();
error FSNetwork_OnlyPremiumPoolCanCall();
error FSNetwork_OnlyFairSideClaimsCanCall();
error FSNetwork_ExceedsMaxCostShareBenefitLimit();
error FSNetwork_IncorrectLossRatioSpecified();
error FSNetwork_OnlyMembershipPurchaseProxyCanCall();
error FSNetwork_FairPurchaseDisabled();
error FSNetwork_InsufficientPrivilegesOnlyGuardian();
error FSNetwork_InsufficientPrivilegesOnlyAdmin();
error FSNetwork_MembershipBlocked();
error FSNetwork_OnlyFairSideBountyPoolCanCall();
error FSNetwork_MembershipTypeDisabled();
}
/**
* @dev Implementation of {FairSideNetwork}.
*
* The FairSideNetwork contract allows purchasing of network membership using
* ETH/Fair. The Fair tokens collected in fees are distribute among the contract,
* staking tribute, governance tribute and funding pool in specific percentages.
*
* Allows opening, updating and processing of Cross Share Requests.
*
* Attributes:
* - Supports the full workflow of a cost share request
* - Handles Fair membership
* - Handles governance rewards
* - Retrieves ETH price via Chainlink
* - Calculates Fair price via Uniswap using Time-Weighted Price Averages (TWAP)
*/
// solhint-disable not-rely-on-time, var-name-mixedcase, reason-string /*
contract FairSideNetwork is
IFairSideNetwork,
IFairSideNetworkErrors,
Initializable,
UUPSUpgradeable,
ReentrancyGuardUpgradeable
{
/* ========== LIBRARIES ========== */
using AddressUpgradeable for address payable;
using ABDKMathQuadUInt256 for uint256;
uint256 private constant MAXIMUM_GRACE_PERIOD = 100 * 365 days; // 100 years
struct MembershipType {
uint256 duration;
uint256 gracePeriod;
uint256 topupDisabledPeriod;
uint256 minimumPurchaseAmount;
uint256 maximumBenefitPerUser;
uint256 cost;
bool active;
}
/* ========== STATE VARIABLES ========== */
//mapping of id -> Memberships
mapping(uint256 => Membership) private membership;
//mapping of account (address) -> memberships
mapping(address => uint256[]) internal userMembership;
//mapping of addresses covered by an address / account
mapping(address => address[]) internal userCoveredAddress;
mapping(uint256 => MembershipType) public membershipTypes;
uint256 public numMembershipTypes;
// Tracking the number of cover ids
uint256 public membershipCount;
// Cost share benefits of the entire personal wallet protection cover in ETH
uint256 public totalPWPCSB;
// total cover cost received from users
uint256 public totalCoverCost;
//network gearing factor used for membership calculation
uint256 public networkGearingFactor;
// Risk based capital
uint256 public riskBasedCapital;
// estimated loss ratio
uint256 public lossRatio;
// Supported tokens for membership purchase, top up
enum TokenType {
ETH,
Fair
}
// Fair Token contract Address
IFair private fair;
//Fair Network
IFairSideClaims public fairSideClaims;
// Funding Pool Address
address public override FUNDING_POOL;
// Premiums Pool Address
address public PREMIUMS_POOL;
//patners pool address address
address public PARTNERS_ADDRESS;
// Premium Reward multisign Address
address public override PREMIUM_REWARD_ADDRESS;
// membership purchase proxy
address public membershipPurchaseProxy;
// Bountry pool contract address
address public FairSIDE_BOUNTY_POOL;
// Timelock Address, owned by Governance
address public TIMELOCK;
// 20% as staking rewards
uint256 public STAKING_REWARDS;
// 15% patner pool rewards
uint256 public PARTNERS_POOL_ALLOCATION;
//7.5% as funding pool
uint256 public FUNDING_POOL_ALLOCATION;
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
// enable or disable Fair purchase
bool public fairPurchaseEnabled;
// Admin contract
IFairsideAdmin private fairsideAdmin;
/* ========== EVENTS ========== */
// An event emitted when a membership is purchased (either new or an extension)
event NewMembership(
address indexed member,
uint256 coverId,
uint256 costshareBenefit,
address indexed coverAddress,
TokenType tokenType,
uint256 membershipTypeId
);
// An event emitted when a membership is topped up
event TopUpCover(
address indexed member,
uint256 coverId,
uint256 costshareBenefit,
TokenType tokenType
);
// An event for setting Fair Premium Reward contract address
event SetFairPremiumReward(address indexed premiumRewardContract);
// An event for setting ETH Premium Reward contract address
event SetETHPremiumReward(address indexed premiumRewardContract);
// An event for setting Network gearing factor
event SetNetworkGearingFactor(uint256 networkGearingFactor);
// An event for setting Risk based capital
event SetRiskBasedCapital(uint256 riskBasedCapital);
// An event for setting Loss ratio
event SetLossRatio(uint256 lossRatio);
event SetFeeDistributionPercenages(
uint256 fundingPoolAllocation,
uint256 partnersPoolAllocation,
uint256 stakingRewards,
uint256 lossRatio
);
// An event for sending Staking rewards on Fair
event PremiumFairDistributed(
uint256 fundingPremium,
uint256 parntersPremium,
uint256 stakingRewards,
uint256 curveReserve
);
// An event for sending Staking rewards on ETH
event PremiumEthDistributed(
uint256 fundingPremium,
uint256 parntersPremium,
uint256 stakingRewards,
uint256 curveReserve
);
// An event for setting the token gearing factor
event TokenGearingFactorSet(uint256 tokenGearingFactor);
// An event for setting the Fair purchase enabled
event SetFairPurchaseEnabled(bool fairPurchaseEnabled);
constructor() {
_disableInitializers();
}
/**
* @dev Initialises the contract's state setting fair, FUNDING_POOL
* and TIMELOCK addresses.
*/
function initialize(
IFair _fair,
address fundingPool,
address premiumsPool,
IFairsideAdmin _fairsideAdmin,
address timelock,
address patnersPool,
address premiumReward
) public initializer {
__UUPSUpgradeable_init();
__ReentrancyGuard_init();
fair = _fair;
FUNDING_POOL = fundingPool;
PREMIUMS_POOL = premiumsPool;
fairsideAdmin = _fairsideAdmin;
TIMELOCK = timelock;
PARTNERS_ADDRESS = patnersPool;
PREMIUM_REWARD_ADDRESS = premiumReward;
membershipPurchaseProxy = msg.sender;
fairPurchaseEnabled = false;
// 20% staking rewards
STAKING_REWARDS = 0.20 ether;
// 15% patner pool rewards
PARTNERS_POOL_ALLOCATION = 0.15 ether;
// 7.5% as funding pool
FUNDING_POOL_ALLOCATION = 0.075 ether;
riskBasedCapital = 2500 ether;
lossRatio = 0.575 ether; //57.5%
networkGearingFactor = 50;
_addMembershipType(
365 days,
60 days,
182 days,
1 ether,
100 ether,
0.0195 ether
);
}
function addMembershipType(
uint256 duration,
uint256 gracePeriod,
uint256 topupDisabledPeriod,
uint256 minimumPurchaseAmount,
uint256 maximumBenefitPerUser,
uint256 cost
) external onlyAdmin {
_addMembershipType(
duration,
gracePeriod,
topupDisabledPeriod,
minimumPurchaseAmount,
maximumBenefitPerUser,
cost
);
}
function _addMembershipType(
uint256 duration,
uint256 gracePeriod,
uint256 topupDisabledPeriod,
uint256 minimumPurchaseAmount,
uint256 maximumBenefitPerUser,
uint256 cost
) private {
// duration should be divisible by 1 day
if (
duration == 0 ||
(duration % 1 days != 0) ||
cost == 0 ||
topupDisabledPeriod > duration ||
gracePeriod > MAXIMUM_GRACE_PERIOD ||
maximumBenefitPerUser == 0 ||
maximumBenefitPerUser < minimumPurchaseAmount
) {
revert FSNetwork_IncorrectValueSpecified();
}
membershipTypes[numMembershipTypes] = MembershipType({
duration: duration,
gracePeriod: gracePeriod,
topupDisabledPeriod: topupDisabledPeriod,
minimumPurchaseAmount: minimumPurchaseAmount,
maximumBenefitPerUser: maximumBenefitPerUser,
cost: cost,
active: true
});
unchecked {
numMembershipTypes += 1;
}
}
function disableMembershipType(uint256 index) external onlyAdmin {
membershipTypes[index].active = false;
}
/**
* @dev Setting the fairside claims contract
*/
function setFairSideClaims(
IFairSideClaims _fairSideClaims
) external onlyAdmin {
fairSideClaims = _fairSideClaims;
}
/*
* @dev Setting the fairside bounty pool contract
*/
function setFairSideBountyPool(
address _fairSideBountyPool
) external onlyAdmin {
FairSIDE_BOUNTY_POOL = _fairSideBountyPool;
}
/**
* @dev returns the list of cover ids purchased by an address
*/
function getAccountMembership(
address account
) external view returns (uint256[] memory) {
return userMembership[account];
}
/**
* @dev returns the list of covered addresses covered by an address
*/
function getAccountCoveredWallets(
address account
) external view returns (address[] memory) {
return userCoveredAddress[account];
}
/**
* @notice returns Membership state containing
* (availableCostShareBenefits, duration, creation, owner account, covered wallet)
*/
function getMembership(
uint256 coverId
) external view override returns (Membership memory) {
return membership[coverId];
}
/**
* @dev Token price evaluated as spot price directly on curve
*/
function getFairPrice() public view override returns (uint256) {
uint256 fShare = getNetworkFShare();
uint256 capitalPool = getCapitalPool();
return FairSideFormula2.f(capitalPool, fShare);
}
/**
* @dev : checks if membership still valid using grace period
*/
function hasPassedGracePeriod(
uint256 coverId
) external view override returns (bool) {
Membership memory _membership = membership[coverId];
if (_membership.availableCostShareBenefits <= 0) {
revert FSNetwork_ActiveMembershipRequired();
}
return
block.timestamp >
_membership.expirationDate +
membershipTypes[_membership.membershipTypeId].gracePeriod;
}
/**
* @notice : returns the maximum CSB based on RSB and network gearing factor
*/
function getMaxTotalCostShareBenefits() public view returns (uint256) {
return riskBasedCapital * networkGearingFactor;
}
/**
* @notice get amount in capital pool (ETH)
* @dev : Capital Pool = Total Funds held in ETH – Open Cost Share Requests
* Open Cost Share Request = Cost share request awaiting assessor consensus
*/
function getCapitalPool() public view override returns (uint256) {
return address(fair).balance - getTotalOpenRequests();
}
/**
* @notice : returns cover cost of a cover
*/
function getCoverCost(
uint256 membershipTypeId
) external view override returns (uint256 coverCost) {
return membershipTypes[membershipTypeId].cost;
}
/**
* @notice Allows purchasing of membership of Fair Network with ETH and Fair
*
* @dev It accepts ETH to allocate the available cross share benefits
* for a member and also determines membership purchase cost.
*
* The membership is purchased using ETH, 57.5% of which remains in the curve,
* 20% is allocated with staking rewards, 7.5% is allocated for the {PREMIUMS_POOL}
* and 7.5% is sent to {FUNDING_POOL}.
*
*/
function purchaseMembership(
uint256 costShareBenefit,
address coverAddress,
uint256 membershipType
) external payable validateOpenCurve(TokenType.ETH) {
_purchaseMembership(
msg.sender,
costShareBenefit,
coverAddress,
membershipType,
TokenType.ETH
);
}
/*
* @notice : Allows purchasing of membership of Fair Network with ETH from a membershipPurchaseProxy
*/
function purchaseMembershipFromProxy(
address primaryAddress,
uint256 costShareBenefit,
address coverAddress,
uint256 membershipType
)
external
payable
validateOpenCurve(TokenType.ETH)
onlyMembershipPurchaseProxy
{
_purchaseMembership(
primaryAddress,
costShareBenefit,
coverAddress,
membershipType,
TokenType.ETH
);
}
/**
* @notice Allows purchasing of membership with Fair
*/
function purchaseMembershipWithFair(
uint256 costShareBenefit,
address coverAddress,
uint256 membershipType
) external onlyFairPurchaseEnabled validateOpenCurve(TokenType.Fair) {
_purchaseMembership(
msg.sender,
costShareBenefit,
coverAddress,
membershipType,
TokenType.Fair
);
}
/*
* @notice : Allows purchasing of membership of Fair Network with Fair from a membershipPurchaseProxy
*/
function purchaseMembershipWithFairFromProxy(
address primaryAddress,
uint256 costShareBenefit,
address coverAddress,
uint256 membershipType
)
external
onlyFairPurchaseEnabled
validateOpenCurve(TokenType.Fair)
onlyMembershipPurchaseProxy
{
_purchaseMembership(
primaryAddress,
costShareBenefit,
coverAddress,
membershipType,
TokenType.Fair
);
}
/**
* @notice : Top up a current exisiting purchased cover with ETH
* @dev : the prorated cost is charged { cost on time of purchase 360 days = full % of cover}
*/
function topupMembership(
uint256 coverId,
uint256 costshareBenefit
) external payable validateOpenCurve(TokenType.ETH) {
_topupMembership(coverId, costshareBenefit, TokenType.ETH);
}
/**
* @notice : Top up a current exisiting purchased cover with Fair
* @dev : the prorated cost is charged { cost on time of purchase 360 days = full % of cover}
*/
function topupMembershipWithFair(
uint256 coverId,
uint256 costshareBenefit
) external onlyFairPurchaseEnabled validateOpenCurve(TokenType.Fair) {
_topupMembership(coverId, costshareBenefit, TokenType.Fair);
}
/**
* @dev : handles premium membership purchase, for PWP applies the PWP gearing factor {PWPGearing} and PWPfshareRation for PWP cover purchase
* and uses the default fshare and fshareRatio for other covers
* the prorated cost is charged { cost on time of purchase 360 days = full % of cover}
*/
function _purchaseMembership(
address primaryAddress,
uint256 costShareBenefit,
address coverAddress,
uint256 membershipTypeId,
TokenType tokenType
) private nonReentrant {
validateCapitalPool(costShareBenefit, membershipTypeId);
MembershipType memory membershipType = membershipTypes[
membershipTypeId
];
if (!membershipType.active) {
revert FSNetwork_MembershipTypeDisabled();
}
if (costShareBenefit > _getMaximumBenefitPerUser(membershipTypeId)) {
revert FSNetwork_ExceedsCSBLimitPerAccount();
}
//calculate membership cost
uint256 coverCostETH = calculateCoverCost(
costShareBenefit,
0,
membershipType.duration,
membershipType.cost
);
distributePremium(coverCostETH, tokenType);
unchecked {
membershipCount += 1;
}
uint256 coverId = membershipCount;
Membership storage membershipId = membership[coverId];
//update storages
totalPWPCSB += costShareBenefit;
totalCoverCost += coverCostETH;
membershipId.availableCostShareBenefits = costShareBenefit;
membershipId.creation = uint80(block.timestamp);
membershipId.expirationDate = uint80(
block.timestamp + membershipType.duration
);
membershipId.owner = primaryAddress;
membershipId.wallet = coverAddress;
membershipId.membershipTypeId = membershipTypeId;
membershipId.coverCost += coverCostETH;
userMembership[primaryAddress].push(coverId);
userCoveredAddress[primaryAddress].push(coverAddress);
emit NewMembership(
primaryAddress,
coverId,
costShareBenefit,
coverAddress,
tokenType,
membershipTypeId
);
}
/**
* @dev : validate if the cost share benefit meets the required threshold
* and the capital pool has enough funds to cover the membership
*/
function validateCapitalPool(
uint256 costShareBenefit,
uint256 membershipTypeId
) private view {
//mimimun CSB
if (
costShareBenefit <
membershipTypes[membershipTypeId].minimumPurchaseAmount
) {
revert FSNetwork_InvalidCostShareBenefitSpecified();
}
if (totalPWPCSB + costShareBenefit > getMaxTotalCostShareBenefits()) {
revert FSNetwork_ExceedsMaxCostShareBenefitLimit();
}
}
/**
* @dev : handles premium membership purchase, for PWP applies the PWP gearing factor {PWPGearing} and PWPfshareRation for PWP cover purchase
* and uses the default fshare and fshareRatio for other covers
* the prorated cost is charged { cost on time of purchase 360 days = full % of cover}
*/
function _topupMembership(
uint256 coverId,
uint256 costShareBenefit,
TokenType tokenType
) private onlyNotBlocked(coverId) nonReentrant {
Membership storage membershipId = membership[coverId];
validateCapitalPool(costShareBenefit, membershipId.membershipTypeId);
MembershipType memory membershipType = membershipTypes[
membershipId.membershipTypeId
];
uint256 membershipExpirationDate = membershipId.expirationDate;
if (
block.timestamp >=
membershipExpirationDate - membershipType.topupDisabledPeriod
) {
revert FSNetwork_MembershipTopupDisabled();
}
uint256 userMembershipCSB = membershipId.availableCostShareBenefits +
costShareBenefit;
if (
userMembershipCSB >
_getMaximumBenefitPerUser(membershipId.membershipTypeId)
) {
revert FSNetwork_ExceedsCostShareBenefitLimitPerAccount();
}
uint256 coverCostETH = calculateCoverCost(
costShareBenefit,
membershipExpirationDate,
membershipType.duration,
membershipType.cost
);
distributePremium(coverCostETH, tokenType);
totalPWPCSB += costShareBenefit;
totalCoverCost += coverCostETH;
membershipId.availableCostShareBenefits = userMembershipCSB;
membershipId.coverCost += coverCostETH;
emit TopUpCover(
membershipId.wallet,
coverId,
costShareBenefit,
tokenType
);
}
/**
* @dev : checks if the cover cost is sufficient
* and distributes premiums to pools based on the token type provided
*/
function distributePremium(
uint256 membershipFeeETH,
TokenType tokenType
) internal {
if (tokenType == TokenType.ETH) {
if (msg.value < membershipFeeETH) {
revert FSNetwork_PremiumFeeSentIsLessThanRequired();
}
premiumDistributionETH(membershipFeeETH);
} else {
//convert to Fair
uint256 membershipFeeFair = membershipFeeETH.div(getFairPrice());
IERC20 _fairToken = IERC20(address(fair));
if (_fairToken.balanceOf(msg.sender) < membershipFeeFair) {
revert FSNetwork_NotEnoughFair();
}
//receive fair from user
_fairToken.transferFrom(
msg.sender,
address(this),
membershipFeeFair
);
//distribute premium
premiumDistributionFair(membershipFeeFair);
}
}
/*
* @dev : remove expired membership's csb from the network
*/
function removeExpiredMembershipCSB(
uint256 coverId
) external override onlyFairSideBountyPool {
Membership storage membershipId = membership[coverId];
if (membershipId.availableCostShareBenefits <= 0) {
revert FSNetwork_ActiveMembershipRequired();
}
MembershipType memory membershipType = membershipTypes[
membershipId.membershipTypeId
];
uint256 gracePeriod = membershipId.expirationDate +
membershipType.gracePeriod;
if (block.timestamp < gracePeriod) {
revert FSNetwork_MembershipNotExpired();
}
totalPWPCSB -= membershipId.availableCostShareBenefits;
totalCoverCost -= membershipId.coverCost;
membershipId.availableCostShareBenefits = 0;
membershipId.coverCost = 0;
}
/**
* @dev : estimates the cost of a cover with given amount
*/
function estimateCost(
uint256 costShareBenefit,
uint256 expirationDate,
uint256 membershipTypeId
) external view override returns (uint256) {
MembershipType memory membershipType = membershipTypes[
membershipTypeId
];
return
calculateCoverCost(
costShareBenefit,
expirationDate,
membershipType.duration,
membershipType.cost
);
}
/**
* @dev changes the premiums pool address to a new address
*/
function setPremiumsPool(address _newPremiumsPool) external onlyAdmin {
PREMIUMS_POOL = _newPremiumsPool;
}
/*
* @dev changes the funding pool address to a new address
*/
function setFundingPool(
address payable _newFundingPool
) external onlyAdmin {
FUNDING_POOL = _newFundingPool;
}
/**
* @dev changes the partners pool address to a new address
*/
function setPartnersPool(address _newPartnersPool) external onlyAdmin {
PARTNERS_ADDRESS = _newPartnersPool;
}
/**
* @dev changes the premium reward address to a new address
*/
function setPremiumRewardAddress(
address _newPremiumRewardAddress
) external onlyAdmin {
PREMIUM_REWARD_ADDRESS = _newPremiumRewardAddress;
}
/**
* @dev changes the membership purchase proxy address to a new address
*/
function setMembershipPurchaseProxy(
address _newMembershipPurchaseProxy
) external onlyAdmin {
membershipPurchaseProxy = _newMembershipPurchaseProxy;
}
/**
* @notice This method handles the distribution of Fair to different pools
* @dev it's called after a membership is purchased, topped up by Fair
* Once the Fair is recieved smart staking calculation is done
* DISTRIBUTION IS HANDLED AS
* Staking Rewards 20% sent to rewardsContract
* Gov. 7.5% = sent to funding pool
* Partners Pool 15% sent to Premiums Pool
* 57.5% burnt
*/
function premiumDistributionFair(uint256 fairToDistribute) private {
if (IERC20(address(fair)).balanceOf(address(this)) < fairToDistribute) {
revert FSNetwork_NotEnoughFair();
}
//Calculate funding pool rewards
uint256 fundingPremium = fairToDistribute.mul(FUNDING_POOL_ALLOCATION);
uint256 parntersPremium = fairToDistribute.mul(
PARTNERS_POOL_ALLOCATION
);
uint256 stakingRewards = fairToDistribute.mul(STAKING_REWARDS);
uint256 curveReserve = fairToDistribute.mul(lossRatio);
ERC20Burnable _fairToken = ERC20Burnable(address(fair));
// 7,5% sent to governance
_fairToken.transfer(FUNDING_POOL, fundingPremium);
//patners pool 15%
_fairToken.transfer(PARTNERS_ADDRESS, parntersPremium);
// 20% staking rewards
_fairToken.transfer(PREMIUM_REWARD_ADDRESS, stakingRewards);
// burn remaining 57.5%
_fairToken.burn(curveReserve);
//register premium distribution in Fair
emit PremiumFairDistributed(
fundingPremium,
parntersPremium,
stakingRewards,
curveReserve
);
}
/**
* @notice This method handles the distribution of ETH to different pools
* @dev it's called after a membership is purchased, topped up by ETH
* Once the ETH is recieved smart staking calculation is done
* DISTRIBUTION IS HANDLED AS
* Staking Rewards 20% sent to rewardsContract
* Gov. 7.5% = sent to funding pool
* Partners Pool 15% sent to Premiums Pool
* 57.5% sent to curve with no emission of FS
*/
function premiumDistributionETH(uint256 ethToDistribute) private {
//Calculate funding pool rewards
uint256 fundingPremium = ethToDistribute.mul(FUNDING_POOL_ALLOCATION);
uint256 parntersPremium = ethToDistribute.mul(PARTNERS_POOL_ALLOCATION);
uint256 stakingRewards = ethToDistribute.mul(STAKING_REWARDS);
uint256 curveReserve = ethToDistribute.mul(lossRatio);
// 7,5% sent to governance
payable(FUNDING_POOL).sendValue(fundingPremium);
//patners pool 15%
payable(PARTNERS_ADDRESS).sendValue(parntersPremium);
//add staking reward in ETH to be distributed
payable(PREMIUM_REWARD_ADDRESS).sendValue(stakingRewards);
//send remaining 57.5% to the curve
fair.bondNoEmission{value: curveReserve}();
//register premium distribution in ETH
emit PremiumEthDistributed(
fundingPremium,
parntersPremium,
stakingRewards,
curveReserve
);
// send back excess ETH
if (msg.value > ethToDistribute) {
payable(msg.sender).sendValue(msg.value - ethToDistribute);
}
}
/* ========== RESTRICTED FUNCTIONS ========== */
/**
* @notice : increases or decreases the CSB when a claims requested is created.
* @dev Can only be called by the fairside claims contract
*/
function increaseOrDecreaseCSB(
uint256 amount,
address account,
uint256 coverId,
bool increase
) external override onlyFairSideClaims {
Membership storage membershipId = membership[coverId];
if (membershipId.owner != account) {
revert FSNetwork_InvalidCoverIdForAccount();
}
if (increase) {
membershipId.availableCostShareBenefits += amount;
} else {
membershipId.availableCostShareBenefits -= amount;
}
}
/**
* @notice : block cover when a claim is created, unblock when claim is resolved
* @dev Can only be called by the fairside claims contract
*/
function blockMembership(
uint256 coverId,
bool blocked
) external override onlyFairSideClaims {
Membership storage membershipId = membership[coverId];
membershipId.blocked = blocked;
}
/**
* @notice This method flushes out assets mistakenly sent to the contract
* @dev Can only be called by the premium pool multisig
*/
function flushAsset(
address tokenContractAddress,
address payable destination
) external payable onlyPremiumPool {
if (tokenContractAddress == address(0)) {
destination.sendValue(address(this).balance);
} else {
IERC20 tokenContract = ERC20(tokenContractAddress);
tokenContract.transfer(
destination,
tokenContract.balanceOf(address(this))
);
}
}
/**
* @dev Sets the gearing factor used for signing new memberships
*
* Requirements:
* - only callable by governance or timelock contracts.
*/
function setNetworkGearingFactor(
uint256 _gearingFactor
) external onlyTimelock {
if (_gearingFactor == 0) {
revert FSNetwork_IncorrectValueSpecified();
}
networkGearingFactor = _gearingFactor;
emit SetNetworkGearingFactor(_gearingFactor);
}
/**
* @dev Sets the risk based capital used for calculating the network FSHARE
*
* Requirements:
* - only callable by governance or timelock contracts.
*/
function setRiskBasedCapital(
uint256 _riskBasedCapital
) external onlyTimelock {
riskBasedCapital = _riskBasedCapital;
emit SetRiskBasedCapital(_riskBasedCapital);
}
/**
* @dev Sets the distribution percentages
*
* Requirements:
* - only callable by governance or timelock contracts.
*/
function setFeeDistributionPercentages(
uint256 _fundingPoolAllocation,
uint256 _partnersPoolAllocation,
uint256 _stakingRewards,
uint256 _lossRatio
) external onlyTimelock {
if (
_fundingPoolAllocation +
_partnersPoolAllocation +
_stakingRewards +
_lossRatio !=
1 ether
) {
revert FSNetwork_IncorrectValueSpecified();
}
FUNDING_POOL_ALLOCATION = _fundingPoolAllocation;
PARTNERS_POOL_ALLOCATION = _partnersPoolAllocation;
STAKING_REWARDS = _stakingRewards;
lossRatio = _lossRatio;
emit SetFeeDistributionPercenages(
_fundingPoolAllocation,
_partnersPoolAllocation,
_stakingRewards,
_lossRatio
);
}
/**
* @dev Set flag for enabling or disabling Fair purchase
*
* Requirements:
* - only callable by governance or timelock contracts.
*/
function setEnableFairPurchase(
bool _fairPurchaseEnabled
) external onlyTimelock {
fairPurchaseEnabled = _fairPurchaseEnabled;
emit SetFairPurchaseEnabled(_fairPurchaseEnabled);
}
/* ========== INTERNAL FUNCTIONS ========== */
/*
* @dev : decrement total cost share benefits after a claim is paid out
*/
function decrementTotalPWPCSB(
uint256 costShareBenefit
) external override onlyFairSideClaims {
totalPWPCSB -= costShareBenefit;
}
/**
* @dev Calculates the prorated cover cost of a given cover
* @return cover cost (uint)
*/
function calculateCoverCost(
uint256 costShareBenefit,
uint256 coverPeriod,
uint256 duration,
uint256 coverCost
) internal view returns (uint256) {
uint256 fee = costShareBenefit.mul(coverCost);
if (block.timestamp > coverPeriod) {
return fee;
} else {
uint256 dailycost = coverCost / (duration / 1 days);
uint256 coverDuration = coverPeriod - block.timestamp;
uint256 daysRemaining = coverDuration / 1 days;
// round up to the nearest day
if (coverDuration % 1 days != 0) {
daysRemaining += 1;
}
uint256 rate = daysRemaining * dailycost;
uint256 proratedCostETH = costShareBenefit.mul(rate);
return proratedCostETH;
}
}
function _authorizeUpgrade(
address newImplementation
) internal override onlyGuardian {}
/**
* @notice Return Open cost share requests in ETH
*/
function getTotalOpenRequests() public view returns (uint256) {
return fairSideClaims.totalOpenRequests();
}
function isOpenCurve() public view returns (bool) {
return fair.currentPhase() == IFair.Phase.Final;
}
/**
* @notice : calculates FSHARE risk based capital and loss ratio over
* the total cost share benefits
*/
function getNetworkFShare() public view override returns (uint256) {
return riskBasedCapital + lossRatio.mul(totalCoverCost);
}
/**
* @dev Returns maximum cross share benefit allowed per user.
*/
function _getMaximumBenefitPerUser(
uint256 membershipTypeId
) internal view returns (uint256) {
return membershipTypes[membershipTypeId].maximumBenefitPerUser;
}
/**
* @notice shows the version of the contract being used
* @dev the value represents the current version of the contract should be updated and overriden with new implementations
* @return version -the current version of the contract
*/
function version() external pure virtual returns (string memory) {
return "1.0.0";
}
/* ========== MODIFIERS ========== */
modifier onlyTimelock() {
if (msg.sender != TIMELOCK) {
revert FSNetwork_InsufficientPrivileges();
}
_;
}
modifier validateOpenCurve(TokenType tokenType) {
if (tokenType != TokenType.ETH) {
if (!isOpenCurve()) {
revert FSNetwork_CurveIsClosedUseETH();
}
}
_;
}
modifier onlyPremiumPool() {
if (msg.sender != PREMIUMS_POOL) {
revert FSNetwork_OnlyPremiumPoolCanCall();
}
_;
}
modifier onlyFairSideClaims() {
if (msg.sender != address(fairSideClaims)) {
revert FSNetwork_OnlyFairSideClaimsCanCall();
}
_;
}
modifier onlyNetworkOrClaims() {
if (
msg.sender != address(fairSideClaims) && msg.sender != address(this)
) revert FSNetwork_OnlyFairSideClaimsCanCall();
_;
}
modifier onlyFairSideBountyPool() {
if (msg.sender != FairSIDE_BOUNTY_POOL) {
revert FSNetwork_OnlyFairSideBountyPoolCanCall();
}
_;
}
modifier onlyMembershipPurchaseProxy() {
if (msg.sender != membershipPurchaseProxy) {
revert FSNetwork_OnlyMembershipPurchaseProxyCanCall();
}
_;
}
modifier onlyFairPurchaseEnabled() {
if (!fairPurchaseEnabled) {
revert FSNetwork_FairPurchaseDisabled();
}
_;
}
modifier onlyGuardian() {
if (
!fairsideAdmin.hasFSRole(
IFairsideAdmin.FSRoles.FS_NETWORK_GUARDIAN_ROLE,
msg.sender
)
) {
revert FSNetwork_InsufficientPrivilegesOnlyGuardian();
}
_;
}
modifier onlyAdmin() {
if (
!fairsideAdmin.hasFSRole(
IFairsideAdmin.FSRoles.FS_ADMIN_ROLE,
msg.sender
)
) {
revert FSNetwork_InsufficientPrivilegesOnlyAdmin();
}
_;
}
modifier onlyNotBlocked(uint256 coverId) {
if (membership[coverId].blocked) {
revert FSNetwork_MembershipBlocked();
}
_;
}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "../dependencies/ABDKMathQuadUInt256.sol";
interface IFSTimeLockErrors {
/**
* @dev FSTimeLock-related custom errors
*/
error FSTimeLock_DelayMustExceedMinimumDelay();
error FSTimeLock_DelayMustNotExceedMaximumDelay();
error FSTimeLock_CallMustComeFromAdmin();
error FSTimeLock_CallMustComeFromPendingAdmin();
error FSTimeLock_EstimatedExecutionBlockMustSatisfyDelay();
error FSTimeLock_TransactionHasNotSurpassedTimeLock();
error FSTimeLock_TransactionIsStale();
error FSTimeLock_TransactionHasNotBeenQueued();
error FSTimeLock_TransactionExecutionReverted();
}
/**
* @dev Implementation of {Timelock} contract.
*
* It allows queueing, execution and cancellation of transactions by the
* {admin}. A queued transaction can be executed after the cool-time represented
* by {delay} has elapsed and grace period has not passed since the queuing
* of transaction.
*
* It allows changing of contract's admin through a queued transaction by the
* prior admin. The new admin the calls {acceptAdmin} to accept its role.
*/
// solhint-disable not-rely-on-time, var-name-mixedcase, reason-string /*
contract Timelock is IFSTimeLockErrors {
using ABDKMathQuadUInt256 for uint256;
/* ========== STATE VARIABLES ========== */
// Time period a tx is valid for execution after eta has elapsed.
uint256 public constant GRACE_PERIOD = 14 days;
// The minimum delay required for execution after a tx is queued
uint256 public constant MINIMUM_DELAY = 2 days;
// The maxium delay required for execution after a tx is queued
uint256 public constant MAXIMUM_DELAY = 30 days;
// Current admin of the contract
address public admin;
// Pending admin of the contract
address public pendingAdmin;
// Cool-off before a queued transaction is executed
uint256 public delay;
// Queued status of a transaction (txHash => tx status).
mapping(bytes32 => bool) public queuedTransactions;
/* ========== EVENTS ========== */
// Emitted when a new admin is set
event NewAdmin(address indexed newAdmin);
// Emitted when a new pending admin is set
event NewPendingAdmin(address indexed newPendingAdmin);
// Emitted when a new delay/cool-off time is set
event NewDelay(uint256 indexed newDelay);
// Emitted when a tx is cancelled
event CancelTransaction(
bytes32 indexed txHash,
address indexed target,
uint256 value,
string signature,
bytes data,
uint256 eta
);
// Emitted when a tx is executed
event ExecuteTransaction(
bytes32 indexed txHash,
address indexed target,
uint256 value,
string signature,
bytes data,
uint256 eta
);
// Emitted when a tx is queued
event QueueTransaction(
bytes32 indexed txHash,
address indexed target,
uint256 value,
string signature,
bytes data,
uint256 eta
);
// Emitted when a tx is cancelled for No Contract-Based Proposal
event EvCancelTransaction(
bytes32 indexed txHash,
uint256 proposalId,
uint256 eta
);
// Emitted when a tx is executed for No Contract-Based Proposal
event EvExecuteTransaction(
bytes32 indexed txHash,
uint256 proposalId,
uint256 eta
);
// Emitted when a tx is queued for No Contract-Based Proposal
event EvQueueTransaction(
bytes32 indexed txHash,
uint256 proposalId,
uint256 eta
);
/* ========== CONSTRUCTOR ========== */
/**
* @dev Sets contract's state variable of {admin} and {delay}
*
* Requirements:
* - `delay_` param must be within range or min and max delay
*/
constructor(address admin_, uint256 delay_) {
if (delay_ < MINIMUM_DELAY) {
revert FSTimeLock_DelayMustExceedMinimumDelay();
}
if (delay_ > MAXIMUM_DELAY) {
revert FSTimeLock_DelayMustNotExceedMaximumDelay();
}
admin = admin_;
delay = delay_;
}
/* ========== VIEWS ========== */
/* ========== MUTATIVE FUNCTIONS ========== */
/**
* @dev Allows of receiving of ether beforehand or in bulk, so the sending
* ether is optional at the time of tx execution.
*/
// solhint-disable-next-line
receive() external payable {}
/* ========== RESTRICTED FUNCTIONS ========== */
/**
* @dev Sets the the new value of {delay}.
* It allows setting of new delay value through queued tx by the admin
*
* Requirements:
* - only current contract can call it
* - `delay_` param must be within the min and max delay range
*/
function setDelay(uint256 delay_) public {
if (msg.sender != admin) {
revert FSTimeLock_CallMustComeFromAdmin();
}
if (delay_ < MINIMUM_DELAY) {
revert FSTimeLock_DelayMustExceedMinimumDelay();
}
if (delay_ > MAXIMUM_DELAY) {
revert FSTimeLock_DelayMustNotExceedMaximumDelay();
}
delay = delay_;
emit NewDelay(delay);
}
/**
* @dev Sets {pendingAdmin} to admin of current contract.
* A {GovernorAlpha} contract which is already set as {pendingAdmin}
* of this contract calls this function to set itself as new admin.
*
* Requirements:
* - only callable by {pendingAdmin}
*/
function acceptAdmin() public {
if (msg.sender != pendingAdmin) {
revert FSTimeLock_CallMustComeFromPendingAdmin();
}
admin = msg.sender;
pendingAdmin = address(0);
emit NewAdmin(admin);
}
/**
* @dev Sets the the new value of {pendingAdmin_}.
* It allows setting of new pendingAdmin value through queued tx by the admin
*
* Requirements:
* - only current contract can call it
*/
function setPendingAdmin(address pendingAdmin_) public {
if (msg.sender != admin) {
revert FSTimeLock_CallMustComeFromAdmin();
}
pendingAdmin = pendingAdmin_;
emit NewPendingAdmin(pendingAdmin);
}
/**
* @dev Queues a transaction by setting its status in {queuedTransactions} mapping.
*
* Requirements:
* - only callable by {admin}
* - `eta` must lie in future compared to delay referenced from current block
*/
function queueTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) public {
if (msg.sender != admin) {
revert FSTimeLock_CallMustComeFromAdmin();
}
if (eta < getBlockTimestamp() + delay) {
revert FSTimeLock_EstimatedExecutionBlockMustSatisfyDelay();
}
bytes32 txHash = keccak256(
abi.encode(target, value, signature, data, eta)
);
queuedTransactions[txHash] = true;
emit QueueTransaction(txHash, target, value, signature, data, eta);
}
/**
* @dev Cancels a transaction by setting its status in {queuedTransactions} mapping.
*
* Requirements:
* - only callable by {admin}
*/
function cancelTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) public {
if (msg.sender != admin) {
revert FSTimeLock_CallMustComeFromAdmin();
}
bytes32 txHash = keccak256(
abi.encode(target, value, signature, data, eta)
);
queuedTransactions[txHash] = false;
emit CancelTransaction(txHash, target, value, signature, data, eta);
}
/**
* @dev Modifier to check if a transaction can be executed.
*
* Requirements:
* The caller should be the admin
* The current timestamp should NOT be ahead of the tx's eta
* The grace period associated with the tx should be valid
*/
modifier canExecuteOnly(uint256 eta) {
if (msg.sender != admin) {
revert FSTimeLock_CallMustComeFromAdmin();
}
if (getBlockTimestamp() < eta) {
revert FSTimeLock_TransactionHasNotSurpassedTimeLock();
}
if (getBlockTimestamp() > eta + GRACE_PERIOD) {
revert FSTimeLock_TransactionIsStale();
}
_;
}
/**
* @dev Executes a transaction by making a low level call to its `target`.
* The call reverts if the low-level call made to `target` reverts.
*
* Requirements:
* - only callable by {admin}
* - tx must already be queued
* - current timestamp is ahead of tx's eta
* - grace period associated with the tx must not have passed
* - the low-level call to tx's `target` must not revert
*/
function executeTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) public payable canExecuteOnly(eta) returns (bytes memory) {
bytes32 txHash = keccak256(
abi.encode(target, value, signature, data, eta)
);
if (!queuedTransactions[txHash]) {
revert FSTimeLock_TransactionHasNotBeenQueued();
}
queuedTransactions[txHash] = false;
bytes memory callData;
if (bytes(signature).length == 0) {
callData = data;
} else {
callData = abi.encodePacked(
bytes4(keccak256(bytes(signature))),
data
);
}
(bool success, bytes memory returnData) = target.call{value: value}(
callData
);
if (!success) {
revert FSTimeLock_TransactionExecutionReverted();
}
emit ExecuteTransaction(txHash, target, value, signature, data, eta);
return returnData;
}
/* ========== INTERNAL FUNCTIONS ========== */
/**
* @dev Gets timestamp from the current block.
*/
function getBlockTimestamp() public view returns (uint256) {
// solium-disable-next-line security/no-block-members
return block.timestamp;
}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "../dependencies/ABDKMathQuadUInt256.sol";
import "../dependencies/FairSideFormula2.sol";
interface IABCErrors {
/**
* @dev ABC-related custom errors
*/
error ABC_InsufficientCapitalToWithdraw();
}
/**
* @dev Implementation of Augmented Bonding Curve (ABC) contract.
*
* Attributes:
* - Calculates amount of Fair to be minted given a particular token supply and an amount of reserve
* - Calculates amount of reserve to be unlocked given a particular token supply and an amount of Fair tokens
* - Tracks creations and timestamps
*/
// solhint-disable not-rely-on-time, var-name-mixedcase, reason-string /*
contract ABC is IABCErrors {
using ABDKMathQuadUInt256 for uint256;
// Returns absolute value of the parameter {a}.
function _abs(int256 a) internal pure returns (uint256) {
return uint256(a < 0 ? -a : a);
}
/**
* @dev Returns the delta amount representing change in the supply of Fair token
* supply after the change in reserve amount is considered.
*
* Requirement:
* - the reserve amount should not go below {Fshare}.
*/
function _calculateDeltaOfFair(
uint256 reserve,
int256 reserveDelta,
uint256 openRequests,
uint256 fShare
) internal pure returns (uint256 supplyDelta) {
// Capital Pool = Total Funds held in ETH – Open Cost Share Requests
// Open Cost Share Request = Cost share request awaiting assessor consensus
uint256 capitalPool = reserve - openRequests;
if (reserveDelta < 0) {
uint256 capitalPostWithdrawal = capitalPool - (_abs(reserveDelta));
if (capitalPostWithdrawal < fShare) {
revert ABC_InsufficientCapitalToWithdraw();
}
supplyDelta = FairSideFormula2.getMintedAmount(
capitalPostWithdrawal,
_abs(reserveDelta),
fShare
);
} else {
supplyDelta = FairSideFormula2.getMintedAmount(
capitalPool,
uint256(reserveDelta),
fShare
);
}
return supplyDelta;
}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20PermitUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "../admin/IFairsideAdmin.sol";
import "../interfaces/network/IFairSideNetwork.sol";
import "../interfaces/token/IFair.sol";
import "../dependencies/ABDKMathQuadUInt256.sol";
import "../dependencies/EstimateEthInputLib.sol";
import "./ABC.sol";
interface IFairErrors {
/**
* @dev Fair-related custom errors
*/
error Fair_DepositAmountCannotBeZero();
error Fair_NothingToBurn();
error Fair_HighSlippage();
error Fair_AlreadyAtFinalPhase();
error Fair_FeeShouldBeLessThanOneEther();
error Fair_DifferentSizedArrays();
error Fair_AlreadySet();
error Fair_CanOnlySyncConvictionInFinalPhase();
error Fair_OnlyTimelockCanCall();
error Fair_OnlyFairSideClaimsCanCall();
error Fair_OnlyFairSideNetworkCanCall();
error Fair_InvalidPhase();
error Fair_Paused();
error Fair_InvalidCaller();
error Fair_AddressZero();
error Fair_ExceedsCapitalPoolWithdrawLimit();
error Fair_DailyUnBondedPercentageShouldBeLessThan100Percent();
error Fair_DepositAmountTooLarge();
}
/**
* @dev Implementation {Fair} ERC20 Token contract.
*
* The Fair contract allows depositing of ETH for bonding to curve and minting
* Fair in return. Only 70% of the deposit is bonded to curve during VCWL phase
* and the rest 30% is deposited to `fundingPool`.
*
* It also allows burning of Fair tokens to withdraw ETH. A portion of withdrawing ETH
* reserve is taken as tribute fee which is distributed to existing network users
*
* Has utility functions to modify the contract's state.
*
* Attributes:
* - Mintable via an Augmented Bonding Curve
* - Burnable via an Agumented Bonding Curve
* - Tracks creations and timestamps
*/
// solhint-disable not-rely-on-time, var-name-mixedcase, reason-string /*
contract Fair is
IFair,
ABC,
IFairErrors,
Initializable,
UUPSUpgradeable,
ReentrancyGuardUpgradeable,
ERC20PermitUpgradeable,
ERC20BurnableUpgradeable
{
/* ========== LIBRARIES ========== */
using ABDKMathQuadUInt256 for uint256;
using AddressUpgradeable for address payable;
/* ========== STATE VARIABLES ========== */
// 70% bonding curve ratio
uint256 private constant BONDING_CURVE_RATIO = 0.7 ether;
// Funding pool needs to achieve 500 ether
address public FUNDING_POOL;
// Timelock address
address public TIME_LOCK;
// tribute pool multisign address
address public tributePool;
// Fair Network address
IFairSideNetwork public fairSideNetwork;
//FairsideClaims Contract address
address public fairSideClaims;
// Fairside admin address
address public fairSideAdmin;
// currect phase of the Fair token
Phase public override currentPhase;
// Indicator of pause
bool public paused;
// 3.5% tribute fee on exit
uint256 public tributeFee;
// 500 ether
uint256 public FUNDING_POOL_THRESHOLD;
//Fair set to be burnt or reallocated
uint256 public toBeBurntFair;
// counter for total ETH staked by an address
mapping(address => uint256) public amountBonded;
uint256 public dailyUnbondedAmount;
uint256 public dailyUnbondedPercentageLimit;
uint256 public dailyUnbondedDate;
uint256 public maxDepositPerTransaction;
/* ========== EVENTS ========== */
/**
* @dev Emitted when a unbonded from Curve.
*/
event Unbond(
address indexed user,
uint256 capitalDesired,
uint256 amount,
uint256 toDistribute
);
/**
* @dev Emitted when bonded to Curve.
*/
event Bond(
address indexed user,
uint256 ethAmountBonded,
uint256 fairIssued
);
/**
* @dev Emitted when bonded without tokens to be burnt, {fairIssued: is Fair that wasnt minted}
*/
event BondWithoutMint(
address indexed user,
uint256 ethAmountBonded,
uint256 fairIssued
);
/**
* @dev Emitted when bonded and burned, {fairIssued: is Fair that wasnt minted}
*/
event BondAndBurn(
address indexed user,
uint256 ethAmountBonded,
uint256 tokensToBurn
);
/**
* @dev Emitted when tributes are distributed
*/
event TributesDistributed(uint256 distributedAmount);
/**
* @dev Emitted when the pause functionality is triggered.
*/
event PauseToggled();
/**
* @dev Emitted when the phase is advanced.
*/
event PhaseAdvanced(uint8 newPhase);
/**
* @dev Emitted when the phase is updated.
*/
event PhaseUpdated(uint8 newPhase);
/**
* @dev Emitted when the tribute fee is set.
*/
event TributeFeeSet(uint256 newTributeFee);
/**
* @dev Initialises the contract's state with {fundingPool} and {timelock} addresses.
* It also passes token name and symbol to ERC20 contract and
* the name to the Permit extension.
*/
function initialize(
address _fundingPool,
address _timelock,
address _tributePool,
address _fairSideAdmin
) public virtual initializer {
__ERC20_init("Fair", "Fair");
__ERC20Burnable_init();
__ERC20Permit_init("Fair");
__ReentrancyGuard_init();
if (
_fundingPool == address(0) ||
_timelock == address(0) ||
_tributePool == address(0) ||
_fairSideAdmin == address(0)
) {
revert Fair_AddressZero();
}
FUNDING_POOL = _fundingPool;
TIME_LOCK = _timelock;
tributePool = _tributePool;
fairSideAdmin = _fairSideAdmin;
tributeFee = 0.035 ether;
dailyUnbondedPercentageLimit = 0.01 ether; // 1%
maxDepositPerTransaction = 50 ether;
FUNDING_POOL_THRESHOLD = 500 ether;
}
/**
* @dev Returns available ETH balance in the curve.
*/
function getReserveBalance() internal view returns (uint256) {
return address(this).balance;
}
/**
* @dev Returns the amount of Fair available for minting after
* reserve is increased by delta.
* @param investment The reserve delta - Amount of tokens to be added to the reserve
*/
function getTokensMinted(
uint256 investment
) external view override returns (uint256) {
return calculateDeltaOfFair(getReserveBalance(), int256(investment));
}
/**
* @dev Returns the amount of Fair available for burning after
* reserve is decreased by delta.
* @param withdrawal The reserve delta - Amount of tokens to be removed from the reserve
*/
function getTokensBurned(
uint256 withdrawal
) public view override returns (uint256) {
return calculateDeltaOfFair(getReserveBalance(), -int256(withdrawal));
}
/* ========== MUTATIVE FUNCTIONS ========== */
/**
* @dev Allows minting of Fair tokens by depositing ETH for bonding in the contract on behalf of an address.
* During the Final phase, 100% of the deposited ETH are bonded.
* @param to The account receiving the Fair tokens
* @param tokenMinimum The minimum amount of tokens to be minted
*
* Requirements:
* - bonding ETH amount MUST NOT be zero
* - the minted Fair amount MUST NOT be less than parameter {tokenMinimum}
*/
function bondTo(
address to,
uint256 tokenMinimum
)
external
payable
override
onlyValidPhase(IFair.Phase.Final)
returns (uint256)
{
return _bondInternal(to, tokenMinimum, true);
}
/**
* @dev Allows minting of Fair tokens by depositing ETH for bonding in the contract.
* During the Final phase, 100% of the deposited ETH are bonded.
* @param tokenMinimum The minimum amount of tokens to be minted
*
* Requirements:
* - bonding ETH amount MUST NOT be zero
* - the minted Fair amount MUST NOT be less than parameter {tokenMinimum}
*/
function bond(
uint256 tokenMinimum
)
external
payable
override
onlyValidPhase(IFair.Phase.Final)
returns (uint256)
{
return _bondInternal(msg.sender, tokenMinimum, true);
}
/**
* @notice Bond and preserve Fair to be burnt or redistributed later
* @dev Allows minting of Fair tokens by depositing ETH for bonding in the contract.
*
* Requirements:
* - bonding ETH amount MUST NOT be zero
* - SHOULD NOT mint Fair
*/
function bondAndBurn()
external
payable
override
onlyOwnerOrNetworkOrClaims
{
uint256 tokensToBurn = _bondInternal(address(this), 0, true);
toBeBurntFair += tokensToBurn;
emit BondAndBurn(msg.sender, msg.value, tokensToBurn);
}
/**
* @notice Burn Fair allocated for burning
* @dev Owner function to burn Fair allocated for burning
*
* Requirements:
* - MUST be called the owner
* - Allocated tokens to be burnt {tobeBurntFair} MUST NOT be zero
**/
function burnAllocatedFair() external onlyOwner {
if (toBeBurntFair == 0) {
revert Fair_NothingToBurn();
}
super._burn(address(this), toBeBurntFair);
toBeBurntFair = 0;
}
/**
* @notice Burning / Repositioning of burnable amount either to pay claims and etc
* @dev transfer the amount allocated burned Fair to reposition for claims
* @param to The account to receive the allocated tokens for burning
*
* Requirements:
* - MUST be called by the owner
* - Allocated tokens to be burnt {tobeBurntFair} MUST NOT be zero
**/
function transferAllocatedFair(address to) external onlyOwner {
if (toBeBurntFair == 0) {
revert Fair_NothingToBurn();
}
_transfer(address(this), to, toBeBurntFair);
toBeBurntFair = 0;
}
/**
* @dev Allows minting of Fair tokens by depositing ETH for bonding in the contract.
*
* Requirements:
* - bonding ETH amount MUST NOT be zero
* - SHOULD NOT mint Fair
*/
function bondNoEmission() external payable override onlyOwnerOrNetwork {
uint256 notMinted = _bondInternal(msg.sender, 0, false);
emit BondWithoutMint(msg.sender, msg.value, notMinted);
}
// @dev view function to check the remaining amount of ETH that can be unbonded from the daily limit
function currentDailyUnbondableAmount()
public
view
override
returns (uint256)
{
return
getCapitalPool().mul(dailyUnbondedPercentageLimit) -
dailyUnbondedAmount;
}
// @dev view function to get the amount of ETH that can be unbonded from available capital pool
function capitalPoolAvailableFund() public view override returns (uint256) {
uint256 fShare = _getNetworkFShare();
uint256 capitalPool = getCapitalPool();
return capitalPool <= fShare ? 0 : capitalPool - fShare;
}
function _getNetworkFShare() internal view returns (uint256) {
return fairSideNetwork.getNetworkFShare();
}
// @dev view function for frontend to check the amount of ETH that can be unbonded
function getAvailableETHForWithdraw()
external
view
override
returns (uint256)
{
uint256 availableAmountFromCapitalPool = capitalPoolAvailableFund();
uint256 availableAmountFromDailyLimit = currentDailyUnbondableAmount();
return
availableAmountFromCapitalPool < availableAmountFromDailyLimit
? availableAmountFromCapitalPool
: availableAmountFromDailyLimit;
}
/**
* @dev Allows burning of Fair tokens for ETH
* It also takes cut of {tributeFee} and adds it as tribute which is distributed
* to the existing users of the network. currently hold in the tribute wallet
* @param capitalDesired the desired amount of ETH to distribute
* @param tokenMaximum the maximum acceptable amount of Fair tokens for burning
*
* Requirements:
* - the Fair token amount being burned MUST NOT exceed parameter {tokenMaximum}
* - the supply SHOULD reduce after the token burn
* - MUST be called in open bonding curve
*/
function unbond(
uint256 capitalDesired,
uint256 tokenMaximum
) external virtual nonReentrant onlyValidPhase(IFair.Phase.Final) {
uint256 curveCapital = getReserveBalance();
uint256 tokenAmount = calculateDeltaOfFair(
curveCapital,
-int256(capitalDesired)
);
if (tokenAmount > tokenMaximum) {
revert Fair_HighSlippage();
}
uint256 tribute = capitalDesired.mul(tributeFee);
uint256 reserveWithdrawn = capitalDesired - tribute;
uint256 curDate = getCurrentDate();
uint256 _dailyUnbondedAmount = dailyUnbondedAmount;
if (curDate != dailyUnbondedDate) {
dailyUnbondedDate = curDate;
_dailyUnbondedAmount = 0;
}
if (
_dailyUnbondedAmount + reserveWithdrawn >
getCapitalPool().mul(dailyUnbondedPercentageLimit)
) {
revert Fair_ExceedsCapitalPoolWithdrawLimit();
}
dailyUnbondedAmount += reserveWithdrawn;
uint256 mintTribute = calculateDeltaOfFair(
curveCapital - capitalDesired,
int256(tribute)
);
_burn(msg.sender, tokenAmount);
_mint(address(this), mintTribute);
_transfer(address(this), tributePool, mintTribute);
emit TributesDistributed(mintTribute);
payable(msg.sender).sendValue(reserveWithdrawn);
emit Unbond(msg.sender, capitalDesired, reserveWithdrawn, mintTribute);
}
/**
* @notice Get ETH amount and convert it to equivalent Fair tokens to be minted
* @dev Returns the amount of Fair tokens to be minted after considering the reserve and reserve delta
* @param ethAmount the amount of ETH to be converted into Fair tokens
*/
function estimateMintAmount(
uint256 ethAmount
) external view returns (uint256) {
return
calculateDeltaOfFair(
getReserveBalance() - ethAmount,
int256(ethAmount)
);
}
/* ========== RESTRICTED FUNCTIONS ========== */
/**
* @notice Advances the current bonding curve to the next phase
* @dev Admin function to move the current phase to the next phase.
*
* Requirements:
* - MUST be called by the owner
* - The current phase MUST NOT be in Final phase(open curve)
*/
function phaseAdvance() external onlyOwner {
if (currentPhase == Phase.Final) {
revert Fair_AlreadyAtFinalPhase();
}
unchecked {
currentPhase = Phase(uint8(currentPhase) + 1);
}
emit PhaseAdvanced(uint8(currentPhase));
}
/**
*
* @notice updates the currentPhase based on the passed Phase Enum value
* @param newPhase - Phase enumeration values 0 - 4
* @dev only Admin owner can call method
*
* Requirements
* - MUST be called by the owner
*/
function updateCurrentPhase(Phase newPhase) external onlyOwner {
currentPhase = Phase(newPhase);
emit PhaseUpdated(uint8(newPhase));
}
/**
* @dev Allows paying of claims upon processing of Cost Share Requests.
* It pays claims by performing account for later withdrawal of ETH by the {beneficiary}.
* @param beneficiary The address of the account to receive to receive payout
* @param amount The amount of ETH to pay
*
* Requirements:
* - MUST be called only by the {Claims} contract.
*/
function payClaim(
address beneficiary,
uint256 amount
) external onlyClaims onlyNonPaused {
payable(beneficiary).sendValue(amount);
}
/**
* @dev Sets tribute fee. The fee is expressed in percentage,
* being 1 ether = 100%
*
* Requirements:
* - Tribute fee SHOULD be less than 1 ether
* - MUST be called only by {owner}, {governance} or {timelock} contracts.
*/
function setTributeFee(uint256 _tributeFee) external onlyTimelock {
if (_tributeFee >= 1 ether) {
revert Fair_FeeShouldBeLessThanOneEther();
}
tributeFee = _tributeFee;
emit TributeFeeSet(_tributeFee);
}
function setMaxDepositPerTransaction(
uint256 _maxDepositPerTransaction
) external onlyTimelock {
maxDepositPerTransaction = _maxDepositPerTransaction;
}
function setDailyUnbondedPercentageLimit(
uint256 _dailyUnboondedPercentageLimit
) external onlyTimelock {
if (_dailyUnboondedPercentageLimit >= 1 ether) {
revert Fair_DailyUnBondedPercentageShouldBeLessThan100Percent();
}
dailyUnbondedPercentageLimit = _dailyUnboondedPercentageLimit;
}
/**
* @dev Allows updating of {tributePool} address.
* @param _tributePool the new address of the {tributePool} contract
*
* Requirements:
* - MUST be called only by {timelock} contract
* - {_tributePool} MUST NOT be a zero address value
*/
function setTributePool(address _tributePool) external onlyTimelock {
if (_tributePool == address(0)) {
revert Fair_AddressZero();
}
tributePool = _tributePool;
}
function setFundingPoolThreshold(
uint256 _fundingPoolThreshold
) external onlyTimelock {
FUNDING_POOL_THRESHOLD = _fundingPoolThreshold;
}
/**
* @dev Allows updating of {_fairSideNetwork} address. Invocable only once.
* @param _fairSideNetwork the new address of the {Network} contract
*
* Requirements:
* - MUST be called only by {owner} contract
* - {_fairSideNetwork} MUST NOT be a zero address value
*/
function setFairSideNetwork(
IFairSideNetwork _fairSideNetwork
) external onlyOwner {
if (address(_fairSideNetwork) == address(0)) {
revert Fair_AddressZero();
}
fairSideNetwork = _fairSideNetwork;
}
/**
* @dev Allows minting of Fair tokens by depositing ETH for bonding in the contract.
* During the Premine phase, 100% of the deposited ETH are bonded. The bonded amount is
* immediately vested according to the Fair vesting schedule.
*
* @param adminMultisigs list of addresses to receive tokens
* @param amounts list of numbers to send to {adminMultisigs}
*
* Requirements:
* - during Premine phase, only the token owner can mint to a group of users
* - during Premine phase, depositing any ether for bonding is disallowed
* - during Premine phase, the tokens that are minted are immediately vested
* - the size of {adminMultisigs} and {amounts} MUST be equal
*/
function mintPremineAdmin(
address[] calldata adminMultisigs,
uint256[] calldata amounts
) external override onlyOwner onlyValidPhase(IFair.Phase.Premine) {
uint256 length = adminMultisigs.length;
if (length != amounts.length) {
revert Fair_DifferentSizedArrays();
}
for (uint8 i; i < length; ++i) {
_mint(adminMultisigs[i], amounts[i]);
}
}
/**
* @dev Allows updating of {_fairSideClaims} address. Invocable only once.
* @param _fairSideClaims the new address of the {claims} contract
*
* Requirements:
* - MUST be called only by {owner} contract
* - {_fairSideClaims} MUST NOT be a zero address value
*/
function setFairSideClaims(address _fairSideClaims) external onlyOwner {
if (fairSideClaims != address(0)) {
revert Fair_AlreadySet();
}
fairSideClaims = _fairSideClaims;
}
/**
* @notice Get amount in capital pool (ETH)
* @dev : Capital Pool = Total Funds held in ETH – Open Cost Share Requests
* Open Cost Share Request = Cost share request awaiting assessor consensus
* @return uint256
*/
function getCapitalPool() public view returns (uint256) {
uint256 totalOpenRequests = fairSideNetwork.getTotalOpenRequests();
return getReserveBalance() - totalOpenRequests;
}
/**
* @dev Allows pausing fair contract.
*
* Requirements:
* - MUST be called only by {owner} contract
*/
function togglePause() external onlyOwner {
paused = !paused;
emit PauseToggled();
}
/* ========== INTERNAL FUNCTIONS ========== */
/**
* @notice ABC wrapper, returns the change in Fair supply upon
* the total reserves and change in reserves.
*
* @param _reserve The amount of tokens
* @param _reserveDelta The amount of tokens to change
*
* @return uint256
*/
function calculateDeltaOfFair(
uint256 _reserve,
int256 _reserveDelta
) internal view returns (uint256) {
uint256 openRequestsInEth = fairSideNetwork.getTotalOpenRequests();
uint256 fshare = _getNetworkFShare();
return
_calculateDeltaOfFair(
_reserve,
_reserveDelta,
openRequestsInEth,
fshare
);
}
/**
* @notice returns current date starting from unix epoch time
*
*/
function getCurrentDate() internal view returns (uint256) {
return block.timestamp / 1 days;
}
/**
* @dev Bond tokens to the curve
*
* @param to the address of the account to receive tokens
* @param tokenMinimum the minimum tokens to bond
* @param mintFair states whether {_mint} should be invoked or not
*
* @return uint256 the amount of tokens minted
*
* Requirements:
* - {tokenMinimum} MUST be greater than the tokens to be minted
*/
function _bondInternal(
address to,
uint256 tokenMinimum,
bool mintFair
) private nonReentrant returns (uint256) {
if (msg.value == 0) {
revert Fair_DepositAmountCannotBeZero();
}
uint256 amountToBond = msg.value;
if (amountToBond > maxDepositPerTransaction) {
revert Fair_DepositAmountTooLarge();
}
uint256 tokensToBeMinted = calculateDeltaOfFair(
getReserveBalance() - msg.value,
int256(amountToBond)
);
if (tokensToBeMinted < tokenMinimum) {
revert Fair_HighSlippage();
}
uint256 fundingPoolBalance = FUNDING_POOL.balance;
//maintain a 500ETH on funding pool and bond the rest to the curve
if (fundingPoolBalance < FUNDING_POOL_THRESHOLD) {
amountToBond = amountToBond.mul(BONDING_CURVE_RATIO);
uint256 maxAllowedInFundingPool;
unchecked {
maxAllowedInFundingPool =
FUNDING_POOL_THRESHOLD -
fundingPoolBalance;
}
uint256 amountAfterBonding = msg.value - amountToBond;
uint256 toFundingPool = amountAfterBonding > maxAllowedInFundingPool
? maxAllowedInFundingPool
: amountAfterBonding;
payable(FUNDING_POOL).sendValue(toFundingPool);
}
if (mintFair) {
_mint(to, tokensToBeMinted);
}
//mint Fair if mintFair = true
amountBonded[to] += msg.value;
emit Bond(msg.sender, msg.value, tokensToBeMinted);
return tokensToBeMinted;
}
function estimateReceivedEth(
uint256 fairAmount
) external view override returns (uint256 fairInput, uint256 ethOut) {
return
EstimateEthInputLib.estimateReceivedEth(
this,
getCapitalPool(),
_getNetworkFShare(),
fairAmount
);
}
/**
* @dev See {ERC20-_beforeTokenTransfer}.
*
* Requirements:
*
* - the contract MUST NOT be paused.
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual override onlyNonPaused {
ERC20Upgradeable._beforeTokenTransfer(from, to, amount);
}
/* ========== MODIFIERS ========== */
modifier onlyTimelock() {
if (msg.sender != TIME_LOCK) {
revert Fair_OnlyTimelockCanCall();
}
_;
}
modifier onlyClaims() {
if (msg.sender != fairSideClaims) {
revert Fair_OnlyFairSideClaimsCanCall();
}
_;
}
modifier onlyFairNetwork() {
if (msg.sender != address(fairSideNetwork)) {
revert Fair_OnlyFairSideNetworkCanCall();
}
_;
}
modifier onlyOwner() {
if (
!IFairsideAdmin(fairSideAdmin).hasFSRole(
IFairsideAdmin.FSRoles.FS_FAIR_OWNER_ROLE,
msg.sender
)
) {
revert Fair_InvalidCaller();
}
_;
}
modifier onlyOwnerOrNetwork() {
if (msg.sender != address(fairSideNetwork)) {
if (
!IFairsideAdmin(fairSideAdmin).hasFSRole(
IFairsideAdmin.FSRoles.FS_FAIR_OWNER_ROLE,
msg.sender
)
) {
revert Fair_InvalidCaller();
}
}
_;
}
modifier onlyOwnerOrNetworkOrClaims() {
if (msg.sender != address(fairSideNetwork)) {
if (msg.sender != fairSideClaims) {
if (
!IFairsideAdmin(fairSideAdmin).hasFSRole(
IFairsideAdmin.FSRoles.FS_FAIR_OWNER_ROLE,
msg.sender
)
) {
revert Fair_InvalidCaller();
}
}
}
_;
}
modifier onlyValidPhase(IFair.Phase _phase) {
if (currentPhase != _phase) {
revert Fair_InvalidPhase();
}
_;
}
modifier onlyNonPaused() {
if (paused) {
revert Fair_Paused();
}
_;
}
function _authorizeUpgrade(
address newImplementation
) internal virtual override onlyOwner {}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "./Fair.sol";
interface IFairTestnetErrors {
/**
* @dev FairTestnet-related custom errors
*/
error FairTestnet_OnlyForSepoliaTestnet();
}
contract FairTestnet is Fair, IFairTestnetErrors {
using ABDKMathQuadUInt256 for uint256;
using AddressUpgradeable for address payable;
function initialize(
address _fundingPool,
address _timelock,
address _tributePool,
address _fairSideAdmin
) public override initializer {
// NOTE: not using super.initialize() because due to that if it is used, the contract size will be increased by 0.391KB
__ERC20_init("FairSide Token", "Fair");
__ERC20Burnable_init();
__ERC20Permit_init("Fair");
__ReentrancyGuard_init();
if (
_fundingPool == address(0) ||
_timelock == address(0) ||
_tributePool == address(0) ||
_fairSideAdmin == address(0)
) {
revert Fair_AddressZero();
}
FUNDING_POOL = _fundingPool;
TIME_LOCK = _timelock;
tributePool = _tributePool;
fairSideAdmin = _fairSideAdmin;
tributeFee = 0.035 ether;
dailyUnbondedPercentageLimit = 0.01 ether; // 1%
maxDepositPerTransaction = 50 ether;
FUNDING_POOL_THRESHOLD = 500 ether;
//@dev: This wrapper it's only for testnet or hardhat chain
if (block.chainid != 11155111 && block.chainid != 31337) {
revert FairTestnet_OnlyForSepoliaTestnet();
}
}
function withdrawFromCurve(
uint256 amount,
address destination
) external onlyTimelock {
payable(destination).sendValue(amount);
}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "./FairVestingBase.sol";
interface IFairSidePausableVestingErrors {
/**
* @dev FairSidePausableVesting-related custom errors
*/
error FSPausableVesting_VestingIsAlreadyPaused();
error FSPausableVesting_VestingIsNotPaused();
}
/**
* Fair Vesting for VC
*
* Attributes:
* - Fair token vesting over a period of time with a cliff
* - Allow users to vote with vested tokens
* - Vesting duration of 30 months with a 12-month cliff
* - 5% unlocked after the cliff period
*/
contract FairPausableVesting is
FairVestingBase,
IFairSidePausableVestingErrors
{
struct PausableVestingPeriod {
bool isPaused;
uint256 pauseStartTime;
uint256 totalPausedDuration;
}
mapping(address => PausableVestingPeriod) public pausableVestedAccount;
// An event emitted when a vesting is paused or unpaused for an account
event VestingPausedStateChanged(address indexed account, bool state);
constructor(
Fair _token
)
FairVestingBase(
_token,
12 * 30 days,
30 * 30 days,
5,
18 * 30 days,
IFair.Phase.Premine
)
{}
function calculateVestingClaim(
address account
) public view override returns (uint256) {
VestingPeriod storage vesting = vestedAccount[account];
PausableVestingPeriod storage pausableVesting = pausableVestedAccount[
account
];
uint256 vestedAmount;
if (!pausableVesting.isPaused) {
uint256 effectiveTime = block.timestamp -
pausableVesting.totalPausedDuration;
if (effectiveTime >= vesting.startTime + DURATION) {
vestedAmount = vesting.amountVested;
} else if (effectiveTime >= vesting.startTime + CLIFF) {
uint256 initialUnlockAmount = (vesting.amountVested *
INITIAL_UNLOCK) / 100;
uint256 timeAfterCliff = effectiveTime -
(vesting.startTime + CLIFF);
uint256 linearVestAmount = ((vesting.amountVested -
initialUnlockAmount) * timeAfterCliff) /
LINEAR_VEST_AFTER_CLIFF;
vestedAmount = initialUnlockAmount + linearVestAmount;
} else {
return 0;
}
}
uint256 claimableAmount = vestedAmount > vesting.claimed
? vestedAmount - vesting.claimed
: 0;
return claimableAmount;
}
/**
* @dev Triggers pausing of a vesting for an account
* @param account Address of the account to pause the vesting for
*/
function pauseVesting(address account) external onlyOwner {
PausableVestingPeriod storage pausableVesting = pausableVestedAccount[
account
];
if (pausableVesting.isPaused) {
revert FSPausableVesting_VestingIsAlreadyPaused();
}
pausableVesting.isPaused = true;
pausableVesting.pauseStartTime = block.timestamp;
emit VestingPausedStateChanged(account, true);
}
/**
* @dev Triggers unpausing of a vesting for an account
* @param account Address of the account to pause the vesting for
*/
function unpauseVesting(address account) external onlyOwner {
PausableVestingPeriod storage pausableVesting = pausableVestedAccount[
account
];
if (!pausableVesting.isPaused) {
revert FSPausableVesting_VestingIsNotPaused();
}
pausableVesting.isPaused = false;
pausableVesting.totalPausedDuration +=
block.timestamp -
pausableVesting.pauseStartTime;
pausableVesting.pauseStartTime = 0;
emit VestingPausedStateChanged(account, false);
}
function _onlyValidPhase() internal view override {}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "./FairVestingBase.sol";
/**
* Fair Vesting Ambassador
*
* Attributes:
* - Fair token vesting over a period of time with Full unlock at 12 month
*/
// solhint-disable not-rely-on-time, var-name-mixedcase, reason-string /*
contract FairVestingAmbassador is FairVestingBase {
// One month in seconds
uint256 private constant ONE_MONTH = 30 days;
// Duration of a vest
uint256 private constant _DURATION = 12 * ONE_MONTH;
/* ========== CONSTRUCTOR ========== */
constructor(
Fair _token
) FairVestingBase(_token, 0, _DURATION, 0, 0, IFair.Phase.KOL) {}
/**
* @dev Calculate the vested tokens available to claim.
* @param account Address of the account to calculate the claim amount for
* @return claimableAmount amount of tokens available to claim from the vestedAmount
*/
function calculateVestingClaim(
address account
) public view override returns (uint256) {
VestingPeriod memory accountVesting = vestedAccount[account];
if (block.timestamp < accountVesting.startTime + DURATION) return 0;
if (accountVesting.claimed >= accountVesting.amountVested) return 0;
return accountVesting.amountVested - accountVesting.claimed;
}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "../interfaces/vesting/IFairVesting.sol";
import "../dependencies/FSOwnable.sol";
import "../token/Fair.sol";
interface IFairSideVestingBaseErrors {
/**
* @dev FairSideVestingBase-related custom errors
*/
error FSVestingBase_NotEnoughFair();
error FSVestingBase_NotEnoughAllowance();
error FSVestingBase_NoVestingCreated();
error FSVestingBase_LessThanClaimed();
error FSVestingBase_DifferentSizedArrays();
error FSVestingBase_ZeroClaimableTokens();
error FSVestingBase_AlreadyHasVesting();
error FSVestingBase_CallerIsNotTheVestingBeneficiary();
error FSVestingBase_InvalidPhase();
}
/**
* Fair Vesting Base
*
* Attributes:
* - Fair token vesting over a period of time with a cliff
* - Allow users to vote with vested tokens
*/
contract FairVestingBase is
IFairVesting,
IFairSideVestingBaseErrors,
FSOwnable
{
/* ========== STATE VARIABLES ========== */
// Cliff period for a vest
uint256 internal immutable CLIFF;
// Duration of a vest
uint256 internal immutable DURATION;
// inital % to be unlocked when cliff is reached
uint256 internal immutable INITIAL_UNLOCK;
// Vesting duration after cliff
uint256 internal immutable LINEAR_VEST_AFTER_CLIFF;
// Valid phase for vesting
IFair.Phase public immutable VALID_PHASE;
//total Fair vested in contract
uint256 public totalVestedTokens;
//total Fair claimed in contract
uint256 public totalClaimedTokens;
/// @param claimed Amount of Fair claimed from the vesting contract
/// @param amountVested of Fair that are vested
/// @param startTime Vesting start timestamp in seconds
/// @param lastClaimAt Latest claim timestamp in seconds
struct VestingPeriod {
uint256 claimed;
uint256 amountVested;
uint256 startTime;
uint256 lastClaimAt;
}
mapping(address => VestingPeriod) public vestedAccount;
// FairSide Token address
Fair public immutable fair;
/* ========== EVENTS ========== */
// An event emitted when a user claims their vested tokens
event TokensClaimed(
address indexed beneficiary,
uint256 amount,
uint256 claimedAt
);
// An event emitted when admin vests tokens to address
event VestingCreated(address indexed account, uint256 amount);
/* ========== CONSTRUCTOR ========== */
constructor(
Fair _token,
uint256 _cliff,
uint256 _duration,
uint256 _initialLock,
uint256 _linearVestAfterCliff,
IFair.Phase _phase
) {
fair = _token;
CLIFF = _cliff;
DURATION = _duration;
INITIAL_UNLOCK = _initialLock;
LINEAR_VEST_AFTER_CLIFF = _linearVestAfterCliff;
VALID_PHASE = _phase;
}
function getTotalVestedTokens() external view override returns (uint256) {
return totalVestedTokens;
}
function getTotalClaimedTokens() external view override returns (uint256) {
return totalClaimedTokens;
}
function getUnclaimedTokens() external view override returns (int256) {
return int256(totalVestedTokens - totalClaimedTokens);
}
/**
* @dev returns current amount that has NOT been claimed from the vestedAmount
* amountVested - ClaimedAmount
*/
function getCurrentVestingAmount(
address account
) external view override returns (uint256) {
return
vestedAccount[account].amountVested -
vestedAccount[account].claimed;
}
/**
* @dev returns the total amount vested in contract for {account}
*/
function getVestedAmount(
address account
) external view override returns (uint256) {
return vestedAccount[account].amountVested;
}
/**
* @dev creates vesting for an account
* @param account Address of the user to create vesting for
* @param _amount Amount of tokens to be vested
*/
function createVesting(
address account,
uint256 _amount
) external override onlyOwner onlyValidPhase {
if (fair.balanceOf(msg.sender) < _amount) {
revert FSVestingBase_NotEnoughFair();
}
if (fair.allowance(msg.sender, address(this)) < _amount) {
revert FSVestingBase_NotEnoughAllowance();
}
_createVesting(account, _amount);
}
/**
* @dev Increase the amount of vested tokens for an account
* @param account Address of the user to increase the vested amount for
* @param _amount Amount of tokens to increase the vested amount by
*/
function increaseAccountVesting(
address account,
uint256 _amount
) external override onlyOwner onlyValidPhase {
if (vestedAccount[account].amountVested == 0) {
revert FSVestingBase_NoVestingCreated();
}
if (fair.allowance(msg.sender, address(this)) < _amount) {
revert FSVestingBase_NotEnoughAllowance();
}
vestedAccount[account].amountVested += _amount;
totalVestedTokens += _amount;
fair.transferFrom(msg.sender, address(this), _amount);
}
/**
* @dev Reduce the amount of vested tokens for an account
* @param account Address of the user to decrease the vested amount for
* @param _amount Amount of tokens to decrease the vested amount by
*/
function decreaseAccountVesting(
address account,
uint256 _amount
) external override onlyOwner onlyValidPhase {
if (vestedAccount[account].amountVested == 0) {
revert FSVestingBase_NoVestingCreated();
}
if (
vestedAccount[account].amountVested <
vestedAccount[account].claimed + _amount
) {
revert FSVestingBase_LessThanClaimed();
}
vestedAccount[account].amountVested -= _amount;
totalVestedTokens -= _amount;
fair.transfer(msg.sender, _amount);
}
/**
* @dev Create multiple vesting contracts in one call
* @param accounts An array of address of the users to create vesting for
* @param _amounts An array of amounts of tokens to be vested for each address
*/
function batchCreateVesting(
address[] calldata accounts,
uint256[] calldata _amounts
) external override onlyOwner onlyValidPhase {
uint256 accountLength = accounts.length;
uint256 amountToVest;
if (accountLength != _amounts.length) {
revert FSVestingBase_DifferentSizedArrays();
}
//validate approval of amount to vest
for (uint8 i; i < accountLength; ++i) {
amountToVest = amountToVest + _amounts[i];
}
if (fair.balanceOf(msg.sender) < amountToVest) {
revert FSVestingBase_NotEnoughFair();
}
if (fair.allowance(msg.sender, address(this)) < amountToVest) {
revert FSVestingBase_NotEnoughAllowance();
}
for (uint8 i; i < accountLength; ++i) {
_createVesting(accounts[i], _amounts[i]);
}
}
/**
* @dev Calculate the vested tokens available to claim.
* @param account Address of the account to calculate the claim amount for
* @return claimableAmount the vested tokens available to claim
*/
function calculateVestingClaim(
address account
) public view virtual returns (uint256) {
uint256 vestedAmount;
VestingPeriod memory accountVesting = vestedAccount[account];
if (block.timestamp >= accountVesting.startTime + DURATION) {
vestedAmount = accountVesting.amountVested;
} else if (block.timestamp >= accountVesting.startTime + CLIFF) {
vestedAmount = (accountVesting.amountVested * INITIAL_UNLOCK) / 100; // 5% unlock
uint256 linearClaim = ((accountVesting.amountVested -
vestedAmount) *
(block.timestamp - (accountVesting.startTime + CLIFF))) /
LINEAR_VEST_AFTER_CLIFF;
vestedAmount = vestedAmount + linearClaim;
} else {
return 0;
}
if (accountVesting.claimed >= vestedAmount) return 0;
return vestedAmount - accountVesting.claimed;
}
/**
* @dev Allows a vesting beneficiary to claim the vested tokens.
*/
function claimVestedTokens() external override onlyVestedAccount {
uint256 tokenClaim = calculateVestingClaim(msg.sender);
if (tokenClaim <= 0) {
revert FSVestingBase_ZeroClaimableTokens();
}
vestedAccount[msg.sender].claimed += tokenClaim;
vestedAccount[msg.sender].lastClaimAt = block.timestamp;
totalClaimedTokens += tokenClaim;
fair.transfer(msg.sender, tokenClaim);
emit TokensClaimed(msg.sender, tokenClaim, block.timestamp);
}
/**
* @dev creates a vesting.
* @param account Address of the account to create vesting for
* @param _amount Amount of tokens to vest
*/
function _createVesting(address account, uint256 _amount) private {
VestingPeriod storage accountVesting = vestedAccount[account];
if (accountVesting.amountVested != 0) {
revert FSVestingBase_AlreadyHasVesting();
}
accountVesting.amountVested = _amount;
accountVesting.startTime = block.timestamp;
totalVestedTokens += _amount;
//takes amount from
fair.transferFrom(msg.sender, address(this), _amount);
emit VestingCreated(account, _amount);
}
/**
* @dev Throws if called by any account other than the vesting beneficiary.
*/
modifier onlyVestedAccount() {
if (vestedAccount[msg.sender].amountVested == 0) {
revert FSVestingBase_CallerIsNotTheVestingBeneficiary();
}
_;
}
function _onlyValidPhase() internal view virtual {
if (fair.currentPhase() != VALID_PHASE) {
revert FSVestingBase_InvalidPhase();
}
}
/**
* @dev only phase is permitted to create the vesting.
*/
modifier onlyValidPhase() {
_onlyValidPhase();
_;
}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "./FairVestingBase.sol";
/**
* Fair Vesting PRESEED
*
* Attributes:
* - Fair token vesting over a period of time with a cliff
* - Allow users to vote with vested tokens
* - Vesting duration of 30 months with a 12-month cliff
* - 5% unlocked after the 12 months cliff period
*/
contract FairVestingPreseed is FairVestingBase {
constructor(
Fair _token
)
FairVestingBase(
_token,
12 * 30 days,
30 * 30 days,
5,
18 * 30 days,
IFair.Phase.Premine
)
{}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "../interfaces/vesting/IFairVesting.sol";
import "../interfaces/vesting/IFairVestingRegistry.sol";
/**
* Fair Vesting Registry
*/
// solhint-disable not-rely-on-time, var-name-mixedcase, reason-string /*
contract FairVestingRegistry is
IFairVestingRegistry,
Initializable,
OwnableUpgradeable,
UUPSUpgradeable
{
IFairVesting public VestingPre;
IFairVesting public VestingVC;
IFairVesting public VestingAmbassador;
IFairVesting public VestingPausable;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(
IFairVesting _vestingPre,
IFairVesting _vestingVC,
IFairVesting _vestingAmbassador,
IFairVesting _vestingPausable
) public initializer {
__Ownable_init();
__UUPSUpgradeable_init();
VestingPre = _vestingPre;
VestingVC = _vestingVC;
VestingAmbassador = _vestingAmbassador;
VestingPausable = _vestingPausable;
}
/**
* @dev returns the total of vested amount for account in VestingPre, VestingVC and VestingAmbassador contracts contracts
* @param account address of the account
*/
function getTotalVestedAmount(
address account
) external view override returns (uint256) {
return (VestingPre.getVestedAmount(account) +
VestingAmbassador.getVestedAmount(account) +
VestingVC.getVestedAmount(account) +
VestingPausable.getVestedAmount(account));
}
/**
* @dev returns the current amount that has NOT been claimed from the vestedAmount for account in VestingPre, VestingVC and VestingAmbassador contracts
* @param account address of the account
*/
function getCurrentVesting(
address account
) external view override returns (uint256) {
return (VestingPre.getCurrentVestingAmount(account) +
VestingAmbassador.getCurrentVestingAmount(account) +
VestingVC.getCurrentVestingAmount(account) +
VestingPausable.getCurrentVestingAmount(account));
}
/**
* @dev returns total amount of vested tokens claimed from VestingPre, VestingVC and VestingAmbassador contracts
*/
function getTotalClaimedVesting() external view returns (uint256) {
return (VestingPre.getTotalClaimedTokens() +
VestingAmbassador.getTotalClaimedTokens() +
VestingVC.getTotalClaimedTokens() +
VestingPausable.getTotalClaimedTokens());
}
/**
* @dev returns total amount of vested tokens from VestingPre, VestingVC and VestingAmbassador contracts
*/
function getTotalVested() external view override returns (uint256) {
return (VestingPre.getTotalVestedTokens() +
VestingAmbassador.getTotalVestedTokens() +
VestingVC.getTotalVestedTokens() +
VestingPausable.getTotalVestedTokens());
}
/**
* @dev returns total amount of vested tokens that has NOT been claimed from VestingPre, VestingVC and VestingAmbassador contracts
*/
function getTotalUnclaimedVesting()
external
view
override
returns (int256)
{
return (VestingPre.getUnclaimedTokens() +
VestingAmbassador.getUnclaimedTokens() +
VestingVC.getUnclaimedTokens() +
VestingPausable.getUnclaimedTokens());
}
function version() external pure virtual returns (string memory) {
return "1.0";
}
function _authorizeUpgrade(
address newImplementation
) internal override onlyOwner {}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "./FairVestingBase.sol";
/**
* Fair Vesting for VC
*
* Attributes:
* - Fair token vesting over a period of time with a cliff
* - Allow users to vote with vested tokens
* - Vesting duration of 24 months with a 12-month cliff
* - 5% unlocked after the cliff period
*/
contract FairVestingVC is FairVestingBase {
constructor(
Fair _token
)
FairVestingBase(
_token,
12 * 30 days,
24 * 30 days,
5,
12 * 30 days,
IFair.Phase.VCWL
)
{}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import "../interfaces/network/IFairSideNetwork.sol";
import "../interfaces/zap/IFairSidePurchaseProxy.sol";
import "../interfaces/dependencies/IWETH.sol";
import "./ZapperBase.sol";
contract UniV2Zapper is ZapperBase, ReentrancyGuard {
using SafeERC20 for IERC20;
using Address for address;
error Zapper_InvalidSwapRouter();
error Zapper_InvalidWeth();
error Zapper_InsufficientBalance();
IUniswapV2Router02 public swapRouter;
address public weth;
constructor(
address _fairsideAdmin,
address _purchaseProxy,
address _network,
address _supportedToken,
address _swapRouter,
address _weth
) ZapperBase(_fairsideAdmin, _purchaseProxy, _network, _supportedToken) {
_setSwapRouter(_swapRouter);
_setWeth(_weth);
}
function _setWeth(address _weth) private {
if (_weth == address(0)) revert Zapper_InvalidWeth();
weth = _weth;
}
function _setSwapRouter(address _swapRouter) private {
if (_swapRouter == address(0)) revert Zapper_InvalidSwapRouter();
swapRouter = IUniswapV2Router02(_swapRouter);
}
function setWeth(address _weth) external onlyAdmin {
_setWeth(_weth);
}
function setSwapRouter(address _swapRouter) external onlyAdmin {
_setSwapRouter(_swapRouter);
}
function zap(
address supportedToken,
uint256 coverAmount,
uint256 amountInMaximum,
uint256 membershipTypeId,
address coverAddress
) external override isSupportedToken(supportedToken) nonReentrant {
uint256 neededETH = network.estimateCost(
coverAmount,
0,
membershipTypeId
);
uint256 requiredAmountIn = swapExactOutputSingle(
supportedToken,
neededETH,
amountInMaximum
);
if (address(this).balance < neededETH)
revert Zapper_InsufficientBalance();
// send eth to network through purchase proxy
purchaseProxy.purchaseMembershipFromProxy{value: neededETH}(
coverAmount,
membershipTypeId,
msg.sender,
coverAddress
);
emit ZapSuccess(
msg.sender,
supportedToken,
coverAmount,
membershipTypeId,
coverAddress,
neededETH,
requiredAmountIn,
amountInMaximum
);
}
function swapExactOutputSingle(
address supportedToken,
uint256 amountOut,
uint256 amountInMaximum
) private returns (uint256 amountIn) {
address[] memory path = new address[](2);
path[0] = supportedToken;
path[1] = weth;
// Transfer the specified amount of supportedToken to this contract.
TransferHelper.safeTransferFrom(
supportedToken,
msg.sender,
address(this),
amountInMaximum
);
// Approve the router to spend the specifed `amountInMaximum` of supportedToken.
TransferHelper.safeApprove(
supportedToken,
address(swapRouter),
amountInMaximum
);
uint256[] memory amounts = swapRouter.swapTokensForExactETH(
amountOut,
amountInMaximum,
path,
address(this),
block.timestamp + 1
);
amountIn = amounts[0];
// For exact output swaps, the amountInMaximum may not have all been spent.
// If the actual amount spent (amountIn) is less than the specified maximum amount, we must refund the msg.sender and approve the swapRouter to spend 0.
if (amountIn < amountInMaximum) {
TransferHelper.safeApprove(supportedToken, address(swapRouter), 0);
TransferHelper.safeTransfer(
supportedToken,
msg.sender,
amountInMaximum - amountIn
);
}
}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
import "./ZapperBase.sol";
import "../interfaces/dependencies/IV3SwapRouter.sol";
contract UniV3Zapper is ZapperBase, ReentrancyGuard {
error Zapper_InvalidFeeType();
error Zapper_InvalidWeth();
error Zapper_InvalidSwapRouter();
address public weth;
uint24 public feeType;
IV3SwapRouter public swapRouter;
constructor(
address _fairsideAdmin,
address _purchaseProxy,
address _network,
address _supportedToken,
address _swapRouter,
address _weth,
uint24 _feeType
) ZapperBase(_fairsideAdmin, _purchaseProxy, _network, _supportedToken) {
_setWeth(_weth);
_setFeeType(_feeType);
_setSwapRouter(_swapRouter);
}
function _setFeeType(uint24 _feeType) private {
if (_feeType != 500 && _feeType != 3000 && _feeType != 10000)
revert Zapper_InvalidFeeType();
feeType = _feeType;
}
function _setWeth(address _weth) private {
if (_weth == address(0)) revert Zapper_InvalidWeth();
weth = _weth;
}
function _setSwapRouter(address _swapRouter) private {
if (_swapRouter == address(0)) revert Zapper_InvalidSwapRouter();
swapRouter = IV3SwapRouter(_swapRouter);
}
function setFeeType(uint24 _feeType) external onlyAdmin {
_setFeeType(_feeType);
}
function setWeth(address _weth) external onlyAdmin {
_setWeth(_weth);
}
function setSwapRouter(address _swapRouter) external onlyAdmin {
_setSwapRouter(_swapRouter);
}
function zap(
address supportedToken,
uint256 coverAmount,
uint256 amountInMaximum,
uint256 membershipTypeId,
address coverAddress
) external override isSupportedToken(supportedToken) nonReentrant {
uint256 neededETH = network.estimateCost(
coverAmount,
0,
membershipTypeId
);
uint256 requiredAmountIn = swapExactOutputSingle(
supportedToken,
neededETH,
amountInMaximum
);
// unwrap weth to eth
IWETH(weth).withdraw(neededETH);
// send eth to network through purchase proxy
purchaseProxy.purchaseMembershipFromProxy{value: neededETH}(
coverAmount,
membershipTypeId,
msg.sender,
coverAddress
);
emit ZapSuccess(
msg.sender,
supportedToken,
coverAmount,
membershipTypeId,
coverAddress,
neededETH,
requiredAmountIn,
amountInMaximum
);
}
/// @notice swapExactOutputSingle swaps a minimum possible amount of DAI for a fixed amount of WETH.
/// @dev The calling address must approve this contract to spend its DAI for this function to succeed. As the amount of input DAI is variable,
/// the calling address will need to approve for a slightly higher amount, anticipating some variance.
/// @param amountOut The exact amount of WETH9 to receive from the swap.
/// @param amountInMaximum The amount of DAI we are willing to spend to receive the specified amount of WETH9.
/// @return amountIn The amount of DAI actually spent in the swap.
function swapExactOutputSingle(
address supportedToken,
uint256 amountOut,
uint256 amountInMaximum
) private returns (uint256 amountIn) {
// Transfer the specified amount of supportedToken to this contract.
TransferHelper.safeTransferFrom(
supportedToken,
msg.sender,
address(this),
amountInMaximum
);
// Approve the router to spend the specifed `amountInMaximum` of supportedToken.
// In production, you should choose the maximum amount to spend based on oracles or other data sources to archeive a better swap.
TransferHelper.safeApprove(
supportedToken,
address(swapRouter),
amountInMaximum
);
IV3SwapRouter.ExactOutputSingleParams memory params = IV3SwapRouter
.ExactOutputSingleParams({
tokenIn: supportedToken,
tokenOut: weth,
fee: feeType,
recipient: address(this),
amountOut: amountOut,
amountInMaximum: amountInMaximum,
sqrtPriceLimitX96: 0
});
// Executes the swap returning the amountIn needed to spend to receive the desired amountOut.
amountIn = swapRouter.exactOutputSingle(params);
// For exact output swaps, the amountInMaximum may not have all been spent.
// If the actual amount spent (amountIn) is less than the specified maximum amount, we must refund the msg.sender and approve the swapRouter to spend 0.
if (amountIn < amountInMaximum) {
TransferHelper.safeApprove(supportedToken, address(swapRouter), 0);
TransferHelper.safeTransfer(
supportedToken,
msg.sender,
amountInMaximum - amountIn
);
}
}
}// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.22;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "../interfaces/network/IFairSideNetwork.sol";
import "../interfaces/dependencies/IWETH.sol";
import "../interfaces/zap/IFairSidePurchaseProxy.sol";
import "../admin/IFairsideAdmin.sol";
contract ZapperBase {
using SafeERC20 for IERC20;
using Address for address;
event ZapSuccess(
address indexed user,
address indexed supportedToken,
uint256 coverAmount,
uint256 membershipTypeId,
address coverAddress,
uint256 neededETH,
uint256 requiredAmountIn,
uint256 amountInMaximum
);
error Zapper_InvalidToken();
error Zapper_InvalidNetwork();
error Zapper_InvalidFairsideAdmin();
error Zapper_InvalidPurchaseProxy();
error Zapper_InsufficientPrivilegesOnlyAdmin();
error Zapper_NotImplemented();
error Zapper_UnsupportedToken();
IFairSideNetwork public network;
IFairsideAdmin public fairsideAdmin;
IFairSidePurchaseProxy public purchaseProxy;
mapping(address => bool) public supportedTokens;
modifier onlyAdmin() {
if (
!fairsideAdmin.hasFSRole(
IFairsideAdmin.FSRoles.FS_ADMIN_ROLE,
msg.sender
)
) {
revert Zapper_InsufficientPrivilegesOnlyAdmin();
}
_;
}
modifier isSupportedToken(address _supportedToken) {
if (!supportedTokens[_supportedToken]) revert Zapper_UnsupportedToken();
_;
}
constructor(
address _fairsideAdmin,
address _purchaseProxy,
address _network,
address _supportedToken
) {
_setFairsideAdmin(_fairsideAdmin);
_setPurchaseProxy(_purchaseProxy);
_setNetwork(_network);
_setSupportedToken(_supportedToken, true);
}
function _setFairsideAdmin(address _fairsideAdmin) private {
if (_fairsideAdmin == address(0)) revert Zapper_InvalidFairsideAdmin();
fairsideAdmin = IFairsideAdmin(_fairsideAdmin);
}
function _setPurchaseProxy(address _purchaseProxy) private {
if (_purchaseProxy == address(0)) revert Zapper_InvalidPurchaseProxy();
purchaseProxy = IFairSidePurchaseProxy(_purchaseProxy);
}
function _setNetwork(address _network) private {
if (_network == address(0)) revert Zapper_InvalidNetwork();
network = IFairSideNetwork(_network);
}
function _setSupportedToken(
address _supportedToken,
bool _supported
) private {
if (_supportedToken == address(0)) revert Zapper_InvalidToken();
supportedTokens[_supportedToken] = _supported;
}
function setPurchaseProxy(address _purchaseProxy) external onlyAdmin {
_setPurchaseProxy(_purchaseProxy);
}
function setSupportedToken(
address _supportedToken,
bool _supported
) external onlyAdmin {
_setSupportedToken(_supportedToken, _supported);
}
receive() external payable {}
function zap(address, uint256, uint256, uint256, address) external virtual {
revert Zapper_NotImplemented();
}
}{
"evmVersion": "paris",
"optimizer": {
"enabled": true,
"mode": "3",
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"abi"
]
}
},
"detectMissingLibraries": false,
"forceEVMLA": false,
"enableEraVMExtensions": false,
"libraries": {
"contracts/dependencies/FairSideFormula2.sol": {
"FairSideFormula2": "0x4D036202f75B8ed506cea871095227B4b0A0EF11"
},
"contracts/dependencies/EstimateEthInputLib.sol": {
"EstimateEthInputLib": "0xC22349B1dabA46a973FFC5fE2692Db985dfC823D"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_network","type":"address"},{"internalType":"address","name":"_fairsideAdmin","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"PurchaseProxy_InsufficientPrivilegesOnlyAdmin","type":"error"},{"inputs":[],"name":"PurchaseProxy_InvalidAddress","type":"error"},{"inputs":[],"name":"PurchaseProxy_NotAllowedAddress","type":"error"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"allowedAddresses","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fairsideAdmin","outputs":[{"internalType":"contract IFairsideAdmin","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"network","outputs":[{"internalType":"contract IFairSideNetwork","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"coverAmount","type":"uint256"},{"internalType":"uint256","name":"membershipTypeId","type":"uint256"},{"internalType":"address","name":"primaryAddress","type":"address"},{"internalType":"address","name":"coverAddress","type":"address"}],"name":"purchaseMembershipFromProxy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"primaryAddress","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"setAllowedAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_fairsideAdmin","type":"address"}],"name":"setFairsideAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_network","type":"address"}],"name":"setNetwork","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
9c4d535b0000000000000000000000000000000000000000000000000000000000000000010000bb2996f95e3044636debcce55141c836229fc9f40b1298e2226ed8885e0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004000000000000000000000000017ad4490a9181cf01f2be059765d4f2d8ee6d8a000000000000000000000000019c2f354280490b99c4d72bb849b08b42bb5d520
Deployed Bytecode
0x000100000000000200030000000000020000000000010355000000600310027000000098033001970000000100200190000000650000c13d0000008002000039000000400020043f000000040030008c000001dd0000413d000000000201043b000000e002200270000000a00020009c0000009d0000a13d000000a10020009c000000a80000213d000000a40020009c000001050000613d000000a50020009c000001dd0000c13d000000240030008c000001dd0000413d0000000002000416000000000002004b000001dd0000c13d0000000401100370000000000101043b0000009b0010009c000001dd0000213d000300000001001d0000000102000039000000000302041a000000a901000041000000800010043f000000840020043f00000000010004110000009b01100197000000a40010043f0000000001000414000000980010009c0000009801008041000000c001100210000000aa011001c7000200000003001d0000009b02300197025b02560000040f00000060031002700000009803300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000080057001bf0000003e0000613d0000008008000039000000000901034f000000009a09043c0000000008a80436000000000058004b0000003a0000c13d000000000006004b0000004b0000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f00000000006504350000000100200190000001940000613d0000001f01400039000000600110018f00000080011001bf000000400010043f000000200030008c0000000304000029000001dd0000413d000000800200043d000000000002004b0000000003000039000000010300c039000000000032004b000001dd0000c13d000000000002004b0000014b0000613d000000000004004b000001e10000613d00000002010000290000009c01100197000000000141019f0000000102000039000000000012041b00000000010000190000025c0001042e0000000002000416000000000002004b000001dd0000c13d0000001f0230003900000099022001970000008002200039000000400020043f0000001f0430018f0000009a053001980000008002500039000000760000613d0000008006000039000000000701034f000000007807043c0000000006860436000000000026004b000000720000c13d000000000004004b000000830000613d000000000151034f0000000304400210000000000502043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f0000000000120435000000400030008c000001dd0000413d000000800100043d0000009b0010009c000001dd0000213d000000a00200043d0000009b0020009c000001dd0000213d000000000001004b0000018c0000613d000000000002004b0000018c0000613d000000000300041a0000009c03300197000000000113019f000000000010041b0000000101000039000000000301041a0000009c03300197000000000223019f000000000021041b0000002001000039000001000010044300000120000004430000009d010000410000025c0001042e000000a60020009c0000014d0000613d000000a70020009c0000016e0000613d000000a80020009c000001dd0000c13d0000000001000416000000000001004b000001dd0000c13d000000000100041a000001880000013d000000a20020009c000001830000613d000000a30020009c000001dd0000c13d000000440030008c000001dd0000413d0000000002000416000000000002004b000001dd0000c13d0000000402100370000000000302043b0000009b0030009c000001dd0000213d0000002401100370000000000201043b000000000002004b0000000001000039000000010100c039000000000012004b000001dd0000c13d000300000003001d000200000002001d0000000101000039000000000201041a000000a903000041000000800030043f000000840010043f00000000010004110000009b01100197000000a40010043f00000000010004140000009b02200197000000980010009c0000009801008041000000c001100210000000aa011001c7025b02560000040f000000800a00003900000060031002700000009803300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000080057001bf000000dc0000613d000000000801034f000000008908043c000000000a9a043600000000005a004b000000d80000c13d000000000006004b000000e90000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f00000000006504350000000100200190000001ac0000613d0000001f01400039000000600110018f00000080011001bf000000400010043f000000200030008c0000000305000029000001dd0000413d000000800200043d000000000002004b0000000003000039000000010300c039000000000032004b000001dd0000c13d000000000002004b0000014b0000613d000000000050043f0000000201000039000000200010043f0000000001000019025b02400000040f000000000201041a000000b80220019700000002022001af000000000021041b00000000010000190000025c0001042e000000240030008c000001dd0000413d0000000002000416000000000002004b000001dd0000c13d0000000401100370000000000101043b0000009b0010009c000001dd0000213d000300000001001d0000000101000039000000000201041a000000a903000041000000800030043f000000840010043f00000000010004110000009b01100197000000a40010043f00000000010004140000009b02200197000000980010009c0000009801008041000000c001100210000000aa011001c7025b02560000040f00000060031002700000009803300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000080057001bf0000012d0000613d0000008008000039000000000901034f000000009a09043c0000000008a80436000000000058004b000001290000c13d000000000006004b0000013a0000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f00000000006504350000000100200190000001a00000613d0000001f01400039000000600110018f00000080011001bf000000400010043f000000200030008c0000000304000029000001dd0000413d000000800200043d000000000002004b0000000003000039000000010300c039000000000032004b000001dd0000c13d000000000002004b000001df0000c13d000000ab02000041000001e20000013d000000840030008c000001dd0000413d0000004402100370000000000202043b0000009b0020009c000001dd0000213d0000006401100370000000000101043b000300000001001d0000009b0010009c000001dd0000213d0000000001000411000000000010043f0000000201000039000000200010043f0000000001000414000000980010009c0000009801008041000000c001100210000000ad011001c7000200000002001d0000801002000039025b02560000040f0000000100200190000001dd0000613d000000000101043b000000000101041a000000ff00100190000001ca0000c13d000000b601000041000000800010043f000000b7010000410000025d00010430000000240030008c000001dd0000413d0000000002000416000000000002004b000001dd0000c13d0000000401100370000000000101043b0000009b0010009c000001dd0000213d000000000010043f0000000201000039000000200010043f0000000001000019025b02400000040f000000000101041a000000ff001001900000000001000039000000010100c039000000800010043f000000ac010000410000025c0001042e0000000001000416000000000001004b000001dd0000c13d0000000101000039000000000101041a0000009b01100197000000800010043f000000ac010000410000025c0001042e000000400100043d0000009e020000410000000000210435000000980010009c000000980100804100000040011002100000009f011001c70000025d000104300000001f0530018f0000009a06300198000000400200043d0000000004620019000001b70000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b0000019b0000c13d000001b70000013d0000001f0530018f0000009a06300198000000400200043d0000000004620019000001b70000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000001a70000c13d000001b70000013d0000001f0530018f0000009a06300198000000400200043d0000000004620019000001b70000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000001b30000c13d000000000005004b000001c40000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f00000000001404350000006001300210000000980020009c00000098020080410000004002200210000000000112019f0000025d00010430000000000100041a000000ae0200004100000000002004430000009b01100197000100000001001d00000004001004430000000001000414000000980010009c0000009801008041000000c001100210000000af011001c70000800202000039025b02560000040f0000000100200190000001e60000613d000000000101043b000000000001004b0000000201000029000001e70000c13d00000000010000190000025d00010430000000000004004b0000020a0000c13d0000009e02000041000000000021043500000040011002100000009f011001c70000025d00010430000000000001042f0000009b0110019700000003020000290000009b02200197000000400500043d000000b00300004100000000003504350000000403500039000000000013043500000000010003670000000403100370000000000303043b00000044045000390000000000240435000000240250003900000000003204350000002401100370000000000101043b00000064025000390000000000120435000000980050009c000300000005001d0000009801000041000000000105401900000040011002100000000002000414000000980020009c0000009802008041000000c002200210000000000112019f0000000003000416000000000003004b000002100000c13d000000b2011001c70000000102000029000002140000013d000000000100041a0000009c01100197000000000141019f000000000010041b00000000010000190000025c0001042e000000b1011001c7000080090200003900000001040000290000000005000019025b02510000040f0000000100200190000002200000613d0000000301000029000000b30010009c0000023c0000413d000000b401000041000000000010043f0000004101000039000000040010043f000000b5010000410000025d0001043000000060061002700000001f0460018f0000009a05600198000000400200043d00000000035200190000022c0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000038004b000002280000c13d0000009806600197000000000004004b0000023a0000613d000000000151034f0000000304400210000000000503043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f00000000001304350000006001600210000001c50000013d000000400010043f00000000010000190000025c0001042e000000000001042f0000000002000414000000980020009c0000009802008041000000c002200210000000980010009c00000098010080410000004001100210000000000121019f000000ad011001c70000801002000039025b02560000040f00000001002001900000024f0000613d000000000101043b000000000001042d00000000010000190000025d0001043000000254002104210000000102000039000000000001042d0000000002000019000000000001042d00000259002104230000000102000039000000000001042d0000000002000019000000000001042d0000025b000004320000025c0001042e0000025d000104300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff00000000000000000000000000000000000000000000000000000001ffffffe000000000000000000000000000000000000000000000000000000000ffffffe0000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000200000000000000000000000000000040000001000000000000000000cc2f111300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000a1d7114100000000000000000000000000000000000000000000000000000000f729383e00000000000000000000000000000000000000000000000000000000f729383f00000000000000000000000000000000000000000000000000000000fae3698600000000000000000000000000000000000000000000000000000000a1d7114200000000000000000000000000000000000000000000000000000000a46e0fe70000000000000000000000000000000000000000000000000000000018a89f64000000000000000000000000000000000000000000000000000000004120657a000000000000000000000000000000000000000000000000000000006739afca913bccdb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044000000800000000000000000da0c2f2100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000080000000000000000002000000000000000000000000000000000000400000000000000000000000001806aa1896bbf26568e884a7374b41e002500962caba6a15023a8d90e8508b830200000200000000000000000000000000000024000000000000000000000000c98e0503000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000084000000000000000000000000000000000000000000000000000000000000008400000000000000000000000000000000000000000000000000000000000000000000000100000000000000004e487b7100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000012af61cb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000800000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000ebfb738421232c8a36e7cf29ce9c3db098da6543dd553ab178d6179f4fe21847
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000017ad4490a9181cf01f2be059765d4f2d8ee6d8a000000000000000000000000019c2f354280490b99c4d72bb849b08b42bb5d520
-----Decoded View---------------
Arg [0] : _network (address): 0x17aD4490A9181cf01F2Be059765d4f2d8Ee6d8a0
Arg [1] : _fairsideAdmin (address): 0x19C2f354280490b99c4D72Bb849B08B42bb5d520
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000017ad4490a9181cf01f2be059765d4f2d8ee6d8a0
Arg [1] : 00000000000000000000000019c2f354280490b99c4d72bb849b08b42bb5d520
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.