Overview
S Balance
S Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
13258862 | 10 days ago | Contract Creation | 0 S |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
FleetCommanderRewardsManagerFactory
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 50 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {IFleetCommanderRewardsManagerFactory} from "../interfaces/IFleetCommanderRewardsManagerFactory.sol"; import {FleetCommanderRewardsManager} from "./FleetCommanderRewardsManager.sol"; contract FleetCommanderRewardsManagerFactory is IFleetCommanderRewardsManagerFactory { constructor() {} function createRewardsManager( address accessManager, address fleetCommander ) external returns (address) { FleetCommanderRewardsManager rewardsManager = new FleetCommanderRewardsManager( accessManager, fleetCommander ); emit RewardsManagerCreated(address(rewardsManager), fleetCommander); return address(rewardsManager); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol) pragma solidity ^0.8.20; import {IAccessControl} from "./IAccessControl.sol"; import {Context} from "../utils/Context.sol"; import {ERC165} from "../utils/introspection/ERC165.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ```solidity * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ```solidity * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} * to enforce additional security measures for this role. */ abstract contract AccessControl is Context, IAccessControl, ERC165 { struct RoleData { mapping(address account => bool) hasRole; bytes32 adminRole; } mapping(bytes32 role => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with an {AccessControlUnauthorizedAccount} error including the required role. */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual returns (bool) { return _roles[role].hasRole[account]; } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()` * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier. */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account` * is missing `role`. */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert AccessControlUnauthorizedAccount(account, role); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleGranted} event. */ function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleRevoked} event. */ function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `callerConfirmation`. * * May emit a {RoleRevoked} event. */ function renounceRole(bytes32 role, address callerConfirmation) public virtual { if (callerConfirmation != _msgSender()) { revert AccessControlBadConfirmation(); } _revokeRole(role, callerConfirmation); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual returns (bool) { if (!hasRole(role, account)) { _roles[role].hasRole[account] = true; emit RoleGranted(role, account, _msgSender()); return true; } else { return false; } } /** * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual returns (bool) { if (hasRole(role, account)) { _roles[role].hasRole[account] = false; emit RoleRevoked(role, account, _msgSender()); return true; } else { return false; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol) pragma solidity ^0.8.20; /** * @dev External interface of AccessControl declared to support ERC-165 detection. */ interface IAccessControl { /** * @dev The `account` is missing a role. */ error AccessControlUnauthorizedAccount(address account, bytes32 neededRole); /** * @dev The caller of a function is not the expected one. * * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}. */ error AccessControlBadConfirmation(); /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role). * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `callerConfirmation`. */ function renounceRole(bytes32 role, address callerConfirmation) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol) pragma solidity ^0.8.20; /** * @dev Standard ERC-20 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens. */ interface IERC20Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC20InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC20InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. * @param spender Address that may be allowed to operate on tokens without being their owner. * @param allowance Amount of tokens a `spender` is allowed to operate with. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC20InvalidApprover(address approver); /** * @dev Indicates a failure with the `spender` to be approved. Used in approvals. * @param spender Address that may be allowed to operate on tokens without being their owner. */ error ERC20InvalidSpender(address spender); } /** * @dev Standard ERC-721 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens. */ interface IERC721Errors { /** * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20. * Used in balance queries. * @param owner Address of the current owner of a token. */ error ERC721InvalidOwner(address owner); /** * @dev Indicates a `tokenId` whose `owner` is the zero address. * @param tokenId Identifier number of a token. */ error ERC721NonexistentToken(uint256 tokenId); /** * @dev Indicates an error related to the ownership over a particular token. Used in transfers. * @param sender Address whose tokens are being transferred. * @param tokenId Identifier number of a token. * @param owner Address of the current owner of a token. */ error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC721InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC721InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param tokenId Identifier number of a token. */ error ERC721InsufficientApproval(address operator, uint256 tokenId); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC721InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC721InvalidOperator(address operator); } /** * @dev Standard ERC-1155 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens. */ interface IERC1155Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. * @param tokenId Identifier number of a token. */ error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC1155InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC1155InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param owner Address of the current owner of a token. */ error ERC1155MissingApprovalForAll(address operator, address owner); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC1155InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC1155InvalidOperator(address operator); /** * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. * Used in batch transfers. * @param idsLength Length of the array of token identifiers * @param valuesLength Length of the array of token amounts */ error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1363.sol) pragma solidity ^0.8.20; import {IERC20} from "./IERC20.sol"; import {IERC165} from "./IERC165.sol"; /** * @title IERC1363 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363]. * * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction. */ interface IERC1363 is IERC20, IERC165 { /* * Note: the ERC-165 identifier for this interface is 0xb0202a11. * 0xb0202a11 === * bytes4(keccak256('transferAndCall(address,uint256)')) ^ * bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^ * bytes4(keccak256('approveAndCall(address,uint256)')) ^ * bytes4(keccak256('approveAndCall(address,uint256,bytes)')) */ /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @param data Additional data with no specified format, sent in call to `spender`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "../utils/introspection/IERC165.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20Metadata.sol) pragma solidity ^0.8.20; import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol"; import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol"; /** * @dev Interface of the ERC-4626 "Tokenized Vault Standard", as defined in * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626]. */ interface IERC4626 is IERC20, IERC20Metadata { event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /** * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. * * - MUST be an ERC-20 token contract. * - MUST NOT revert. */ function asset() external view returns (address assetTokenAddress); /** * @dev Returns the total amount of the underlying asset that is “managed” by Vault. * * - SHOULD include any compounding that occurs from yield. * - MUST be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT revert. */ function totalAssets() external view returns (uint256 totalManagedAssets); /** * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToShares(uint256 assets) external view returns (uint256 shares); /** * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToAssets(uint256 shares) external view returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, * through a deposit call. * * - MUST return a limited value if receiver is subject to some deposit limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. * - MUST NOT revert. */ function maxDeposit(address receiver) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given * current on-chain conditions. * * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called * in the same transaction. * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the * deposit would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewDeposit(uint256 assets) external view returns (uint256 shares); /** * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * deposit execution, and are accounted for during deposit. * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function deposit(uint256 assets, address receiver) external returns (uint256 shares); /** * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. * - MUST return a limited value if receiver is subject to some mint limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. * - MUST NOT revert. */ function maxMint(address receiver) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given * current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the * same transaction. * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint * would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by minting. */ function previewMint(uint256 shares) external view returns (uint256 assets); /** * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint * execution, and are accounted for during mint. * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function mint(uint256 shares, address receiver) external returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the * Vault, through a withdraw call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST NOT revert. */ function maxWithdraw(address owner) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, * given current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if * called * in the same transaction. * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though * the withdrawal would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewWithdraw(uint256 assets) external view returns (uint256 shares); /** * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * withdraw execution, and are accounted for during withdraw. * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); /** * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, * through a redeem call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. * - MUST NOT revert. */ function maxRedeem(address owner) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, * given current on-chain conditions. * * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the * same transaction. * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the * redemption would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by redeeming. */ function previewRedeem(uint256 shares) external view returns (uint256 assets); /** * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * redeem execution, and are accounted for during redeem. * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "./IERC20.sol"; import {IERC20Metadata} from "./extensions/IERC20Metadata.sol"; import {Context} from "../../utils/Context.sol"; import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC-20 * applications. */ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { mapping(address account => uint256) private _balances; mapping(address account => mapping(address spender => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the default value returned by this function, unless * it's overridden. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `value`. */ function transfer(address to, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _transfer(owner, to, value); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, value); return true; } /** * @dev See {IERC20-transferFrom}. * * Skips emitting an {Approval} event indicating an allowance update. This is not * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve]. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `value`. * - the caller must have allowance for ``from``'s tokens of at least * `value`. */ function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, value); _transfer(from, to, value); return true; } /** * @dev Moves a `value` amount of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _transfer(address from, address to, uint256 value) internal { if (from == address(0)) { revert ERC20InvalidSender(address(0)); } if (to == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(from, to, value); } /** * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding * this function. * * Emits a {Transfer} event. */ function _update(address from, address to, uint256 value) internal virtual { if (from == address(0)) { // Overflow check required: The rest of the code assumes that totalSupply never overflows _totalSupply += value; } else { uint256 fromBalance = _balances[from]; if (fromBalance < value) { revert ERC20InsufficientBalance(from, fromBalance, value); } unchecked { // Overflow not possible: value <= fromBalance <= totalSupply. _balances[from] = fromBalance - value; } } if (to == address(0)) { unchecked { // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. _totalSupply -= value; } } else { unchecked { // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256. _balances[to] += value; } } emit Transfer(from, to, value); } /** * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). * Relies on the `_update` mechanism * * Emits a {Transfer} event with `from` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _mint(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(address(0), account, value); } /** * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. * Relies on the `_update` mechanism. * * Emits a {Transfer} event with `to` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead */ function _burn(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidSender(address(0)); } _update(account, address(0), value); } /** * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. * * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. */ function _approve(address owner, address spender, uint256 value) internal { _approve(owner, spender, value, true); } /** * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event. * * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any * `Approval` event during `transferFrom` operations. * * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to * true using the following override: * * ```solidity * function _approve(address owner, address spender, uint256 value, bool) internal virtual override { * super._approve(owner, spender, value, true); * } * ``` * * Requirements are the same as {_approve}. */ function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { if (owner == address(0)) { revert ERC20InvalidApprover(address(0)); } if (spender == address(0)) { revert ERC20InvalidSpender(address(0)); } _allowances[owner][spender] = value; if (emitEvent) { emit Approval(owner, spender, value); } } /** * @dev Updates `owner` s allowance for `spender` based on spent `value`. * * Does not update the allowance value in case of infinite allowance. * Revert if not enough allowance is available. * * Does not emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 value) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { if (currentAllowance < value) { revert ERC20InsufficientAllowance(spender, currentAllowance, value); } unchecked { _approve(owner, spender, currentAllowance - value, false); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC20Wrapper.sol) pragma solidity ^0.8.20; import {IERC20, IERC20Metadata, ERC20} from "../ERC20.sol"; import {SafeERC20} from "../utils/SafeERC20.sol"; /** * @dev Extension of the ERC-20 token contract to support token wrapping. * * Users can deposit and withdraw "underlying tokens" and receive a matching number of "wrapped tokens". This is useful * in conjunction with other modules. For example, combining this wrapping mechanism with {ERC20Votes} will allow the * wrapping of an existing "basic" ERC-20 into a governance token. * * WARNING: Any mechanism in which the underlying token changes the {balanceOf} of an account without an explicit transfer * may desynchronize this contract's supply and its underlying balance. Please exercise caution when wrapping tokens that * may undercollateralize the wrapper (i.e. wrapper's total supply is higher than its underlying balance). See {_recover} * for recovering value accrued to the wrapper. */ abstract contract ERC20Wrapper is ERC20 { IERC20 private immutable _underlying; /** * @dev The underlying token couldn't be wrapped. */ error ERC20InvalidUnderlying(address token); constructor(IERC20 underlyingToken) { if (underlyingToken == this) { revert ERC20InvalidUnderlying(address(this)); } _underlying = underlyingToken; } /** * @dev See {ERC20-decimals}. */ function decimals() public view virtual override returns (uint8) { try IERC20Metadata(address(_underlying)).decimals() returns (uint8 value) { return value; } catch { return super.decimals(); } } /** * @dev Returns the address of the underlying ERC-20 token that is being wrapped. */ function underlying() public view returns (IERC20) { return _underlying; } /** * @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens. */ function depositFor(address account, uint256 value) public virtual returns (bool) { address sender = _msgSender(); if (sender == address(this)) { revert ERC20InvalidSender(address(this)); } if (account == address(this)) { revert ERC20InvalidReceiver(account); } SafeERC20.safeTransferFrom(_underlying, sender, address(this), value); _mint(account, value); return true; } /** * @dev Allow a user to burn a number of wrapped tokens and withdraw the corresponding number of underlying tokens. */ function withdrawTo(address account, uint256 value) public virtual returns (bool) { if (account == address(this)) { revert ERC20InvalidReceiver(account); } _burn(_msgSender(), value); SafeERC20.safeTransfer(_underlying, account, value); return true; } /** * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake or acquired from * rebasing mechanisms. Internal function that can be exposed with access control if desired. */ function _recover(address account) internal virtual returns (uint256) { uint256 value = _underlying.balanceOf(address(this)) - totalSupply(); _mint(account, value); return value; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC4626.sol) pragma solidity ^0.8.20; import {IERC20, IERC20Metadata, ERC20} from "../ERC20.sol"; import {SafeERC20} from "../utils/SafeERC20.sol"; import {IERC4626} from "../../../interfaces/IERC4626.sol"; import {Math} from "../../../utils/math/Math.sol"; /** * @dev Implementation of the ERC-4626 "Tokenized Vault Standard" as defined in * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626]. * * This extension allows the minting and burning of "shares" (represented using the ERC-20 inheritance) in exchange for * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends * the ERC-20 standard. Any additional extensions included along it would affect the "shares" token represented by this * contract and not the "assets" token which is an independent contract. * * [CAUTION] * ==== * In empty (or nearly empty) ERC-4626 vaults, deposits are at high risk of being stolen through frontrunning * with a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may * similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by * verifying the amount received is as expected, using a wrapper that performs these checks such as * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router]. * * Since v4.9, this implementation introduces configurable virtual assets and shares to help developers mitigate that risk. * The `_decimalsOffset()` corresponds to an offset in the decimal representation between the underlying asset's decimals * and the vault decimals. This offset also determines the rate of virtual shares to virtual assets in the vault, which * itself determines the initial exchange rate. While not fully preventing the attack, analysis shows that the default * offset (0) makes it non-profitable even if an attacker is able to capture value from multiple user deposits, as a result * of the value being captured by the virtual shares (out of the attacker's donation) matching the attacker's expected gains. * With a larger offset, the attack becomes orders of magnitude more expensive than it is profitable. More details about the * underlying math can be found xref:erc4626.adoc#inflation-attack[here]. * * The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued * to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets * will cause the first user to exit to experience reduced losses in detriment to the last users that will experience * bigger losses. Developers willing to revert back to the pre-v4.9 behavior just need to override the * `_convertToShares` and `_convertToAssets` functions. * * To learn more, check out our xref:ROOT:erc4626.adoc[ERC-4626 guide]. * ==== */ abstract contract ERC4626 is ERC20, IERC4626 { using Math for uint256; IERC20 private immutable _asset; uint8 private immutable _underlyingDecimals; /** * @dev Attempted to deposit more assets than the max amount for `receiver`. */ error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max); /** * @dev Attempted to mint more shares than the max amount for `receiver`. */ error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max); /** * @dev Attempted to withdraw more assets than the max amount for `receiver`. */ error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max); /** * @dev Attempted to redeem more shares than the max amount for `receiver`. */ error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max); /** * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC-20 or ERC-777). */ constructor(IERC20 asset_) { (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_); _underlyingDecimals = success ? assetDecimals : 18; _asset = asset_; } /** * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way. */ function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool ok, uint8 assetDecimals) { (bool success, bytes memory encodedDecimals) = address(asset_).staticcall( abi.encodeCall(IERC20Metadata.decimals, ()) ); if (success && encodedDecimals.length >= 32) { uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256)); if (returnedDecimals <= type(uint8).max) { return (true, uint8(returnedDecimals)); } } return (false, 0); } /** * @dev Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This * "original" value is cached during construction of the vault contract. If this read operation fails (e.g., the * asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals. * * See {IERC20Metadata-decimals}. */ function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) { return _underlyingDecimals + _decimalsOffset(); } /** @dev See {IERC4626-asset}. */ function asset() public view virtual returns (address) { return address(_asset); } /** @dev See {IERC4626-totalAssets}. */ function totalAssets() public view virtual returns (uint256) { return _asset.balanceOf(address(this)); } /** @dev See {IERC4626-convertToShares}. */ function convertToShares(uint256 assets) public view virtual returns (uint256) { return _convertToShares(assets, Math.Rounding.Floor); } /** @dev See {IERC4626-convertToAssets}. */ function convertToAssets(uint256 shares) public view virtual returns (uint256) { return _convertToAssets(shares, Math.Rounding.Floor); } /** @dev See {IERC4626-maxDeposit}. */ function maxDeposit(address) public view virtual returns (uint256) { return type(uint256).max; } /** @dev See {IERC4626-maxMint}. */ function maxMint(address) public view virtual returns (uint256) { return type(uint256).max; } /** @dev See {IERC4626-maxWithdraw}. */ function maxWithdraw(address owner) public view virtual returns (uint256) { return _convertToAssets(balanceOf(owner), Math.Rounding.Floor); } /** @dev See {IERC4626-maxRedeem}. */ function maxRedeem(address owner) public view virtual returns (uint256) { return balanceOf(owner); } /** @dev See {IERC4626-previewDeposit}. */ function previewDeposit(uint256 assets) public view virtual returns (uint256) { return _convertToShares(assets, Math.Rounding.Floor); } /** @dev See {IERC4626-previewMint}. */ function previewMint(uint256 shares) public view virtual returns (uint256) { return _convertToAssets(shares, Math.Rounding.Ceil); } /** @dev See {IERC4626-previewWithdraw}. */ function previewWithdraw(uint256 assets) public view virtual returns (uint256) { return _convertToShares(assets, Math.Rounding.Ceil); } /** @dev See {IERC4626-previewRedeem}. */ function previewRedeem(uint256 shares) public view virtual returns (uint256) { return _convertToAssets(shares, Math.Rounding.Floor); } /** @dev See {IERC4626-deposit}. */ function deposit(uint256 assets, address receiver) public virtual returns (uint256) { uint256 maxAssets = maxDeposit(receiver); if (assets > maxAssets) { revert ERC4626ExceededMaxDeposit(receiver, assets, maxAssets); } uint256 shares = previewDeposit(assets); _deposit(_msgSender(), receiver, assets, shares); return shares; } /** @dev See {IERC4626-mint}. */ function mint(uint256 shares, address receiver) public virtual returns (uint256) { uint256 maxShares = maxMint(receiver); if (shares > maxShares) { revert ERC4626ExceededMaxMint(receiver, shares, maxShares); } uint256 assets = previewMint(shares); _deposit(_msgSender(), receiver, assets, shares); return assets; } /** @dev See {IERC4626-withdraw}. */ function withdraw(uint256 assets, address receiver, address owner) public virtual returns (uint256) { uint256 maxAssets = maxWithdraw(owner); if (assets > maxAssets) { revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets); } uint256 shares = previewWithdraw(assets); _withdraw(_msgSender(), receiver, owner, assets, shares); return shares; } /** @dev See {IERC4626-redeem}. */ function redeem(uint256 shares, address receiver, address owner) public virtual returns (uint256) { uint256 maxShares = maxRedeem(owner); if (shares > maxShares) { revert ERC4626ExceededMaxRedeem(owner, shares, maxShares); } uint256 assets = previewRedeem(shares); _withdraw(_msgSender(), receiver, owner, assets, shares); return assets; } /** * @dev Internal conversion function (from assets to shares) with support for rounding direction. */ function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) { return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding); } /** * @dev Internal conversion function (from shares to assets) with support for rounding direction. */ function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) { return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding); } /** * @dev Deposit/mint common workflow. */ function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual { // If _asset is ERC-777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the // assets are transferred and before the shares are minted, which is a valid state. // slither-disable-next-line reentrancy-no-eth SafeERC20.safeTransferFrom(_asset, caller, address(this), assets); _mint(receiver, shares); emit Deposit(caller, receiver, assets, shares); } /** * @dev Withdraw/redeem common workflow. */ function _withdraw( address caller, address receiver, address owner, uint256 assets, uint256 shares ) internal virtual { if (caller != owner) { _spendAllowance(owner, caller, shares); } // If _asset is ERC-777, `transfer` can trigger a reentrancy AFTER the transfer happens through the // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the // shares are burned and after the assets are transferred, which is a valid state. _burn(owner, shares); SafeERC20.safeTransfer(_asset, receiver, assets); emit Withdraw(caller, receiver, owner, assets, shares); } function _decimalsOffset() internal view virtual returns (uint8) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC-20 standard. */ 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 v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-20 standard as defined in the ERC. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC1363} from "../../../interfaces/IERC1363.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC-20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { /** * @dev An operation with an ERC-20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { safeTransfer(token, to, value); } else if (!token.transferAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferFromAndCallRelaxed( IERC1363 token, address from, address to, uint256 value, bytes memory data ) internal { if (to.code.length == 0) { safeTransferFrom(token, from, to, value); } else if (!token.transferFromAndCall(from, to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}. * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall} * once without retrying, and relies on the returned value to be true. * * Reverts if the returned value is other than `true`. */ function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { forceApprove(token, to, value); } else if (!token.approveAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements. */ function _callOptionalReturn(IERC20 token, bytes memory data) private { uint256 returnSize; uint256 returnValue; assembly ("memory-safe") { let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20) // bubble errors if iszero(success) { let ptr := mload(0x40) returndatacopy(ptr, 0, returndatasize()) revert(ptr, returndatasize()) } returnSize := returndatasize() returnValue := mload(0) } if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { bool success; uint256 returnSize; uint256 returnValue; assembly ("memory-safe") { success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20) returnSize := returndatasize() returnValue := mload(0) } return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; import {Errors} from "./Errors.sol"; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert Errors.InsufficientBalance(address(this).balance, amount); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert Errors.FailedCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {Errors.FailedCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert Errors.InsufficientBalance(address(this).balance, value); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case * of an unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {Errors.FailedCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly ("memory-safe") { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert Errors.FailedCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; /** * @dev Collection of common custom errors used in multiple contracts * * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library. * It is recommended to avoid relying on the error API for critical functionality. * * _Available since v5.1._ */ library Errors { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error InsufficientBalance(uint256 balance, uint256 needed); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedCall(); /** * @dev The deployment failed. */ error FailedDeployment(); /** * @dev A necessary precompile is missing. */ error MissingPrecompile(address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[ERC]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; 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 Branchless ternary evaluation for `a ? b : c`. Gas costs are constant. * * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone. * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute * one branch when needed, making this function more expensive. */ function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) { unchecked { // branchless ternary works because: // b ^ (a ^ b) == a // b ^ 0 == b return b ^ ((a ^ b) * SafeCast.toUint(condition)); } } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return ternary(a > b, a, b); } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return ternary(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 SafeCast.toUint(a > 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 // 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(ternary(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, except 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 Fermat's little theorem and get the * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}. */ 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 ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative. } } /** * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`. * * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that * `a**(p-2)` is the modular multiplicative inverse of a in Fp. * * NOTE: this function does NOT check that `p` is a prime greater than `2`. */ function invModPrime(uint256 a, uint256 p) internal view returns (uint256) { unchecked { return Math.modExp(a, p - 2, p); } } /** * @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 as 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); assembly ("memory-safe") { 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); assembly ("memory-safe") { 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.20; /** * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeCast { /** * @dev Value doesn't fit in an uint of `bits` size. */ error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value); /** * @dev An int value doesn't fit in an uint of `bits` size. */ error SafeCastOverflowedIntToUint(int256 value); /** * @dev Value doesn't fit in an int of `bits` size. */ error SafeCastOverflowedIntDowncast(uint8 bits, int256 value); /** * @dev An uint value doesn't fit in an int of `bits` size. */ error SafeCastOverflowedUintToInt(uint256 value); /** * @dev Returns the downcasted uint248 from uint256, reverting on * overflow (when the input is greater than largest uint248). * * Counterpart to Solidity's `uint248` operator. * * Requirements: * * - input must fit into 248 bits */ function toUint248(uint256 value) internal pure returns (uint248) { if (value > type(uint248).max) { revert SafeCastOverflowedUintDowncast(248, value); } return uint248(value); } /** * @dev Returns the downcasted uint240 from uint256, reverting on * overflow (when the input is greater than largest uint240). * * Counterpart to Solidity's `uint240` operator. * * Requirements: * * - input must fit into 240 bits */ function toUint240(uint256 value) internal pure returns (uint240) { if (value > type(uint240).max) { revert SafeCastOverflowedUintDowncast(240, value); } return uint240(value); } /** * @dev Returns the downcasted uint232 from uint256, reverting on * overflow (when the input is greater than largest uint232). * * Counterpart to Solidity's `uint232` operator. * * Requirements: * * - input must fit into 232 bits */ function toUint232(uint256 value) internal pure returns (uint232) { if (value > type(uint232).max) { revert SafeCastOverflowedUintDowncast(232, value); } return uint232(value); } /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits */ function toUint224(uint256 value) internal pure returns (uint224) { if (value > type(uint224).max) { revert SafeCastOverflowedUintDowncast(224, value); } return uint224(value); } /** * @dev Returns the downcasted uint216 from uint256, reverting on * overflow (when the input is greater than largest uint216). * * Counterpart to Solidity's `uint216` operator. * * Requirements: * * - input must fit into 216 bits */ function toUint216(uint256 value) internal pure returns (uint216) { if (value > type(uint216).max) { revert SafeCastOverflowedUintDowncast(216, value); } return uint216(value); } /** * @dev Returns the downcasted uint208 from uint256, reverting on * overflow (when the input is greater than largest uint208). * * Counterpart to Solidity's `uint208` operator. * * Requirements: * * - input must fit into 208 bits */ function toUint208(uint256 value) internal pure returns (uint208) { if (value > type(uint208).max) { revert SafeCastOverflowedUintDowncast(208, value); } return uint208(value); } /** * @dev Returns the downcasted uint200 from uint256, reverting on * overflow (when the input is greater than largest uint200). * * Counterpart to Solidity's `uint200` operator. * * Requirements: * * - input must fit into 200 bits */ function toUint200(uint256 value) internal pure returns (uint200) { if (value > type(uint200).max) { revert SafeCastOverflowedUintDowncast(200, value); } return uint200(value); } /** * @dev Returns the downcasted uint192 from uint256, reverting on * overflow (when the input is greater than largest uint192). * * Counterpart to Solidity's `uint192` operator. * * Requirements: * * - input must fit into 192 bits */ function toUint192(uint256 value) internal pure returns (uint192) { if (value > type(uint192).max) { revert SafeCastOverflowedUintDowncast(192, value); } return uint192(value); } /** * @dev Returns the downcasted uint184 from uint256, reverting on * overflow (when the input is greater than largest uint184). * * Counterpart to Solidity's `uint184` operator. * * Requirements: * * - input must fit into 184 bits */ function toUint184(uint256 value) internal pure returns (uint184) { if (value > type(uint184).max) { revert SafeCastOverflowedUintDowncast(184, value); } return uint184(value); } /** * @dev Returns the downcasted uint176 from uint256, reverting on * overflow (when the input is greater than largest uint176). * * Counterpart to Solidity's `uint176` operator. * * Requirements: * * - input must fit into 176 bits */ function toUint176(uint256 value) internal pure returns (uint176) { if (value > type(uint176).max) { revert SafeCastOverflowedUintDowncast(176, value); } return uint176(value); } /** * @dev Returns the downcasted uint168 from uint256, reverting on * overflow (when the input is greater than largest uint168). * * Counterpart to Solidity's `uint168` operator. * * Requirements: * * - input must fit into 168 bits */ function toUint168(uint256 value) internal pure returns (uint168) { if (value > type(uint168).max) { revert SafeCastOverflowedUintDowncast(168, value); } return uint168(value); } /** * @dev Returns the downcasted uint160 from uint256, reverting on * overflow (when the input is greater than largest uint160). * * Counterpart to Solidity's `uint160` operator. * * Requirements: * * - input must fit into 160 bits */ function toUint160(uint256 value) internal pure returns (uint160) { if (value > type(uint160).max) { revert SafeCastOverflowedUintDowncast(160, value); } return uint160(value); } /** * @dev Returns the downcasted uint152 from uint256, reverting on * overflow (when the input is greater than largest uint152). * * Counterpart to Solidity's `uint152` operator. * * Requirements: * * - input must fit into 152 bits */ function toUint152(uint256 value) internal pure returns (uint152) { if (value > type(uint152).max) { revert SafeCastOverflowedUintDowncast(152, value); } return uint152(value); } /** * @dev Returns the downcasted uint144 from uint256, reverting on * overflow (when the input is greater than largest uint144). * * Counterpart to Solidity's `uint144` operator. * * Requirements: * * - input must fit into 144 bits */ function toUint144(uint256 value) internal pure returns (uint144) { if (value > type(uint144).max) { revert SafeCastOverflowedUintDowncast(144, value); } return uint144(value); } /** * @dev Returns the downcasted uint136 from uint256, reverting on * overflow (when the input is greater than largest uint136). * * Counterpart to Solidity's `uint136` operator. * * Requirements: * * - input must fit into 136 bits */ function toUint136(uint256 value) internal pure returns (uint136) { if (value > type(uint136).max) { revert SafeCastOverflowedUintDowncast(136, value); } return uint136(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { if (value > type(uint128).max) { revert SafeCastOverflowedUintDowncast(128, value); } return uint128(value); } /** * @dev Returns the downcasted uint120 from uint256, reverting on * overflow (when the input is greater than largest uint120). * * Counterpart to Solidity's `uint120` operator. * * Requirements: * * - input must fit into 120 bits */ function toUint120(uint256 value) internal pure returns (uint120) { if (value > type(uint120).max) { revert SafeCastOverflowedUintDowncast(120, value); } return uint120(value); } /** * @dev Returns the downcasted uint112 from uint256, reverting on * overflow (when the input is greater than largest uint112). * * Counterpart to Solidity's `uint112` operator. * * Requirements: * * - input must fit into 112 bits */ function toUint112(uint256 value) internal pure returns (uint112) { if (value > type(uint112).max) { revert SafeCastOverflowedUintDowncast(112, value); } return uint112(value); } /** * @dev Returns the downcasted uint104 from uint256, reverting on * overflow (when the input is greater than largest uint104). * * Counterpart to Solidity's `uint104` operator. * * Requirements: * * - input must fit into 104 bits */ function toUint104(uint256 value) internal pure returns (uint104) { if (value > type(uint104).max) { revert SafeCastOverflowedUintDowncast(104, value); } return uint104(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits */ function toUint96(uint256 value) internal pure returns (uint96) { if (value > type(uint96).max) { revert SafeCastOverflowedUintDowncast(96, value); } return uint96(value); } /** * @dev Returns the downcasted uint88 from uint256, reverting on * overflow (when the input is greater than largest uint88). * * Counterpart to Solidity's `uint88` operator. * * Requirements: * * - input must fit into 88 bits */ function toUint88(uint256 value) internal pure returns (uint88) { if (value > type(uint88).max) { revert SafeCastOverflowedUintDowncast(88, value); } return uint88(value); } /** * @dev Returns the downcasted uint80 from uint256, reverting on * overflow (when the input is greater than largest uint80). * * Counterpart to Solidity's `uint80` operator. * * Requirements: * * - input must fit into 80 bits */ function toUint80(uint256 value) internal pure returns (uint80) { if (value > type(uint80).max) { revert SafeCastOverflowedUintDowncast(80, value); } return uint80(value); } /** * @dev Returns the downcasted uint72 from uint256, reverting on * overflow (when the input is greater than largest uint72). * * Counterpart to Solidity's `uint72` operator. * * Requirements: * * - input must fit into 72 bits */ function toUint72(uint256 value) internal pure returns (uint72) { if (value > type(uint72).max) { revert SafeCastOverflowedUintDowncast(72, value); } return uint72(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { if (value > type(uint64).max) { revert SafeCastOverflowedUintDowncast(64, value); } return uint64(value); } /** * @dev Returns the downcasted uint56 from uint256, reverting on * overflow (when the input is greater than largest uint56). * * Counterpart to Solidity's `uint56` operator. * * Requirements: * * - input must fit into 56 bits */ function toUint56(uint256 value) internal pure returns (uint56) { if (value > type(uint56).max) { revert SafeCastOverflowedUintDowncast(56, value); } return uint56(value); } /** * @dev Returns the downcasted uint48 from uint256, reverting on * overflow (when the input is greater than largest uint48). * * Counterpart to Solidity's `uint48` operator. * * Requirements: * * - input must fit into 48 bits */ function toUint48(uint256 value) internal pure returns (uint48) { if (value > type(uint48).max) { revert SafeCastOverflowedUintDowncast(48, value); } return uint48(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint40` operator. * * Requirements: * * - input must fit into 40 bits */ function toUint40(uint256 value) internal pure returns (uint40) { if (value > type(uint40).max) { revert SafeCastOverflowedUintDowncast(40, value); } return uint40(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { if (value > type(uint32).max) { revert SafeCastOverflowedUintDowncast(32, value); } return uint32(value); } /** * @dev Returns the downcasted uint24 from uint256, reverting on * overflow (when the input is greater than largest uint24). * * Counterpart to Solidity's `uint24` operator. * * Requirements: * * - input must fit into 24 bits */ function toUint24(uint256 value) internal pure returns (uint24) { if (value > type(uint24).max) { revert SafeCastOverflowedUintDowncast(24, value); } return uint24(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { if (value > type(uint16).max) { revert SafeCastOverflowedUintDowncast(16, value); } return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits */ function toUint8(uint256 value) internal pure returns (uint8) { if (value > type(uint8).max) { revert SafeCastOverflowedUintDowncast(8, value); } return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. */ function toUint256(int256 value) internal pure returns (uint256) { if (value < 0) { revert SafeCastOverflowedIntToUint(value); } return uint256(value); } /** * @dev Returns the downcasted int248 from int256, reverting on * overflow (when the input is less than smallest int248 or * greater than largest int248). * * Counterpart to Solidity's `int248` operator. * * Requirements: * * - input must fit into 248 bits */ function toInt248(int256 value) internal pure returns (int248 downcasted) { downcasted = int248(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(248, value); } } /** * @dev Returns the downcasted int240 from int256, reverting on * overflow (when the input is less than smallest int240 or * greater than largest int240). * * Counterpart to Solidity's `int240` operator. * * Requirements: * * - input must fit into 240 bits */ function toInt240(int256 value) internal pure returns (int240 downcasted) { downcasted = int240(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(240, value); } } /** * @dev Returns the downcasted int232 from int256, reverting on * overflow (when the input is less than smallest int232 or * greater than largest int232). * * Counterpart to Solidity's `int232` operator. * * Requirements: * * - input must fit into 232 bits */ function toInt232(int256 value) internal pure returns (int232 downcasted) { downcasted = int232(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(232, value); } } /** * @dev Returns the downcasted int224 from int256, reverting on * overflow (when the input is less than smallest int224 or * greater than largest int224). * * Counterpart to Solidity's `int224` operator. * * Requirements: * * - input must fit into 224 bits */ function toInt224(int256 value) internal pure returns (int224 downcasted) { downcasted = int224(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(224, value); } } /** * @dev Returns the downcasted int216 from int256, reverting on * overflow (when the input is less than smallest int216 or * greater than largest int216). * * Counterpart to Solidity's `int216` operator. * * Requirements: * * - input must fit into 216 bits */ function toInt216(int256 value) internal pure returns (int216 downcasted) { downcasted = int216(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(216, value); } } /** * @dev Returns the downcasted int208 from int256, reverting on * overflow (when the input is less than smallest int208 or * greater than largest int208). * * Counterpart to Solidity's `int208` operator. * * Requirements: * * - input must fit into 208 bits */ function toInt208(int256 value) internal pure returns (int208 downcasted) { downcasted = int208(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(208, value); } } /** * @dev Returns the downcasted int200 from int256, reverting on * overflow (when the input is less than smallest int200 or * greater than largest int200). * * Counterpart to Solidity's `int200` operator. * * Requirements: * * - input must fit into 200 bits */ function toInt200(int256 value) internal pure returns (int200 downcasted) { downcasted = int200(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(200, value); } } /** * @dev Returns the downcasted int192 from int256, reverting on * overflow (when the input is less than smallest int192 or * greater than largest int192). * * Counterpart to Solidity's `int192` operator. * * Requirements: * * - input must fit into 192 bits */ function toInt192(int256 value) internal pure returns (int192 downcasted) { downcasted = int192(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(192, value); } } /** * @dev Returns the downcasted int184 from int256, reverting on * overflow (when the input is less than smallest int184 or * greater than largest int184). * * Counterpart to Solidity's `int184` operator. * * Requirements: * * - input must fit into 184 bits */ function toInt184(int256 value) internal pure returns (int184 downcasted) { downcasted = int184(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(184, value); } } /** * @dev Returns the downcasted int176 from int256, reverting on * overflow (when the input is less than smallest int176 or * greater than largest int176). * * Counterpart to Solidity's `int176` operator. * * Requirements: * * - input must fit into 176 bits */ function toInt176(int256 value) internal pure returns (int176 downcasted) { downcasted = int176(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(176, value); } } /** * @dev Returns the downcasted int168 from int256, reverting on * overflow (when the input is less than smallest int168 or * greater than largest int168). * * Counterpart to Solidity's `int168` operator. * * Requirements: * * - input must fit into 168 bits */ function toInt168(int256 value) internal pure returns (int168 downcasted) { downcasted = int168(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(168, value); } } /** * @dev Returns the downcasted int160 from int256, reverting on * overflow (when the input is less than smallest int160 or * greater than largest int160). * * Counterpart to Solidity's `int160` operator. * * Requirements: * * - input must fit into 160 bits */ function toInt160(int256 value) internal pure returns (int160 downcasted) { downcasted = int160(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(160, value); } } /** * @dev Returns the downcasted int152 from int256, reverting on * overflow (when the input is less than smallest int152 or * greater than largest int152). * * Counterpart to Solidity's `int152` operator. * * Requirements: * * - input must fit into 152 bits */ function toInt152(int256 value) internal pure returns (int152 downcasted) { downcasted = int152(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(152, value); } } /** * @dev Returns the downcasted int144 from int256, reverting on * overflow (when the input is less than smallest int144 or * greater than largest int144). * * Counterpart to Solidity's `int144` operator. * * Requirements: * * - input must fit into 144 bits */ function toInt144(int256 value) internal pure returns (int144 downcasted) { downcasted = int144(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(144, value); } } /** * @dev Returns the downcasted int136 from int256, reverting on * overflow (when the input is less than smallest int136 or * greater than largest int136). * * Counterpart to Solidity's `int136` operator. * * Requirements: * * - input must fit into 136 bits */ function toInt136(int256 value) internal pure returns (int136 downcasted) { downcasted = int136(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(136, value); } } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits */ function toInt128(int256 value) internal pure returns (int128 downcasted) { downcasted = int128(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(128, value); } } /** * @dev Returns the downcasted int120 from int256, reverting on * overflow (when the input is less than smallest int120 or * greater than largest int120). * * Counterpart to Solidity's `int120` operator. * * Requirements: * * - input must fit into 120 bits */ function toInt120(int256 value) internal pure returns (int120 downcasted) { downcasted = int120(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(120, value); } } /** * @dev Returns the downcasted int112 from int256, reverting on * overflow (when the input is less than smallest int112 or * greater than largest int112). * * Counterpart to Solidity's `int112` operator. * * Requirements: * * - input must fit into 112 bits */ function toInt112(int256 value) internal pure returns (int112 downcasted) { downcasted = int112(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(112, value); } } /** * @dev Returns the downcasted int104 from int256, reverting on * overflow (when the input is less than smallest int104 or * greater than largest int104). * * Counterpart to Solidity's `int104` operator. * * Requirements: * * - input must fit into 104 bits */ function toInt104(int256 value) internal pure returns (int104 downcasted) { downcasted = int104(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(104, value); } } /** * @dev Returns the downcasted int96 from int256, reverting on * overflow (when the input is less than smallest int96 or * greater than largest int96). * * Counterpart to Solidity's `int96` operator. * * Requirements: * * - input must fit into 96 bits */ function toInt96(int256 value) internal pure returns (int96 downcasted) { downcasted = int96(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(96, value); } } /** * @dev Returns the downcasted int88 from int256, reverting on * overflow (when the input is less than smallest int88 or * greater than largest int88). * * Counterpart to Solidity's `int88` operator. * * Requirements: * * - input must fit into 88 bits */ function toInt88(int256 value) internal pure returns (int88 downcasted) { downcasted = int88(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(88, value); } } /** * @dev Returns the downcasted int80 from int256, reverting on * overflow (when the input is less than smallest int80 or * greater than largest int80). * * Counterpart to Solidity's `int80` operator. * * Requirements: * * - input must fit into 80 bits */ function toInt80(int256 value) internal pure returns (int80 downcasted) { downcasted = int80(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(80, value); } } /** * @dev Returns the downcasted int72 from int256, reverting on * overflow (when the input is less than smallest int72 or * greater than largest int72). * * Counterpart to Solidity's `int72` operator. * * Requirements: * * - input must fit into 72 bits */ function toInt72(int256 value) internal pure returns (int72 downcasted) { downcasted = int72(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(72, value); } } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits */ function toInt64(int256 value) internal pure returns (int64 downcasted) { downcasted = int64(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(64, value); } } /** * @dev Returns the downcasted int56 from int256, reverting on * overflow (when the input is less than smallest int56 or * greater than largest int56). * * Counterpart to Solidity's `int56` operator. * * Requirements: * * - input must fit into 56 bits */ function toInt56(int256 value) internal pure returns (int56 downcasted) { downcasted = int56(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(56, value); } } /** * @dev Returns the downcasted int48 from int256, reverting on * overflow (when the input is less than smallest int48 or * greater than largest int48). * * Counterpart to Solidity's `int48` operator. * * Requirements: * * - input must fit into 48 bits */ function toInt48(int256 value) internal pure returns (int48 downcasted) { downcasted = int48(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(48, value); } } /** * @dev Returns the downcasted int40 from int256, reverting on * overflow (when the input is less than smallest int40 or * greater than largest int40). * * Counterpart to Solidity's `int40` operator. * * Requirements: * * - input must fit into 40 bits */ function toInt40(int256 value) internal pure returns (int40 downcasted) { downcasted = int40(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(40, value); } } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits */ function toInt32(int256 value) internal pure returns (int32 downcasted) { downcasted = int32(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(32, value); } } /** * @dev Returns the downcasted int24 from int256, reverting on * overflow (when the input is less than smallest int24 or * greater than largest int24). * * Counterpart to Solidity's `int24` operator. * * Requirements: * * - input must fit into 24 bits */ function toInt24(int256 value) internal pure returns (int24 downcasted) { downcasted = int24(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(24, value); } } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits */ function toInt16(int256 value) internal pure returns (int16 downcasted) { downcasted = int16(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(16, value); } } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits */ function toInt8(int256 value) internal pure returns (int8 downcasted) { downcasted = int8(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(8, value); } } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive if (value > uint256(type(int256).max)) { revert SafeCastOverflowedUintToInt(value); } return int256(value); } /** * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump. */ function toUint(bool b) internal pure returns (uint256 u) { assembly ("memory-safe") { u := iszero(iszero(b)) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; /** * @dev Helper library for emitting standardized panic codes. * * ```solidity * contract Example { * using Panic for uint256; * * // Use any of the declared internal constants * function foo() { Panic.GENERIC.panic(); } * * // Alternatively * function foo() { Panic.panic(Panic.GENERIC); } * } * ``` * * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil]. * * _Available since v5.1._ */ // slither-disable-next-line unused-state library Panic { /// @dev generic / unspecified error uint256 internal constant GENERIC = 0x00; /// @dev used by the assert() builtin uint256 internal constant ASSERT = 0x01; /// @dev arithmetic underflow or overflow uint256 internal constant UNDER_OVERFLOW = 0x11; /// @dev division or modulo by zero uint256 internal constant DIVISION_BY_ZERO = 0x12; /// @dev enum conversion error uint256 internal constant ENUM_CONVERSION_ERROR = 0x21; /// @dev invalid encoding in storage uint256 internal constant STORAGE_ENCODING_ERROR = 0x22; /// @dev empty array pop uint256 internal constant EMPTY_ARRAY_POP = 0x31; /// @dev array out of bounds access uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32; /// @dev resource error (too large allocation or too large array) uint256 internal constant RESOURCE_ERROR = 0x41; /// @dev calling invalid internal function uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51; /// @dev Reverts with a panic code. Recommended to use with /// the internal constants with predefined codes. function panic(uint256 code) internal pure { assembly ("memory-safe") { mstore(0x00, 0x4e487b71) mstore(0x20, code) revert(0x1c, 0x24) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.20; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ```solidity * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position is the index of the value in the `values` array plus 1. // Position 0 is used to mean a value is not in the set. mapping(bytes32 value => uint256) _positions; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._positions[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We cache the value's position to prevent multiple reads from the same storage slot uint256 position = set._positions[value]; if (position != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 valueIndex = position - 1; uint256 lastIndex = set._values.length - 1; if (valueIndex != lastIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the lastValue to the index where the value to delete is set._values[valueIndex] = lastValue; // Update the tracked position of the lastValue (that was just moved) set._positions[lastValue] = position; } // Delete the slot where the moved value was stored set._values.pop(); // Delete the tracked position for the deleted slot delete set._positions[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._positions[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; assembly ("memory-safe") { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; assembly ("memory-safe") { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; assembly ("memory-safe") { result := store } return result; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {IAccessControlErrors} from "../interfaces/IAccessControlErrors.sol"; import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; /** * @title LimitedAccessControl * @dev This contract extends OpenZeppelin's AccessControl, disabling direct role granting and revoking. * It's designed to be used as a base contract for more specific access control implementations. * @dev This contract overrides the grantRole and revokeRole functions from AccessControl to disable direct role * granting and revoking. * @dev It doesn't override the renounceRole function, so it can be used to renounce roles for compromised accounts. */ abstract contract LimitedAccessControl is AccessControl, IAccessControlErrors { /** * @dev Overrides the grantRole function from AccessControl to disable direct role granting. * @notice This function always reverts with a DirectGrantIsDisabled error. */ function grantRole(bytes32, address) public view override { revert DirectGrantIsDisabled(msg.sender); } /** * @dev Overrides the revokeRole function from AccessControl to disable direct role revoking. * @notice This function always reverts with a DirectRevokeIsDisabled error. */ function revokeRole(bytes32, address) public view override { revert DirectRevokeIsDisabled(msg.sender); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {IAccessControlErrors} from "../interfaces/IAccessControlErrors.sol"; import {ContractSpecificRoles, IProtocolAccessManager} from "../interfaces/IProtocolAccessManager.sol"; import {ProtocolAccessManager} from "./ProtocolAccessManager.sol"; import {Context} from "@openzeppelin/contracts/utils/Context.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** * @title ProtocolAccessManaged * @notice This contract provides role-based access control functionality for protocol contracts * by interfacing with a central ProtocolAccessManager. * * @dev This contract is meant to be inherited by other protocol contracts that need * role-based access control. It provides modifiers and utilities to check various roles. * * The contract supports several key roles through modifiers: * 1. GOVERNOR_ROLE: System-wide administrators * 2. KEEPER_ROLE: Routine maintenance operators (contract-specific) * 3. SUPER_KEEPER_ROLE: Advanced maintenance operators (global) * 4. CURATOR_ROLE: Fleet-specific managers * 5. GUARDIAN_ROLE: Emergency response operators * 6. DECAY_CONTROLLER_ROLE: Specific role for decay management * 7. ADMIRALS_QUARTERS_ROLE: Specific role for admirals quarters bundler contract * * Usage: * - Inherit from this contract to gain access to role-checking modifiers * - Use modifiers like onlyGovernor, onlyKeeper, etc. to protect functions * - Access the internal _accessManager to perform custom role checks * * Security Considerations: * - The contract validates the access manager address during construction * - All role checks are performed against the immutable access manager instance * - Contract-specific roles are generated using the contract's address to prevent conflicts */ contract ProtocolAccessManaged is IAccessControlErrors, Context { /*////////////////////////////////////////////////////////////// CONSTANTS //////////////////////////////////////////////////////////////*/ /// @notice Role identifier for protocol governors - highest privilege level with admin capabilities bytes32 public constant GOVERNOR_ROLE = keccak256("GOVERNOR_ROLE"); /// @notice Role identifier for super keepers who can globally perform fleet maintanence roles bytes32 public constant SUPER_KEEPER_ROLE = keccak256("SUPER_KEEPER_ROLE"); /** * @notice Role identifier for protocol guardians * @dev Guardians have emergency powers across multiple protocol components: * - Can pause/unpause Fleet operations for security * - Can pause/unpause TipJar operations * - Can cancel governance proposals on SummerGovernor even if they don't meet normal cancellation requirements * - Can cancel TipJar proposals * * The guardian role serves as an emergency backstop to protect the protocol, but with less * privilege than governors. */ bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE"); /** * @notice Role identifier for decay controller * @dev This role allows the decay controller to manage the decay of user voting power */ bytes32 public constant DECAY_CONTROLLER_ROLE = keccak256("DECAY_CONTROLLER_ROLE"); /** * @notice Role identifier for admirals quarters bundler contract * @dev This role allows Admirals Quarters to unstake and withdraw assets from fleets, on behalf of users * @dev Withdrawn tokens go straight to users wallet, lowering the risk of manipulation if the role is compromised */ bytes32 public constant ADMIRALS_QUARTERS_ROLE = keccak256("ADMIRALS_QUARTERS_ROLE"); /*////////////////////////////////////////////////////////////// STATE VARIABLES //////////////////////////////////////////////////////////////*/ /// @notice The ProtocolAccessManager instance used for access control ProtocolAccessManager internal immutable _accessManager; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ /** * @notice Initializes the ProtocolAccessManaged contract * @param accessManager Address of the ProtocolAccessManager contract * @dev Validates the provided accessManager address and initializes the _accessManager */ constructor(address accessManager) { if (accessManager == address(0)) { revert InvalidAccessManagerAddress(address(0)); } if ( !IERC165(accessManager).supportsInterface( type(IProtocolAccessManager).interfaceId ) ) { revert InvalidAccessManagerAddress(accessManager); } _accessManager = ProtocolAccessManager(accessManager); } /*////////////////////////////////////////////////////////////// MODIFIERS //////////////////////////////////////////////////////////////*/ /** * @notice Modifier to restrict access to governors only * * @dev Modifier to check that the caller has the Governor role * @custom:internal-logic * - Checks if the caller has the GOVERNOR_ROLE in the access manager * @custom:effects * - Reverts if the caller doesn't have the GOVERNOR_ROLE * - Allows the function to proceed if the caller has the role * @custom:security-considerations * - Ensures that only authorized governors can access critical functions * - Relies on the correct setup of the access manager */ modifier onlyGovernor() { if (!_accessManager.hasRole(GOVERNOR_ROLE, msg.sender)) { revert CallerIsNotGovernor(msg.sender); } _; } /** * @notice Modifier to restrict access to keepers only * @dev Modifier to check that the caller has the Keeper role * @custom:internal-logic * - Checks if the caller has either the contract-specific KEEPER_ROLE or the SUPER_KEEPER_ROLE * @custom:effects * - Reverts if the caller doesn't have either of the required roles * - Allows the function to proceed if the caller has one of the roles * @custom:security-considerations * - Ensures that only authorized keepers can access maintenance functions * - Allows for both contract-specific and super keepers * @custom:gas-considerations * - Performs two role checks, which may impact gas usage */ modifier onlyKeeper() { if ( !_accessManager.hasRole( generateRole(ContractSpecificRoles.KEEPER_ROLE, address(this)), msg.sender ) && !_accessManager.hasRole(SUPER_KEEPER_ROLE, msg.sender) ) { revert CallerIsNotKeeper(msg.sender); } _; } /** * @notice Modifier to restrict access to super keepers only * @dev Modifier to check that the caller has the Super Keeper role * @custom:internal-logic * - Checks if the caller has the SUPER_KEEPER_ROLE in the access manager * @custom:effects * - Reverts if the caller doesn't have the SUPER_KEEPER_ROLE * - Allows the function to proceed if the caller has the role * @custom:security-considerations * - Ensures that only authorized super keepers can access advanced maintenance functions * - Relies on the correct setup of the access manager */ modifier onlySuperKeeper() { if (!_accessManager.hasRole(SUPER_KEEPER_ROLE, msg.sender)) { revert CallerIsNotSuperKeeper(msg.sender); } _; } /** * @notice Modifier to restrict access to curators only * @param fleetAddress The address of the fleet to check the curator role for * @dev Checks if the caller has the contract-specific CURATOR_ROLE */ modifier onlyCurator(address fleetAddress) { if ( fleetAddress == address(0) || !_accessManager.hasRole( generateRole(ContractSpecificRoles.CURATOR_ROLE, fleetAddress), msg.sender ) ) { revert CallerIsNotCurator(msg.sender); } _; } /** * @notice Modifier to restrict access to guardians only * @dev Modifier to check that the caller has the Guardian role * @custom:internal-logic * - Checks if the caller has the GUARDIAN_ROLE in the access manager * @custom:effects * - Reverts if the caller doesn't have the GUARDIAN_ROLE * - Allows the function to proceed if the caller has the role * @custom:security-considerations * - Ensures that only authorized guardians can access emergency functions * - Relies on the correct setup of the access manager */ modifier onlyGuardian() { if (!_accessManager.hasRole(GUARDIAN_ROLE, msg.sender)) { revert CallerIsNotGuardian(msg.sender); } _; } /** * @notice Modifier to restrict access to either guardians or governors * @dev Modifier to check that the caller has either the Guardian or Governor role * @custom:internal-logic * - Checks if the caller has either the GUARDIAN_ROLE or the GOVERNOR_ROLE * @custom:effects * - Reverts if the caller doesn't have either of the required roles * - Allows the function to proceed if the caller has one of the roles * @custom:security-considerations * - Ensures that only authorized guardians or governors can access certain functions * - Provides flexibility for functions that can be accessed by either role * @custom:gas-considerations * - Performs two role checks, which may impact gas usage */ modifier onlyGuardianOrGovernor() { if ( !_accessManager.hasRole(GUARDIAN_ROLE, msg.sender) && !_accessManager.hasRole(GOVERNOR_ROLE, msg.sender) ) { revert CallerIsNotGuardianOrGovernor(msg.sender); } _; } /** * @notice Modifier to restrict access to decay controllers only */ modifier onlyDecayController() { if (!_accessManager.hasRole(DECAY_CONTROLLER_ROLE, msg.sender)) { revert CallerIsNotDecayController(msg.sender); } _; } /** * @notice Modifier to restrict access to foundation only * @dev Modifier to check that the caller has the Foundation role * @custom:security-considerations * - Ensures that only the Foundation can access vesting and related functions * - Relies on the correct setup of the access manager */ modifier onlyFoundation() { if ( !_accessManager.hasRole( _accessManager.FOUNDATION_ROLE(), msg.sender ) ) { revert CallerIsNotFoundation(msg.sender); } _; } /*////////////////////////////////////////////////////////////// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Generates a role identifier for a specific contract and role * @param roleName The name of the role * @param roleTargetContract The address of the contract the role is for * @return The generated role identifier * @dev This function is used to create unique role identifiers for contract-specific roles */ function generateRole( ContractSpecificRoles roleName, address roleTargetContract ) public pure returns (bytes32) { return keccak256(abi.encodePacked(roleName, roleTargetContract)); } /** * @notice Checks if an account has the Admirals Quarters role * @param account The address to check * @return bool True if the account has the Admirals Quarters role */ function hasAdmiralsQuartersRole( address account ) public view returns (bool) { return _accessManager.hasRole(ADMIRALS_QUARTERS_ROLE, account); } /*////////////////////////////////////////////////////////////// INTERNAL FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Helper function to check if an address has the Governor role * @param account The address to check * @return bool True if the address has the Governor role */ function _isGovernor(address account) internal view returns (bool) { return _accessManager.hasRole(GOVERNOR_ROLE, account); } function _isDecayController(address account) internal view returns (bool) { return _accessManager.hasRole(DECAY_CONTROLLER_ROLE, account); } /** * @notice Helper function to check if an address has the Foundation role * @param account The address to check * @return bool True if the address has the Foundation role */ function _isFoundation(address account) internal view returns (bool) { return _accessManager.hasRole(_accessManager.FOUNDATION_ROLE(), account); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {ContractSpecificRoles, IProtocolAccessManager} from "../interfaces/IProtocolAccessManager.sol"; import {LimitedAccessControl} from "./LimitedAccessControl.sol"; import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; /** * @title ProtocolAccessManager * @notice This contract is the central authority for access control within the protocol. * It defines and manages various roles that govern different aspects of the system. * * @dev This contract extends LimitedAccessControl, which restricts direct role management. * Roles are typically assigned during deployment or through governance proposals. * * The contract defines four main roles: * 1. GOVERNOR_ROLE: System-wide administrators * 2. KEEPER_ROLE: Routine maintenance operators * 3. SUPER_KEEPER_ROLE: Advanced maintenance operators * 4. COMMANDER_ROLE: Managers of specific protocol components (Arks) * 5. ADMIRALS_QUARTERS_ROLE: Specific role for admirals quarters bundler contract * Role Hierarchy and Management: * - The GOVERNOR_ROLE is at the top of the hierarchy and can manage all other roles. * - Other roles cannot manage roles directly due to LimitedAccessControl restrictions. * - Role assignments are typically done through governance proposals or during initial setup. * * Usage in the System: * - Other contracts in the system inherit from ProtocolAccessManaged, which checks permissions * against this ProtocolAccessManager. * - Critical functions in various contracts are protected by role-based modifiers * (e.g., onlyGovernor, onlyKeeper, etc.) which query this contract for permissions. * * Security Considerations: * - The GOVERNOR_ROLE has significant power and should be managed carefully, potentially * through a multi-sig wallet or governance contract. * - The SUPER_KEEPER_ROLE has elevated privileges and should be assigned judiciously. * - The COMMANDER_ROLE is not directly manageable through this contract but is used * in other parts of the system for specific access control. */ contract ProtocolAccessManager is IProtocolAccessManager, LimitedAccessControl { /*////////////////////////////////////////////////////////////// CONSTANTS //////////////////////////////////////////////////////////////*/ /// @notice Role identifier for protocol governors - highest privilege level with admin capabilities bytes32 public constant GOVERNOR_ROLE = keccak256("GOVERNOR_ROLE"); /// @notice Role identifier for super keepers who can globally perform fleet maintanence roles bytes32 public constant SUPER_KEEPER_ROLE = keccak256("SUPER_KEEPER_ROLE"); /** * @notice Role identifier for protocol guardians * @dev Guardians have emergency powers across multiple protocol components: * - Can pause/unpause Fleet operations for security * - Can pause/unpause TipJar operations * - Can cancel governance proposals on SummerGovernor even if they don't meet normal cancellation requirements * - Can cancel TipJar proposals * * The guardian role serves as an emergency backstop to protect the protocol, but with less * privilege than governors. */ bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE"); /** * @notice Role identifier for decay controller * @dev This role allows the decay controller to manage the decay of user voting power */ bytes32 public constant DECAY_CONTROLLER_ROLE = keccak256("DECAY_CONTROLLER_ROLE"); /** * @notice Role identifier for admirals quarters bundler contract * @dev This role allows Admirals Quarters to unstake and withdraw assets from fleets, on behalf of users * @dev Withdrawn tokens go straight to users wallet, lowering the risk of manipulation if the role is compromised */ bytes32 public constant ADMIRALS_QUARTERS_ROLE = keccak256("ADMIRALS_QUARTERS_ROLE"); /// @notice Minimum allowed guardian expiration period (7 days) uint256 public constant MIN_GUARDIAN_EXPIRY = 7 days; /// @notice Maximum allowed guardian expiration period (180 days) uint256 public constant MAX_GUARDIAN_EXPIRY = 180 days; /// @notice Role identifier for the Foundation which manages vesting wallets and related operations bytes32 public constant FOUNDATION_ROLE = keccak256("FOUNDATION_ROLE"); /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ /** * @notice Initializes the ProtocolAccessManager contract * @param governor Address of the initial governor * @dev Grants the governor address the GOVERNOR_ROLE */ constructor(address governor) { _grantRole(GOVERNOR_ROLE, governor); } /** * @dev Modifier to check that the caller has the Governor role */ modifier onlyGovernor() { if (!hasRole(GOVERNOR_ROLE, msg.sender)) { revert CallerIsNotGovernor(msg.sender); } _; } /*////////////////////////////////////////////////////////////// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Checks if the contract supports a given interface * @dev Overrides the supportsInterface function from AccessControl * @param interfaceId The interface identifier, as specified in ERC-165 * @return bool True if the contract supports the interface, false otherwise * * This function supports: * - IProtocolAccessManager interface * - All interfaces supported by the parent AccessControl contract */ function supportsInterface( bytes4 interfaceId ) public view override returns (bool) { return interfaceId == type(IProtocolAccessManager).interfaceId || super.supportsInterface(interfaceId); } /// @inheritdoc IProtocolAccessManager function grantGovernorRole(address account) external onlyGovernor { _grantRole(GOVERNOR_ROLE, account); } /// @inheritdoc IProtocolAccessManager function revokeGovernorRole(address account) external onlyGovernor { _revokeRole(GOVERNOR_ROLE, account); } /*////////////////////////////////////////////////////////////// EXTERNAL GOVERNOR FUNCTIONS //////////////////////////////////////////////////////////////*/ /// @inheritdoc IProtocolAccessManager function grantSuperKeeperRole(address account) external onlyGovernor { _grantRole(SUPER_KEEPER_ROLE, account); } /// @inheritdoc IProtocolAccessManager function grantGuardianRole(address account) external onlyGovernor { _grantRole(GUARDIAN_ROLE, account); } /// @inheritdoc IProtocolAccessManager function revokeGuardianRole(address account) external onlyGovernor { _revokeRole(GUARDIAN_ROLE, account); } /// @inheritdoc IProtocolAccessManager function revokeSuperKeeperRole(address account) external onlyGovernor { _revokeRole(SUPER_KEEPER_ROLE, account); } /// @inheritdoc IProtocolAccessManager function grantContractSpecificRole( ContractSpecificRoles roleName, address roleTargetContract, address roleOwner ) public onlyGovernor { bytes32 role = generateRole(roleName, roleTargetContract); _grantRole(role, roleOwner); } /// @inheritdoc IProtocolAccessManager function revokeContractSpecificRole( ContractSpecificRoles roleName, address roleTargetContract, address roleOwner ) public onlyGovernor { bytes32 role = generateRole(roleName, roleTargetContract); _revokeRole(role, roleOwner); } /// @inheritdoc IProtocolAccessManager function grantCuratorRole( address fleetCommanderAddress, address account ) public onlyGovernor { grantContractSpecificRole( ContractSpecificRoles.CURATOR_ROLE, fleetCommanderAddress, account ); } /// @inheritdoc IProtocolAccessManager function revokeCuratorRole( address fleetCommanderAddress, address account ) public onlyGovernor { revokeContractSpecificRole( ContractSpecificRoles.CURATOR_ROLE, fleetCommanderAddress, account ); } /// @inheritdoc IProtocolAccessManager function grantKeeperRole( address fleetCommanderAddress, address account ) public onlyGovernor { grantContractSpecificRole( ContractSpecificRoles.KEEPER_ROLE, fleetCommanderAddress, account ); } /// @inheritdoc IProtocolAccessManager function revokeKeeperRole( address fleetCommanderAddress, address account ) public onlyGovernor { revokeContractSpecificRole( ContractSpecificRoles.KEEPER_ROLE, fleetCommanderAddress, account ); } /// @inheritdoc IProtocolAccessManager function grantCommanderRole( address arkAddress, address account ) public onlyGovernor { grantContractSpecificRole( ContractSpecificRoles.COMMANDER_ROLE, arkAddress, account ); } /// @inheritdoc IProtocolAccessManager function revokeCommanderRole( address arkAddress, address account ) public onlyGovernor { revokeContractSpecificRole( ContractSpecificRoles.COMMANDER_ROLE, arkAddress, account ); } /// @inheritdoc IProtocolAccessManager function grantDecayControllerRole(address account) public onlyGovernor { _grantRole(DECAY_CONTROLLER_ROLE, account); } /// @inheritdoc IProtocolAccessManager function revokeDecayControllerRole(address account) public onlyGovernor { _revokeRole(DECAY_CONTROLLER_ROLE, account); } /*////////////////////////////////////////////////////////////// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////*/ /// @inheritdoc IProtocolAccessManager function selfRevokeContractSpecificRole( ContractSpecificRoles roleName, address roleTargetContract ) public { bytes32 role = generateRole(roleName, roleTargetContract); if (!hasRole(role, msg.sender)) { revert CallerIsNotContractSpecificRole(msg.sender, role); } _revokeRole(role, msg.sender); } /// @inheritdoc IProtocolAccessManager function generateRole( ContractSpecificRoles roleName, address roleTargetContract ) public pure returns (bytes32) { return keccak256(abi.encodePacked(roleName, roleTargetContract)); } /// @inheritdoc IProtocolAccessManager function grantAdmiralsQuartersRole( address account ) external onlyRole(GOVERNOR_ROLE) { _grantRole(ADMIRALS_QUARTERS_ROLE, account); } /// @inheritdoc IProtocolAccessManager function revokeAdmiralsQuartersRole( address account ) external onlyRole(GOVERNOR_ROLE) { _revokeRole(ADMIRALS_QUARTERS_ROLE, account); } mapping(address guardian => uint256 expirationTimestamp) public guardianExpirations; /** * @notice Checks if an account is an active guardian (has role and not expired) * @param account Address to check * @return bool True if account is an active guardian */ function isActiveGuardian(address account) public view returns (bool) { return hasRole(GUARDIAN_ROLE, account) && guardianExpirations[account] > block.timestamp; } /** * @notice Sets the expiration timestamp for a guardian * @param account Guardian address * @param expiration Timestamp when guardian powers expire * @dev The expiration period (time from now until expiration) must be between MIN_GUARDIAN_EXPIRY and MAX_GUARDIAN_EXPIRY * This ensures guardians can't be immediately removed (protecting against malicious proposals) while still * allowing for their eventual phase-out (protecting against malicious guardians) */ function setGuardianExpiration( address account, uint256 expiration ) external onlyRole(GOVERNOR_ROLE) { if (!hasRole(GUARDIAN_ROLE, account)) { revert CallerIsNotGuardian(account); } uint256 expiryPeriod = expiration - block.timestamp; if ( expiryPeriod < MIN_GUARDIAN_EXPIRY || expiryPeriod > MAX_GUARDIAN_EXPIRY ) { revert InvalidGuardianExpiryPeriod( expiryPeriod, MIN_GUARDIAN_EXPIRY, MAX_GUARDIAN_EXPIRY ); } guardianExpirations[account] = expiration; emit GuardianExpirationSet(account, expiration); } /** * @inheritdoc IProtocolAccessManager */ function hasRole( bytes32 role, address account ) public view virtual override(IProtocolAccessManager, AccessControl) returns (bool) { return super.hasRole(role, account); } /// @inheritdoc IProtocolAccessManager function getGuardianExpiration( address account ) external view returns (uint256 expiration) { if (!hasRole(GUARDIAN_ROLE, account)) { revert CallerIsNotGuardian(account); } return guardianExpirations[account]; } /// @inheritdoc IProtocolAccessManager function grantFoundationRole(address account) external onlyGovernor { _grantRole(FOUNDATION_ROLE, account); } /// @inheritdoc IProtocolAccessManager function revokeFoundationRole(address account) external onlyGovernor { _revokeRole(FOUNDATION_ROLE, account); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; /** * @title IAccessControlErrors * @dev This file contains custom error definitions for access control in the system. * @notice These custom errors provide more gas-efficient and informative error handling * compared to traditional require statements with string messages. */ interface IAccessControlErrors { /** * @notice Thrown when a caller does not have the required role. */ error CallerIsNotContractSpecificRole(address caller, bytes32 role); /** * @notice Thrown when a caller is not the curator. */ error CallerIsNotCurator(address caller); /** * @notice Thrown when a caller is not the governor. */ error CallerIsNotGovernor(address caller); /** * @notice Thrown when a caller is not a keeper. */ error CallerIsNotKeeper(address caller); /** * @notice Thrown when a caller is not a super keeper. */ error CallerIsNotSuperKeeper(address caller); /** * @notice Thrown when a caller is not the commander. */ error CallerIsNotCommander(address caller); /** * @notice Thrown when a caller is neither the Raft nor the commander. */ error CallerIsNotRaftOrCommander(address caller); /** * @notice Thrown when a caller is not the Raft. */ error CallerIsNotRaft(address caller); /** * @notice Thrown when a caller is not an admin. */ error CallerIsNotAdmin(address caller); /** * @notice Thrown when a caller is not the guardian. */ error CallerIsNotGuardian(address caller); /** * @notice Thrown when a caller is not the guardian or governor. */ error CallerIsNotGuardianOrGovernor(address caller); /** * @notice Thrown when a caller is not the decay controller. */ error CallerIsNotDecayController(address caller); /** * @notice Thrown when a caller is not authorized to board. */ error CallerIsNotAuthorizedToBoard(address caller); /** * @notice Thrown when direct grant is disabled. */ error DirectGrantIsDisabled(address caller); /** * @notice Thrown when direct revoke is disabled. */ error DirectRevokeIsDisabled(address caller); /** * @notice Thrown when an invalid access manager address is provided. */ error InvalidAccessManagerAddress(address invalidAddress); /** * @notice Error thrown when a caller is not the Foundation * @param caller The address that attempted the operation */ error CallerIsNotFoundation(address caller); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; /** * @dev Dynamic roles are roles that are not hardcoded in the contract but are defined by the protocol * Members of this enum are treated as prefixes to the role generated using prefix and target contract address * e.g generateRole(ContractSpecificRoles.CURATOR_ROLE, address(this)) for FleetCommander, to generate the CURATOR_ROLE * for the curator of the FleetCommander contract */ enum ContractSpecificRoles { CURATOR_ROLE, KEEPER_ROLE, COMMANDER_ROLE } /** * @title IProtocolAccessManager * @notice Defines system roles and provides role based remote-access control for * contracts that inherit from ProtocolAccessManaged contract */ interface IProtocolAccessManager { /** * @notice Grants the Governor role to a given account * * @param account The account to which the Governor role will be granted */ function grantGovernorRole(address account) external; /** * @notice Revokes the Governor role from a given account * * @param account The account from which the Governor role will be revoked */ function revokeGovernorRole(address account) external; /** * @notice Grants the Super Keeper role to a given account * * @param account The account to which the Super Keeper role will be granted */ function grantSuperKeeperRole(address account) external; /** * @notice Revokes the Super Keeper role from a given account * * @param account The account from which the Super Keeper role will be revoked */ function revokeSuperKeeperRole(address account) external; /** * @dev Generates a unique role identifier based on the role name and target contract address * @param roleName The name of the role (from ContractSpecificRoles enum) * @param roleTargetContract The address of the contract the role is for * @return bytes32 The generated role identifier * @custom:internal-logic * - Combines the roleName and roleTargetContract using abi.encodePacked * - Applies keccak256 hash function to generate a unique bytes32 identifier * @custom:effects * - Does not modify any state, pure function * @custom:security-considerations * - Ensures unique role identifiers for different contracts * - Relies on the uniqueness of contract addresses and role names */ function generateRole( ContractSpecificRoles roleName, address roleTargetContract ) external pure returns (bytes32); /** * @notice Grants a contract specific role to a given account * @param roleName The name of the role to grant * @param roleTargetContract The address of the contract to grant the role for * @param account The account to which the role will be granted */ function grantContractSpecificRole( ContractSpecificRoles roleName, address roleTargetContract, address account ) external; /** * @notice Revokes a contract specific role from a given account * @param roleName The name of the role to revoke * @param roleTargetContract The address of the contract to revoke the role for * @param account The account from which the role will be revoked */ function revokeContractSpecificRole( ContractSpecificRoles roleName, address roleTargetContract, address account ) external; /** * @notice Grants the Curator role to a given account * @param fleetCommanderAddress The address of the fleet commander to grant the role for * @param account The account to which the role will be granted */ function grantCuratorRole( address fleetCommanderAddress, address account ) external; /** * @notice Revokes the Curator role from a given account * @param fleetCommanderAddress The address of the fleet commander to revoke the role for * @param account The account from which the role will be revoked */ function revokeCuratorRole( address fleetCommanderAddress, address account ) external; /** * @notice Grants the Keeper role to a given account * @param fleetCommanderAddress The address of the fleet commander to grant the role for * @param account The account to which the role will be granted */ function grantKeeperRole( address fleetCommanderAddress, address account ) external; /** * @notice Revokes the Keeper role from a given account * @param fleetCommanderAddress The address of the fleet commander to revoke the role for * @param account The account from which the role will be revoked */ function revokeKeeperRole( address fleetCommanderAddress, address account ) external; /** * @notice Grants the Commander role for a specific Ark * @param arkAddress Address of the Ark contract * @param account Address to grant the Commander role to */ function grantCommanderRole(address arkAddress, address account) external; /** * @notice Revokes the Commander role for a specific Ark * @param arkAddress Address of the Ark contract * @param account Address to revoke the Commander role from */ function revokeCommanderRole(address arkAddress, address account) external; /** * @notice Revokes a contract specific role from the caller * @param roleName The name of the role to revoke * @param roleTargetContract The address of the contract to revoke the role for */ function selfRevokeContractSpecificRole( ContractSpecificRoles roleName, address roleTargetContract ) external; /** * @notice Grants the Guardian role to a given account * * @param account The account to which the Guardian role will be granted */ function grantGuardianRole(address account) external; /** * @notice Revokes the Guardian role from a given account * * @param account The account from which the Guardian role will be revoked */ function revokeGuardianRole(address account) external; /** * @notice Grants the Decay Controller role to a given account * @param account The account to which the Decay Controller role will be granted */ function grantDecayControllerRole(address account) external; /** * @notice Revokes the Decay Controller role from a given account * @param account The account from which the Decay Controller role will be revoked */ function revokeDecayControllerRole(address account) external; /** * @notice Grants the ADMIRALS_QUARTERS_ROLE to an address * @param account The address to grant the role to */ function grantAdmiralsQuartersRole(address account) external; /** * @notice Revokes the ADMIRALS_QUARTERS_ROLE from an address * @param account The address to revoke the role from */ function revokeAdmiralsQuartersRole(address account) external; /*////////////////////////////////////////////////////////////// ROLE CONSTANTS //////////////////////////////////////////////////////////////*/ /// @notice Role identifier for the Governor role function GOVERNOR_ROLE() external pure returns (bytes32); /// @notice Role identifier for the Guardian role function GUARDIAN_ROLE() external pure returns (bytes32); /// @notice Role identifier for the Super Keeper role function SUPER_KEEPER_ROLE() external pure returns (bytes32); /// @notice Role identifier for the Decay Controller role function DECAY_CONTROLLER_ROLE() external pure returns (bytes32); /// @notice Role identifier for the Admirals Quarters role function ADMIRALS_QUARTERS_ROLE() external pure returns (bytes32); /// @notice Role identifier for the Foundation, responsible for managing vesting wallets and related operations function FOUNDATION_ROLE() external pure returns (bytes32); /** * @notice Checks if an account has a specific role * @param role The role identifier to check * @param account The account to check the role for * @return bool True if the account has the role, false otherwise */ function hasRole( bytes32 role, address account ) external view returns (bool); /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ /** * @notice Emitted when a guardian's expiration is set * @param account The address of the guardian * @param expiration The timestamp until which the guardian powers are valid */ event GuardianExpirationSet(address indexed account, uint256 expiration); /*////////////////////////////////////////////////////////////// GUARDIAN FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Checks if an account is an active guardian (has role and not expired) * @param account Address to check * @return bool True if account is an active guardian */ function isActiveGuardian(address account) external view returns (bool); /** * @notice Sets the expiration timestamp for a guardian * @param account Guardian address * @param expiration Timestamp when guardian powers expire */ function setGuardianExpiration( address account, uint256 expiration ) external; /** * @notice Gets the expiration timestamp for a guardian * @param account Guardian address * @return uint256 Timestamp when guardian powers expire */ function guardianExpirations( address account ) external view returns (uint256); /** * @notice Gets the expiration timestamp for a guardian * @param account Guardian address * @return expiration Timestamp when guardian powers expire */ function getGuardianExpiration( address account ) external view returns (uint256 expiration); /** * @notice Emitted when an invalid guardian expiry period is set * @param expiryPeriod The expiry period that was set * @param minExpiryPeriod The minimum allowed expiry period * @param maxExpiryPeriod The maximum allowed expiry period */ error InvalidGuardianExpiryPeriod( uint256 expiryPeriod, uint256 minExpiryPeriod, uint256 maxExpiryPeriod ); /** * @notice Grants the Foundation role to a given account. The Foundation is responsible for * managing vesting wallets and related operations. * @param account The account to which the Foundation role will be granted */ function grantFoundationRole(address account) external; /** * @notice Revokes the Foundation role from a given account * @param account The account from which the Foundation role will be revoked */ function revokeFoundationRole(address account) external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; library Constants { // WAD: Common unit, stands for "18 decimals" uint256 public constant WAD = 1e18; // RAY: Higher precision unit, "27 decimals" uint256 public constant RAY = 1e27; // Conversion factor from WAD to RAY uint256 public constant WAD_TO_RAY = 1e9; // Number of seconds in a day uint256 public constant SECONDS_PER_DAY = 1 days; // Number of seconds in a year (assuming 365 days) uint256 public constant SECONDS_PER_YEAR = 365 days; // Maximum value for uint256 uint256 public constant MAX_UINT256 = type(uint256).max; // AAVE V3 POOL CONFIG DATA MASK uint256 internal constant ACTIVE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFF; uint256 internal constant FROZEN_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF; uint256 internal constant PAUSED_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFF; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {StorageSlot} from "./StorageSlot.sol"; /** * @dev Variant of {ReentrancyGuard} that uses transient storage. * * NOTE: This variant only works on networks where EIP-1153 is available. */ abstract contract ReentrancyGuardTransient { using StorageSlot for *; // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant REENTRANCY_GUARD_STORAGE = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be NOT_ENTERED if (_reentrancyGuardEntered()) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail REENTRANCY_GUARD_STORAGE.asBoolean().tstore(true); } function _nonReentrantAfter() private { REENTRANCY_GUARD_STORAGE.asBoolean().tstore(false); } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return REENTRANCY_GUARD_STORAGE.asBoolean().tload(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol) // This file was procedurally generated from scripts/generate/templates/StorageSlot.js. pragma solidity ^0.8.24; /** * @dev Library for reading and writing primitive types to specific storage slots. * * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. * This library helps with reading and writing to such slots without the need for inline assembly. * * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. * * Example usage to set ERC-1967 implementation slot: * ```solidity * contract ERC1967 { * // Define the slot. Alternatively, use the SlotDerivation library to derive the slot. * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; * * function _getImplementation() internal view returns (address) { * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; * } * * function _setImplementation(address newImplementation) internal { * require(newImplementation.code.length > 0); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` * * Since version 5.1, this library also support writing and reading value types to and from transient storage. * * * Example using transient storage: * ```solidity * contract Lock { * // Define the slot. Alternatively, use the SlotDerivation library to derive the slot. * bytes32 internal constant _LOCK_SLOT = 0xf4678858b2b588224636b8522b729e7722d32fc491da849ed75b3fdf3c84f542; * * modifier locked() { * require(!_LOCK_SLOT.asBoolean().tload()); * * _LOCK_SLOT.asBoolean().tstore(true); * _; * _LOCK_SLOT.asBoolean().tstore(false); * } * } * ``` * * TIP: Consider using this library along with {SlotDerivation}. */ library StorageSlot { struct AddressSlot { address value; } struct BooleanSlot { bool value; } struct Bytes32Slot { bytes32 value; } struct Uint256Slot { uint256 value; } struct Int256Slot { int256 value; } struct StringSlot { string value; } struct BytesSlot { bytes value; } /** * @dev Returns an `AddressSlot` with member `value` located at `slot`. */ function getAddressSlot( bytes32 slot ) internal pure returns (AddressSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `BooleanSlot` with member `value` located at `slot`. */ function getBooleanSlot( bytes32 slot ) internal pure returns (BooleanSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. */ function getBytes32Slot( bytes32 slot ) internal pure returns (Bytes32Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `Uint256Slot` with member `value` located at `slot`. */ function getUint256Slot( bytes32 slot ) internal pure returns (Uint256Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `Int256Slot` with member `value` located at `slot`. */ function getInt256Slot( bytes32 slot ) internal pure returns (Int256Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `StringSlot` with member `value` located at `slot`. */ function getStringSlot( bytes32 slot ) internal pure returns (StringSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `StringSlot` representation of the string storage pointer `store`. */ function getStringSlot( string storage store ) internal pure returns (StringSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := store.slot } } /** * @dev Returns an `BytesSlot` with member `value` located at `slot`. */ function getBytesSlot( bytes32 slot ) internal pure returns (BytesSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`. */ function getBytesSlot( bytes storage store ) internal pure returns (BytesSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := store.slot } } /** * @dev UDVT that represent a slot holding a address. */ type AddressSlotType is bytes32; /** * @dev Cast an arbitrary slot to a AddressSlotType. */ function asAddress(bytes32 slot) internal pure returns (AddressSlotType) { return AddressSlotType.wrap(slot); } /** * @dev UDVT that represent a slot holding a bool. */ type BooleanSlotType is bytes32; /** * @dev Cast an arbitrary slot to a BooleanSlotType. */ function asBoolean(bytes32 slot) internal pure returns (BooleanSlotType) { return BooleanSlotType.wrap(slot); } /** * @dev UDVT that represent a slot holding a bytes32. */ type Bytes32SlotType is bytes32; /** * @dev Cast an arbitrary slot to a Bytes32SlotType. */ function asBytes32(bytes32 slot) internal pure returns (Bytes32SlotType) { return Bytes32SlotType.wrap(slot); } /** * @dev UDVT that represent a slot holding a uint256. */ type Uint256SlotType is bytes32; /** * @dev Cast an arbitrary slot to a Uint256SlotType. */ function asUint256(bytes32 slot) internal pure returns (Uint256SlotType) { return Uint256SlotType.wrap(slot); } /** * @dev UDVT that represent a slot holding a int256. */ type Int256SlotType is bytes32; /** * @dev Cast an arbitrary slot to a Int256SlotType. */ function asInt256(bytes32 slot) internal pure returns (Int256SlotType) { return Int256SlotType.wrap(slot); } /** * @dev Load the value held at location `slot` in transient storage. */ function tload(AddressSlotType slot) internal view returns (address value) { /// @solidity memory-safe-assembly assembly { value := tload(slot) } } /** * @dev Store `value` at location `slot` in transient storage. */ function tstore(AddressSlotType slot, address value) internal { /// @solidity memory-safe-assembly assembly { tstore(slot, value) } } /** * @dev Load the value held at location `slot` in transient storage. */ function tload(BooleanSlotType slot) internal view returns (bool value) { /// @solidity memory-safe-assembly assembly { value := tload(slot) } } /** * @dev Store `value` at location `slot` in transient storage. */ function tstore(BooleanSlotType slot, bool value) internal { /// @solidity memory-safe-assembly assembly { tstore(slot, value) } } /** * @dev Load the value held at location `slot` in transient storage. */ function tload(Bytes32SlotType slot) internal view returns (bytes32 value) { /// @solidity memory-safe-assembly assembly { value := tload(slot) } } /** * @dev Store `value` at location `slot` in transient storage. */ function tstore(Bytes32SlotType slot, bytes32 value) internal { /// @solidity memory-safe-assembly assembly { tstore(slot, value) } } /** * @dev Load the value held at location `slot` in transient storage. */ function tload(Uint256SlotType slot) internal view returns (uint256 value) { /// @solidity memory-safe-assembly assembly { value := tload(slot) } } /** * @dev Store `value` at location `slot` in transient storage. */ function tstore(Uint256SlotType slot, uint256 value) internal { /// @solidity memory-safe-assembly assembly { tstore(slot, value) } } /** * @dev Load the value held at location `slot` in transient storage. */ function tload(Int256SlotType slot) internal view returns (int256 value) { /// @solidity memory-safe-assembly assembly { value := tload(slot) } } /** * @dev Store `value` at location `slot` in transient storage. */ function tstore(Int256SlotType slot, int256 value) internal { /// @solidity memory-safe-assembly assembly { tstore(slot, value) } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; /** * @title Percentage * @author Roberto Cano * @notice Custom type for Percentage values with associated utility functions * @dev This contract defines a custom Percentage type and overloaded operators * to perform arithmetic and comparison operations on Percentage values. */ /** * @dev Custom percentage type as uint256 * @notice This type is used to represent percentage values with high precision */ type Percentage is uint256; /** * @dev Overridden operators declaration for Percentage * @notice These operators allow for intuitive arithmetic and comparison operations * on Percentage values */ using { add as +, subtract as -, multiply as *, divide as /, lessOrEqualThan as <=, lessThan as <, greaterOrEqualThan as >=, greaterThan as >, equalTo as == } for Percentage global; /** * @dev The number of decimals used for the percentage * This constant defines the precision of the Percentage type */ uint256 constant PERCENTAGE_DECIMALS = 18; /** * @dev The factor used to scale the percentage * This constant is used to convert between human-readable percentages * and the internal representation */ uint256 constant PERCENTAGE_FACTOR = 10 ** PERCENTAGE_DECIMALS; /** * @dev Percentage of 100% with the given `PERCENTAGE_DECIMALS` * This constant represents 100% in the Percentage type */ Percentage constant PERCENTAGE_100 = Percentage.wrap(100 * PERCENTAGE_FACTOR); /** * OPERATOR FUNCTIONS */ /** * @dev Adds two Percentage values * @param a The first Percentage value * @param b The second Percentage value * @return The sum of a and b as a Percentage */ function add(Percentage a, Percentage b) pure returns (Percentage) { return Percentage.wrap(Percentage.unwrap(a) + Percentage.unwrap(b)); } /** * @dev Subtracts one Percentage value from another * @param a The Percentage value to subtract from * @param b The Percentage value to subtract * @return The difference between a and b as a Percentage */ function subtract(Percentage a, Percentage b) pure returns (Percentage) { return Percentage.wrap(Percentage.unwrap(a) - Percentage.unwrap(b)); } /** * @dev Multiplies two Percentage values * @param a The first Percentage value * @param b The second Percentage value * @return The product of a and b as a Percentage, scaled appropriately */ function multiply(Percentage a, Percentage b) pure returns (Percentage) { return Percentage.wrap( (Percentage.unwrap(a) * Percentage.unwrap(b)) / Percentage.unwrap(PERCENTAGE_100) ); } /** * @dev Divides one Percentage value by another * @param a The Percentage value to divide * @param b The Percentage value to divide by * @return The quotient of a divided by b as a Percentage, scaled appropriately */ function divide(Percentage a, Percentage b) pure returns (Percentage) { return Percentage.wrap( (Percentage.unwrap(a) * Percentage.unwrap(PERCENTAGE_100)) / Percentage.unwrap(b) ); } /** * @dev Checks if one Percentage value is less than or equal to another * @param a The first Percentage value * @param b The second Percentage value * @return True if a is less than or equal to b, false otherwise */ function lessOrEqualThan(Percentage a, Percentage b) pure returns (bool) { return Percentage.unwrap(a) <= Percentage.unwrap(b); } /** * @dev Checks if one Percentage value is less than another * @param a The first Percentage value * @param b The second Percentage value * @return True if a is less than b, false otherwise */ function lessThan(Percentage a, Percentage b) pure returns (bool) { return Percentage.unwrap(a) < Percentage.unwrap(b); } /** * @dev Checks if one Percentage value is greater than or equal to another * @param a The first Percentage value * @param b The second Percentage value * @return True if a is greater than or equal to b, false otherwise */ function greaterOrEqualThan(Percentage a, Percentage b) pure returns (bool) { return Percentage.unwrap(a) >= Percentage.unwrap(b); } /** * @dev Checks if one Percentage value is greater than another * @param a The first Percentage value * @param b The second Percentage value * @return True if a is greater than b, false otherwise */ function greaterThan(Percentage a, Percentage b) pure returns (bool) { return Percentage.unwrap(a) > Percentage.unwrap(b); } /** * @dev Checks if two Percentage values are equal * @param a The first Percentage value * @param b The second Percentage value * @return True if a is equal to b, false otherwise */ function equalTo(Percentage a, Percentage b) pure returns (bool) { return Percentage.unwrap(a) == Percentage.unwrap(b); } /** * @dev Alias for equalTo function * @param a The first Percentage value * @param b The second Percentage value * @return True if a is equal to b, false otherwise */ function equals(Percentage a, Percentage b) pure returns (bool) { return Percentage.unwrap(a) == Percentage.unwrap(b); } /** * @dev Converts a uint256 value to a Percentage * @param value The uint256 value to convert * @return The input value as a Percentage */ function toPercentage(uint256 value) pure returns (Percentage) { return Percentage.wrap(value * PERCENTAGE_FACTOR); } /** * @dev Converts a Percentage value to a uint256 * @param value The Percentage value to convert * @return The Percentage value as a uint256 */ function fromPercentage(Percentage value) pure returns (uint256) { return Percentage.unwrap(value) / PERCENTAGE_FACTOR; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; /** * @title StakingRewardsManager * @notice Contract for managing staking rewards with multiple reward tokens in the Summer protocol * @dev Implements IStakingRewards interface and inherits from ReentrancyGuardTransient and ProtocolAccessManaged * @dev Inspired by Synthetix's StakingRewards contract: * https://github.com/Synthetixio/synthetix/blob/v2.101.3/contracts/StakingRewards.sol */ import {IStakingRewardsManagerBase} from "../interfaces/IStakingRewardsManagerBase.sol"; import {ProtocolAccessManaged} from "@summerfi/access-contracts/contracts/ProtocolAccessManaged.sol"; import {ReentrancyGuardTransient} from "@summerfi/dependencies/openzeppelin-next/ReentrancyGuardTransient.sol"; import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {Constants} from "@summerfi/constants/Constants.sol"; import {ERC20Wrapper} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Wrapper.sol"; /** * @title StakingRewards * @notice Contract for managing staking rewards with multiple reward tokens in the Summer protocol * @dev Implements IStakingRewards interface and inherits from ReentrancyGuardTransient and ProtocolAccessManaged */ abstract contract StakingRewardsManagerBase is IStakingRewardsManagerBase, ReentrancyGuardTransient, ProtocolAccessManaged { using SafeERC20 for IERC20; using EnumerableSet for EnumerableSet.AddressSet; struct RewardData { uint256 periodFinish; uint256 rewardRate; uint256 rewardsDuration; uint256 lastUpdateTime; uint256 rewardPerTokenStored; } /*////////////////////////////////////////////////////////////// STATE VARIABLES //////////////////////////////////////////////////////////////*/ /* @notice List of all reward tokens supported by this contract */ EnumerableSet.AddressSet internal _rewardTokensList; /* @notice The token that users stake to earn rewards */ address public immutable stakingToken; /* @notice Mapping of reward token to its reward distribution data */ mapping(address rewardToken => RewardData data) public rewardData; /* @notice Tracks the last reward per token paid to each user for each reward token */ mapping(address rewardToken => mapping(address account => uint256 rewardPerTokenPaid)) public userRewardPerTokenPaid; /* @notice Tracks the unclaimed rewards for each user for each reward token */ mapping(address rewardToken => mapping(address account => uint256 rewardAmount)) public rewards; /* @notice Total amount of tokens staked in the contract */ uint256 public totalSupply; mapping(address account => uint256 balance) internal _balances; uint256 private constant MAX_REWARD_DURATION = 360 days; // 1 year /*////////////////////////////////////////////////////////////// MODIFIERS //////////////////////////////////////////////////////////////*/ modifier updateReward(address account) virtual { _updateReward(account); _; } /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ /** * @notice Initializes the StakingRewards contract * @param accessManager The address of the access manager */ constructor(address accessManager) ProtocolAccessManaged(accessManager) {} /*////////////////////////////////////////////////////////////// VIEWS //////////////////////////////////////////////////////////////*/ /// @inheritdoc IStakingRewardsManagerBase function rewardTokens( uint256 index ) external view override returns (address) { if (index >= _rewardTokensList.length()) revert IndexOutOfBounds(); address rewardTokenAddress = _rewardTokensList.at(index); return rewardTokenAddress; } /// @inheritdoc IStakingRewardsManagerBase function rewardTokensLength() external view returns (uint256) { return _rewardTokensList.length(); } /// @inheritdoc IStakingRewardsManagerBase function balanceOf(address account) public view virtual returns (uint256) { return _balances[account]; } /// @inheritdoc IStakingRewardsManagerBase function lastTimeRewardApplicable( address rewardToken ) public view returns (uint256) { return block.timestamp < rewardData[rewardToken].periodFinish ? block.timestamp : rewardData[rewardToken].periodFinish; } /// @inheritdoc IStakingRewardsManagerBase function rewardPerToken(address rewardToken) public view returns (uint256) { if (totalSupply == 0) { return rewardData[rewardToken].rewardPerTokenStored; } return rewardData[rewardToken].rewardPerTokenStored + ((lastTimeRewardApplicable(rewardToken) - rewardData[rewardToken].lastUpdateTime) * rewardData[rewardToken].rewardRate) / totalSupply; } /// @inheritdoc IStakingRewardsManagerBase function earned( address account, address rewardToken ) public view virtual returns (uint256) { return _earned(account, rewardToken); } /// @inheritdoc IStakingRewardsManagerBase function getRewardForDuration( address rewardToken ) external view returns (uint256) { RewardData storage data = rewardData[rewardToken]; if (block.timestamp >= data.periodFinish) { return (data.rewardRate * data.rewardsDuration) / Constants.WAD; } // For active periods, calculate remaining rewards plus any new rewards uint256 remaining = data.periodFinish - block.timestamp; return (data.rewardRate * remaining) / Constants.WAD; } /// @inheritdoc IStakingRewardsManagerBase function isRewardToken(address rewardToken) external view returns (bool) { return _isRewardToken(rewardToken); } /*////////////////////////////////////////////////////////////// MUTATIVE FUNCTIONS //////////////////////////////////////////////////////////////*/ /// @inheritdoc IStakingRewardsManagerBase function stake(uint256 amount) external virtual updateReward(_msgSender()) { _stake(_msgSender(), _msgSender(), amount); } /// @inheritdoc IStakingRewardsManagerBase function unstake( uint256 amount ) external virtual updateReward(_msgSender()) { _unstake(_msgSender(), _msgSender(), amount); } /// @inheritdoc IStakingRewardsManagerBase function getReward() public virtual nonReentrant { uint256 rewardTokenCount = _rewardTokensList.length(); for (uint256 i = 0; i < rewardTokenCount; i++) { address rewardTokenAddress = _rewardTokensList.at(i); _getReward(_msgSender(), rewardTokenAddress); } } /// @inheritdoc IStakingRewardsManagerBase function getReward(address rewardToken) public virtual nonReentrant { if (!_isRewardToken(rewardToken)) revert RewardTokenDoesNotExist(); _getReward(_msgSender(), rewardToken); } /// @inheritdoc IStakingRewardsManagerBase function exit() external virtual { getReward(); _unstake(_msgSender(), _msgSender(), _balances[_msgSender()]); } /// @notice Claims rewards for a specific account /// @param account The address to claim rewards for function getRewardFor(address account) public virtual nonReentrant { uint256 rewardTokenCount = _rewardTokensList.length(); for (uint256 i = 0; i < rewardTokenCount; i++) { address rewardTokenAddress = _rewardTokensList.at(i); _getReward(account, rewardTokenAddress); } } /// @notice Claims rewards for a specific account and specific reward token /// @param account The address to claim rewards for /// @param rewardToken The address of the reward token to claim function getRewardFor( address account, address rewardToken ) public virtual nonReentrant { if (!_isRewardToken(rewardToken)) revert RewardTokenDoesNotExist(); _getReward(account, rewardToken); } /*////////////////////////////////////////////////////////////// RESTRICTED FUNCTIONS //////////////////////////////////////////////////////////////*/ /// @inheritdoc IStakingRewardsManagerBase function notifyRewardAmount( address rewardToken, uint256 reward, uint256 newRewardsDuration ) external virtual onlyGovernor updateReward(address(0)) { _notifyRewardAmount(rewardToken, reward, newRewardsDuration); } /// @inheritdoc IStakingRewardsManagerBase function setRewardsDuration( address rewardToken, uint256 _rewardsDuration ) external onlyGovernor { if (!_isRewardToken(rewardToken)) { revert RewardTokenDoesNotExist(); } if (_rewardsDuration == 0) { revert RewardsDurationCannotBeZero(); } if (_rewardsDuration > MAX_REWARD_DURATION) { revert RewardsDurationTooLong(); } RewardData storage data = rewardData[rewardToken]; if (block.timestamp <= data.periodFinish) { revert RewardPeriodNotComplete(); } data.rewardsDuration = _rewardsDuration; emit RewardsDurationUpdated(address(rewardToken), _rewardsDuration); } /// @notice Removes a reward token from the list of reward tokens /// @param rewardToken The address of the reward token to remove function removeRewardToken(address rewardToken) external onlyGovernor { if (!_isRewardToken(rewardToken)) { revert RewardTokenDoesNotExist(); } if (block.timestamp <= rewardData[rewardToken].periodFinish) { revert RewardPeriodNotComplete(); } // Check if all tokens have been claimed, allowing a small dust balance uint256 remainingBalance = IERC20(rewardToken).balanceOf(address(this)); uint256 dustThreshold; try IERC20Metadata(address(rewardToken)).decimals() returns ( uint8 decimals ) { // For tokens with 4 or fewer decimals, use a minimum threshold of 1 // For tokens with more decimals, use 0.01% of 1 token if (decimals <= 4) { dustThreshold = 1; } else { dustThreshold = 10 ** (decimals - 4); // 0.01% of 1 token } } catch { dustThreshold = 1e14; // Default threshold for tokens without decimals } if (remainingBalance > dustThreshold) { revert RewardTokenStillHasBalance(remainingBalance); } // Remove the token from the rewardTokens map bool success = _rewardTokensList.remove(address(rewardToken)); if (!success) revert RewardTokenDoesNotExist(); emit RewardTokenRemoved(address(rewardToken)); } /*////////////////////////////////////////////////////////////// INTERNAL FUNCTIONS //////////////////////////////////////////////////////////////*/ function _isRewardToken(address rewardToken) internal view returns (bool) { return _rewardTokensList.contains(rewardToken); } function _stake( address staker, address receiver, uint256 amount ) internal virtual { if (receiver == address(0)) revert CannotStakeToZeroAddress(); if (amount == 0) revert CannotStakeZero(); if (address(stakingToken) == address(0)) { revert StakingTokenNotInitialized(); } totalSupply += amount; _balances[receiver] += amount; IERC20(stakingToken).safeTransferFrom(staker, address(this), amount); emit Staked(staker, receiver, amount); } function _unstake( address staker, address receiver, uint256 amount ) internal virtual { if (amount == 0) revert CannotUnstakeZero(); totalSupply -= amount; _balances[staker] -= amount; IERC20(stakingToken).safeTransfer(receiver, amount); emit Unstaked(staker, receiver, amount); } /* * @notice Internal function to calculate earned rewards for an account * @param account The address to calculate earnings for * @param rewardToken The reward token to calculate earnings for * @return The amount of reward tokens earned */ function _earned( address account, address rewardToken ) internal view returns (uint256) { return (_balances[account] * (rewardPerToken(rewardToken) - userRewardPerTokenPaid[rewardToken][account])) / Constants.WAD + rewards[rewardToken][account]; } function _updateReward(address account) internal { uint256 rewardTokenCount = _rewardTokensList.length(); for (uint256 i = 0; i < rewardTokenCount; i++) { address rewardTokenAddress = _rewardTokensList.at(i); RewardData storage rewardTokenData = rewardData[rewardTokenAddress]; rewardTokenData.rewardPerTokenStored = rewardPerToken( rewardTokenAddress ); rewardTokenData.lastUpdateTime = lastTimeRewardApplicable( rewardTokenAddress ); if (account != address(0)) { rewards[rewardTokenAddress][account] = earned( account, rewardTokenAddress ); userRewardPerTokenPaid[rewardTokenAddress][ account ] = rewardTokenData.rewardPerTokenStored; } } } /** * @notice Internal function to claim rewards for an account for a specific token * @param account The address to claim rewards for * @param rewardTokenAddress The address of the reward token to claim * @dev rewards go straight to the user's wallet */ function _getReward( address account, address rewardTokenAddress ) internal virtual updateReward(account) { uint256 reward = rewards[rewardTokenAddress][account]; if (reward > 0) { rewards[rewardTokenAddress][account] = 0; IERC20(rewardTokenAddress).safeTransfer(account, reward); emit RewardPaid(account, rewardTokenAddress, reward); } } /** * @dev Internal implementation of notifyRewardAmount * @param rewardToken The token to distribute as rewards * @param reward The amount of reward tokens to distribute * @param newRewardsDuration The duration for new reward tokens (only used for first time) */ function _notifyRewardAmount( address rewardToken, uint256 reward, uint256 newRewardsDuration ) internal { RewardData storage rewardTokenData = rewardData[rewardToken]; if (newRewardsDuration == 0) { revert RewardsDurationCannotBeZero(); } if (newRewardsDuration > MAX_REWARD_DURATION) { revert RewardsDurationTooLong(); } // For existing reward tokens, check if current period is complete if (_isRewardToken(rewardToken)) { if (newRewardsDuration != rewardTokenData.rewardsDuration) { revert CannotChangeRewardsDuration(); } } else { // First time setup for new reward token bool success = _rewardTokensList.add(rewardToken); if (!success) revert RewardTokenAlreadyExists(); rewardTokenData.rewardsDuration = newRewardsDuration; emit RewardTokenAdded(rewardToken, rewardTokenData.rewardsDuration); } // Transfer exact amount needed for new rewards IERC20(rewardToken).safeTransferFrom(msg.sender, address(this), reward); // Calculate new reward rate rewardTokenData.rewardRate = (reward * Constants.WAD) / rewardTokenData.rewardsDuration; rewardTokenData.lastUpdateTime = block.timestamp; rewardTokenData.periodFinish = block.timestamp + rewardTokenData.rewardsDuration; emit RewardAdded(address(rewardToken), reward); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IStakingRewardsManagerBaseErrors} from "./IStakingRewardsManagerBaseErrors.sol"; /* @title IStakingRewardsManagerBase * @notice Interface for the Staking Rewards Manager contract * @dev Manages staking and distribution of multiple reward tokens */ interface IStakingRewardsManagerBase is IStakingRewardsManagerBaseErrors { // Views /* @notice Get the total amount of staked tokens * @return The total supply of staked tokens */ function totalSupply() external view returns (uint256); /* @notice Get the staked balance of a specific account * @param account The address of the account to check * @return The staked balance of the account */ function balanceOf(address account) external view returns (uint256); /* @notice Get the last time the reward was applicable for a specific reward token * @param rewardToken The address of the reward token * @return The timestamp of the last applicable reward time */ function lastTimeRewardApplicable( address rewardToken ) external view returns (uint256); /* @notice Get the reward per token for a specific reward token * @param rewardToken The address of the reward token * @return The reward amount per staked token (WAD-scaled) * @dev Returns a WAD-scaled value (1e18) to maintain precision in calculations * @dev This value represents: (rewardRate * timeElapsed * WAD) / totalSupply */ function rewardPerToken( address rewardToken ) external view returns (uint256); /* @notice Calculate the earned reward for an account and a specific reward token * @param account The address of the account * @param rewardToken The address of the reward token * @return The amount of reward tokens earned (not WAD-scaled) * @dev Calculated as: (balance * (rewardPerToken - userRewardPerTokenPaid)) / WAD + rewards */ function earned( address account, address rewardToken ) external view returns (uint256); /* @notice Get the reward for the entire duration for a specific reward token * @param rewardToken The address of the reward token * @return The total reward amount for the duration (not WAD-scaled) * @dev Calculated as: (rewardRate * rewardsDuration) / WAD */ function getRewardForDuration( address rewardToken ) external view returns (uint256); /* @notice Get the address of the staking token * @return The address of the staking token */ function stakingToken() external view returns (address); /* @notice Get the reward token at a specific index * @param index The index of the reward token * @return The address of the reward token * @dev Reverts with IndexOutOfBounds if index >= rewardTokensLength() */ function rewardTokens(uint256 index) external view returns (address); /* @notice Get the total number of reward tokens * @return The length of the reward tokens list */ function rewardTokensLength() external view returns (uint256); /* @notice Check if a token is in the list of reward tokens * @param rewardToken The address to check * @return bool True if the token is a reward token, false otherwise */ function isRewardToken(address rewardToken) external view returns (bool); // Mutative functions /* @notice Stake tokens for an account * @param amount The amount of tokens to stake */ function stake(uint256 amount) external; /* @notice Stake tokens for an account on behalf of another account * @param receiver The address of the account to stake for * @param amount The amount of tokens to stake */ function stakeOnBehalfOf(address receiver, uint256 amount) external; /* @notice Unstake staked tokens on behalf of another account * @param owner The address of the account to unstake from * @param amount The amount of tokens to unstake * @param claimRewards Whether to claim rewards before unstaking */ function unstakeAndWithdrawOnBehalfOf( address owner, uint256 amount, bool claimRewards ) external; /* @notice Unstake staked tokens * @param amount The amount of tokens to unstake */ function unstake(uint256 amount) external; /* @notice Claim accumulated rewards for all reward tokens */ function getReward() external; /* @notice Claim accumulated rewards for a specific reward token * @param rewardToken The address of the reward token to claim */ function getReward(address rewardToken) external; /* @notice Withdraw all staked tokens and claim rewards */ function exit() external; // Admin functions /* @notice Notify the contract about new reward amount * @param rewardToken The address of the reward token * @param reward The amount of new reward (not WAD-scaled) * @param newRewardsDuration The duration for rewards distribution (only used when adding a new reward token) * @dev Internally sets rewardRate as (reward * WAD) / duration to maintain precision */ function notifyRewardAmount( address rewardToken, uint256 reward, uint256 newRewardsDuration ) external; /* @notice Set the duration for rewards distribution * @param rewardToken The address of the reward token * @param _rewardsDuration The new duration for rewards */ function setRewardsDuration( address rewardToken, uint256 _rewardsDuration ) external; /* @notice Removes a reward token from the list of reward tokens * @dev Can only be called by governor * @dev Can only be called after reward period is complete * @dev Can only be called if remaining balance is below dust threshold * @param rewardToken The address of the reward token to remove */ function removeRewardToken(address rewardToken) external; // Events /* @notice Emitted when a new reward is added * @param rewardToken The address of the reward token * @param reward The amount of reward added */ event RewardAdded(address indexed rewardToken, uint256 reward); /* @notice Emitted when tokens are staked * @param staker The address that provided the tokens for staking * @param receiver The address whose staking balance was updated * @param amount The amount of tokens added to the staking position */ event Staked( address indexed staker, address indexed receiver, uint256 amount ); /* @notice Emitted when tokens are unstaked * @param staker The address whose tokens were unstaked * @param receiver The address receiving the unstaked tokens * @param amount The amount of tokens unstaked */ event Unstaked( address indexed staker, address indexed receiver, uint256 amount ); /* @notice Emitted when tokens are withdrawn * @param user The address of the user that withdrew * @param amount The amount of tokens withdrawn */ event Withdrawn(address indexed user, uint256 amount); /* @notice Emitted when rewards are paid out * @param user The address of the user receiving the reward * @param rewardToken The address of the reward token * @param reward The amount of reward paid */ event RewardPaid( address indexed user, address indexed rewardToken, uint256 reward ); /* @notice Emitted when the rewards duration is updated * @param rewardToken The address of the reward token * @param newDuration The new duration for rewards */ event RewardsDurationUpdated( address indexed rewardToken, uint256 newDuration ); /* @notice Emitted when a new reward token is added * @param rewardToken The address of the new reward token * @param rewardsDuration The duration for the new reward token */ event RewardTokenAdded(address rewardToken, uint256 rewardsDuration); /* @notice Emitted when a reward token is removed * @param rewardToken The address of the reward token */ event RewardTokenRemoved(address rewardToken); /* @notice Claims rewards for a specific account * @param account The address to claim rewards for */ function getRewardFor(address account) external; /* @notice Claims rewards for a specific account and specific reward token * @param account The address to claim rewards for * @param rewardToken The address of the reward token to claim */ function getRewardFor(address account, address rewardToken) external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; /* @title IStakingRewardsManagerBaseErrors * @notice Interface defining custom errors for the Staking Rewards Manager */ interface IStakingRewardsManagerBaseErrors { /* @notice Thrown when attempting to stake zero tokens */ error CannotStakeZero(); /* @notice Thrown when attempting to withdraw zero tokens */ error CannotWithdrawZero(); /* @notice Thrown when the provided reward amount is too high */ error ProvidedRewardTooHigh(); /* @notice Thrown when trying to set rewards before the current period is complete */ error RewardPeriodNotComplete(); /* @notice Thrown when there are no reward tokens set */ error NoRewardTokens(); /* @notice Thrown when trying to add a reward token that already exists */ error RewardTokenAlreadyExists(); /* @notice Thrown when setting an invalid rewards duration */ error InvalidRewardsDuration(); /* @notice Thrown when trying to interact with a reward token that hasn't been initialized */ error RewardTokenNotInitialized(); /* @notice Thrown when the reward amount is invalid for the given duration * @param rewardToken The address of the reward token * @param rewardsDuration The duration for which the reward is invalid */ error InvalidRewardAmount(address rewardToken, uint256 rewardsDuration); /* @notice Thrown when trying to interact with the staking token before it's initialized */ error StakingTokenNotInitialized(); /* @notice Thrown when trying to remove a reward token that doesn't exist */ error RewardTokenDoesNotExist(); /* @notice Thrown when trying to change the rewards duration of a reward token */ error CannotChangeRewardsDuration(); /* @notice Thrown when a reward token still has a balance */ error RewardTokenStillHasBalance(uint256 balance); /* @notice Thrown when the index is out of bounds */ error IndexOutOfBounds(); /* @notice Thrown when the rewards duration is zero */ error RewardsDurationCannotBeZero(); /* @notice Thrown when attempting to unstake zero tokens */ error CannotUnstakeZero(); /* @notice Thrown when the rewards duration is too long */ error RewardsDurationTooLong(); /** * @notice Thrown when the receiver is the zero address */ error CannotStakeToZeroAddress(); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {IFleetCommander} from "../interfaces/IFleetCommander.sol"; import {IFleetCommanderRewardsManager} from "../interfaces/IFleetCommanderRewardsManager.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {StakingRewardsManagerBase, EnumerableSet} from "@summerfi/rewards-contracts/contracts/StakingRewardsManagerBase.sol"; import {IStakingRewardsManagerBase} from "@summerfi/rewards-contracts/interfaces/IStakingRewardsManagerBase.sol"; /** * @title FleetCommanderRewardsManager * @notice Contract for managing staking rewards specific to the Fleet system * @dev Extends StakingRewardsManagerBase with Fleet-specific functionality */ contract FleetCommanderRewardsManager is IFleetCommanderRewardsManager, StakingRewardsManagerBase { using EnumerableSet for EnumerableSet.AddressSet; address public immutable fleetCommander; /** * @notice Initializes the FleetStakingRewardsManager contract * @param _accessManager Address of the AccessManager contract * @param _fleetCommander Address of the FleetCommander contract */ constructor( address _accessManager, address _fleetCommander ) StakingRewardsManagerBase(_accessManager) { fleetCommander = _fleetCommander; stakingToken = fleetCommander; } /// @inheritdoc IStakingRewardsManagerBase function stakeOnBehalfOf( address receiver, uint256 amount ) external override updateReward(receiver) { _stake(_msgSender(), receiver, amount); } /// @inheritdoc IStakingRewardsManagerBase function notifyRewardAmount( address rewardToken, uint256 reward, uint256 newRewardsDuration ) external override(StakingRewardsManagerBase, IStakingRewardsManagerBase) onlyGovernor updateReward(address(0)) { if (address(rewardToken) == address(stakingToken)) { revert CantAddStakingTokenAsReward(); } _notifyRewardAmount(rewardToken, reward, newRewardsDuration); } function unstakeAndWithdrawOnBehalfOf( address owner, uint256 amount, bool claimRewards ) external override updateReward(owner) { // Check if the caller is the same as the 'owner' address or has the required role if (_msgSender() != owner && !hasAdmiralsQuartersRole(_msgSender())) { revert CallerNotAdmiralsQuarters(); } _unstake(owner, address(this), amount); IFleetCommander(fleetCommander).redeem(amount, owner, address(this)); if (claimRewards) { uint256 rewardTokenCount = _rewardTokensList.length(); for (uint256 i = 0; i < rewardTokenCount; i++) { address rewardTokenAddress = _rewardTokensList.at(i); _getReward(owner, rewardTokenAddress); } } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; /** * @title IArkConfigProviderErrors * @dev This file contains custom error definitions for the ArkConfigProvider contract. * @notice These custom errors provide more gas-efficient and informative error handling * compared to traditional require statements with string messages. */ interface IArkConfigProviderErrors { /** * @notice Thrown when attempting to deploy an Ark without specifying a configuration manager. */ error CannotDeployArkWithoutConfigurationManager(); /** * @notice Thrown when attempting to deploy an Ark without specifying a Raft address. */ error CannotDeployArkWithoutRaft(); /** * @notice Thrown when attempting to deploy an Ark without specifying a token address. */ error CannotDeployArkWithoutToken(); /** * @notice Thrown when attempting to deploy an Ark with an empty name. */ error CannotDeployArkWithEmptyName(); /** * @notice Thrown when an invalid vault address is provided. */ error InvalidVaultAddress(); /** * @notice Thrown when there's a mismatch between expected and actual assets in an ERC4626 operation. */ error ERC4626AssetMismatch(); /** * @notice Thrown when the max deposit percentage of TVL is greater than 100%. */ error MaxDepositPercentageOfTVLTooHigh(); /** * @notice Thrown when attempting to register a FleetCommander when one is already registered. */ error FleetCommanderAlreadyRegistered(); /** * @notice Thrown when attempting to unregister a FleetCommander by a non-registered address. */ error FleetCommanderNotRegistered(); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; /** * @title IArkErrors * @dev This file contains custom error definitions for the Ark contract. * @notice These custom errors provide more gas-efficient and informative error handling * compared to traditional require statements with string messages. */ interface IArkErrors { /** * @notice Thrown when attempting to remove a commander from an Ark that still has assets. */ error CannotRemoveCommanderFromArkWithAssets(); /** * @notice Thrown when trying to add a commander to an Ark that already has one. */ error CannotAddCommanderToArkWithCommander(); /** * @notice Thrown when attempting to use keeper data when it's not required. */ error CannotUseKeeperDataWhenNotRequired(); /** * @notice Thrown when keeper data is required but not provided. */ error KeeperDataRequired(); /** * @notice Thrown when invalid board data is provided. */ error InvalidBoardData(); /** * @notice Thrown when invalid disembark data is provided. */ error InvalidDisembarkData(); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; /** * @title IFleetCommanderConfigProviderErrors * @dev This file contains custom error definitions for the FleetCommanderConfigProvider contract. * @notice These custom errors provide more gas-efficient and informative error handling * compared to traditional require statements with string messages. */ interface IFleetCommanderConfigProviderErrors { /** * @notice Thrown when an operation is attempted on a non-existent Ark * @param ark The address of the Ark that was not found */ error FleetCommanderArkNotFound(address ark); /** * @notice Thrown when trying to remove an Ark that still has a non-zero deposit cap * @param ark The address of the Ark with a non-zero deposit cap */ error FleetCommanderArkDepositCapGreaterThanZero(address ark); /** * @notice Thrown when attempting to remove an Ark that still holds assets * @param ark The address of the Ark with non-zero assets */ error FleetCommanderArkAssetsNotZero(address ark); /** * @notice Thrown when trying to add an Ark that already exists in the system * @param ark The address of the Ark that already exists */ error FleetCommanderArkAlreadyExists(address ark); /** * @notice Thrown when an invalid Ark address is provided (e.g., zero address) */ error FleetCommanderInvalidArkAddress(); /** * @notice Thrown when trying to set a StakingRewardsManager to the zero address */ error FleetCommanderInvalidStakingRewardsManager(); /** * @notice Thrown when trying to set a max rebalance operations to a value greater than the max allowed * @param newMaxRebalanceOperations The new max rebalance operations value */ error FleetCommanderMaxRebalanceOperationsTooHigh( uint256 newMaxRebalanceOperations ); /** * @notice Thrown when the asset of the Ark does not match the asset of the FleetCommander */ error FleetCommanderAssetMismatch(); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; /** * @title IFleetCommanderErrors * @dev This file contains custom error definitions for the FleetCommander contract. * @notice These custom errors provide more gas-efficient and informative error handling * compared to traditional require statements with string messages. */ interface IFleetCommanderErrors { /** * @notice Thrown when transfers are disabled. */ error FleetCommanderTransfersDisabled(); /** * @notice Thrown when an operation is attempted on an inactive Ark. * @param ark The address of the inactive Ark. */ error FleetCommanderArkNotActive(address ark); /** * @notice Thrown when attempting to rebalance to an invalid Ark. * @param ark The address of the invalid Ark. * @param amount Amount of tokens added to target ark * @param effectiveDepositCap Effective deposit cap of the ark (minimum of % of fleet TVL or arbitrary ark deposit * cap) */ error FleetCommanderEffectiveDepositCapExceeded( address ark, uint256 amount, uint256 effectiveDepositCap ); /** * @notice Thrown when there is insufficient buffer for an operation. */ error FleetCommanderInsufficientBuffer(); /** * @notice Thrown when a rebalance operation is attempted with no actual operations. */ error FleetCommanderRebalanceNoOperations(); /** * @notice Thrown when a rebalance operation exceeds the maximum allowed number of operations. * @param operationsCount The number of operations attempted. */ error FleetCommanderRebalanceTooManyOperations(uint256 operationsCount); /** * @notice Thrown when a rebalance amount for an Ark is zero. * @param ark The address of the Ark with zero rebalance amount. */ error FleetCommanderRebalanceAmountZero(address ark); /** * @notice Thrown when a withdrawal amount exceeds the maximum buffer limit. */ error WithdrawalAmountExceedsMaxBufferLimit(); /** * @notice Thrown when an Ark's deposit cap is zero. * @param ark The address of the Ark with zero deposit cap. */ error FleetCommanderArkDepositCapZero(address ark); /** * @notice Thrown when no funds were moved in an operation that expected fund movement. */ error FleetCommanderNoFundsMoved(); /** * @notice Thrown when there are no excess funds to perform an operation. */ error FleetCommanderNoExcessFunds(); /** * @notice Thrown when an invalid source Ark is specified for an operation. * @param ark The address of the invalid source Ark. */ error FleetCommanderInvalidSourceArk(address ark); /** * @notice Thrown when an operation attempts to move more funds than available. */ error FleetCommanderMovedMoreThanAvailable(); /** * @notice Thrown when an unauthorized withdrawal is attempted. * @param caller The address attempting the withdrawal. * @param owner The address of the authorized owner. */ error FleetCommanderUnauthorizedWithdrawal(address caller, address owner); /** * @notice Thrown when an unauthorized redemption is attempted. * @param caller The address attempting the redemption. * @param owner The address of the authorized owner. */ error FleetCommanderUnauthorizedRedemption(address caller, address owner); /** * @notice Thrown when attempting to use rebalance on a buffer Ark. */ error FleetCommanderCantUseRebalanceOnBufferArk(); /** * @notice Thrown when attempting to use the maximum uint value for buffer adjustment from buffer. */ error FleetCommanderCantUseMaxUintMovingFromBuffer(); /** * @notice Thrown when a rebalance operation exceeds the maximum outflow for an Ark. * @param fromArk The address of the Ark from which funds are being moved. * @param amount The amount being moved. * @param maxRebalanceOutflow The maximum allowed outflow. */ error FleetCommanderExceedsMaxOutflow( address fromArk, uint256 amount, uint256 maxRebalanceOutflow ); /** * @notice Thrown when a rebalance operation exceeds the maximum inflow for an Ark. * @param fromArk The address of the Ark to which funds are being moved. * @param amount The amount being moved. * @param maxRebalanceInflow The maximum allowed inflow. */ error FleetCommanderExceedsMaxInflow( address fromArk, uint256 amount, uint256 maxRebalanceInflow ); /** * @notice Thrown when the staking rewards manager is not set. */ error FleetCommanderStakingRewardsManagerNotSet(); /** * @notice Thrown when user attempts to deposit/mint or withdraw/redeem 0 units */ error FleetCommanderZeroAmount(); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {Percentage} from "@summerfi/percentage-solidity/contracts/Percentage.sol"; /** * @title IArkConfigProviderEvents * @notice Interface for events emitted by ArkConfigProvider contracts */ interface IArkConfigProviderEvents { /** * @notice Emitted when the deposit cap of the Ark is updated * @param newCap The new deposit cap value */ event DepositCapUpdated(uint256 newCap); /** * @notice Emitted when the maximum deposit percentage of TVL is updated * @param newMaxDepositPercentageOfTVL The new maximum deposit percentage of TVL */ event MaxDepositPercentageOfTVLUpdated( Percentage newMaxDepositPercentageOfTVL ); /** * @notice Emitted when the Raft address associated with the Ark is updated * @param newRaft The address of the new Raft */ event RaftUpdated(address newRaft); /** * @notice Emitted when the maximum outflow limit for the Ark during rebalancing is updated * @param newMaxOutflow The new maximum amount that can be transferred out of the Ark during a rebalance */ event MaxRebalanceOutflowUpdated(uint256 newMaxOutflow); /** * @notice Emitted when the maximum inflow limit for the Ark during rebalancing is updated * @param newMaxInflow The new maximum amount that can be transferred into the Ark during a rebalance */ event MaxRebalanceInflowUpdated(uint256 newMaxInflow); /** * @notice Emitted when the Fleet Commander is registered * @param commander The address of the Fleet Commander */ event FleetCommanderRegistered(address commander); /** * @notice Emitted when the Fleet Commander is unregistered * @param commander The address of the Fleet Commander */ event FleetCommanderUnregistered(address commander); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; /** * @title IArkEvents * @notice Interface for events emitted by Ark contracts */ interface IArkEvents { /** * @notice Emitted when rewards are harvested from an Ark * @param rewardTokens The addresses of the harvested reward tokens * @param rewardAmounts The amounts of the harvested reward tokens */ event ArkHarvested( address[] indexed rewardTokens, uint256[] indexed rewardAmounts ); /** * @notice Emitted when tokens are boarded (deposited) into the Ark * @param commander The address of the FleetCommander initiating the boarding * @param token The address of the token being boarded * @param amount The amount of tokens boarded */ event Boarded(address indexed commander, address token, uint256 amount); /** * @notice Emitted when tokens are disembarked (withdrawn) from the Ark * @param commander The address of the FleetCommander initiating the disembarking * @param token The address of the token being disembarked * @param amount The amount of tokens disembarked */ event Disembarked(address indexed commander, address token, uint256 amount); /** * @notice Emitted when tokens are moved from one address to another * @param from Ark being boarded from * @param to Ark being boarded to * @param token The address of the token being moved * @param amount The amount of tokens moved */ event Moved( address indexed from, address indexed to, address token, uint256 amount ); /** * @notice Emitted when the Ark is poked and the share price is updated * @param currentPrice Current share price of the Ark * @param timestamp The timestamp of the poke */ event ArkPoked(uint256 currentPrice, uint256 timestamp); /** * @notice Emitted when the Ark is swept * @param sweptTokens The addresses of the swept tokens * @param sweptAmounts The amounts of the swept tokens */ event ArkSwept( address[] indexed sweptTokens, uint256[] indexed sweptAmounts ); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; interface IFleetCommanderConfigProviderEvents { /** * @notice Emitted when the deposit cap is updated * @param newCap The new deposit cap value */ event FleetCommanderDepositCapUpdated(uint256 newCap); /** * @notice Emitted when a new Ark is added * @param ark The address of the newly added Ark */ event ArkAdded(address indexed ark); /** * @notice Emitted when an Ark is removed * @param ark The address of the removed Ark */ event ArkRemoved(address indexed ark); /** * @notice Emitted when new minimum funds buffer balance is set * @param newBalance New minimum funds buffer balance */ event FleetCommanderminimumBufferBalanceUpdated(uint256 newBalance); /** * @notice Emitted when new max allowed rebalance operations is set * @param newMaxRebalanceOperations Max allowed rebalance operations */ event FleetCommanderMaxRebalanceOperationsUpdated( uint256 newMaxRebalanceOperations ); /** * @notice Emitted when the staking rewards contract address is updated * @param newStakingRewards The address of the new staking rewards contract */ event FleetCommanderStakingRewardsUpdated(address newStakingRewards); /** * @notice Emitted when the transfer enabled status is updated */ event TransfersEnabled(); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {RebalanceData} from "../types/FleetCommanderTypes.sol"; interface IFleetCommanderEvents { /* EVENTS */ /** * @notice Emitted when a rebalance operation is completed * @param keeper The address of the keeper who initiated the rebalance * @param rebalances An array of RebalanceData structs detailing the rebalance operations */ event Rebalanced(address indexed keeper, RebalanceData[] rebalances); /** * @notice Emitted when queued funds are committed * @param keeper The address of the keeper who committed the funds * @param prevBalance The previous balance before committing funds * @param newBalance The new balance after committing funds */ event QueuedFundsCommitted( address indexed keeper, uint256 prevBalance, uint256 newBalance ); /** * @notice Emitted when the funds queue is refilled * @param keeper The address of the keeper who initiated the queue refill * @param prevBalance The previous balance before refilling * @param newBalance The new balance after refilling */ event FundsQueueRefilled( address indexed keeper, uint256 prevBalance, uint256 newBalance ); /** * @notice Emitted when the minimum balance of the funds queue is updated * @param keeper The address of the keeper who updated the minimum balance * @param newBalance The new minimum balance */ event MinFundsQueueBalanceUpdated( address indexed keeper, uint256 newBalance ); /** * @notice Emitted when the fee address is updated * @param newAddress The new fee address */ event FeeAddressUpdated(address newAddress); /** * @notice Emitted when the funds buffer balance is updated * @param user The address of the user who triggered the update * @param prevBalance The previous buffer balance * @param newBalance The new buffer balance */ event FundsBufferBalanceUpdated( address indexed user, uint256 prevBalance, uint256 newBalance ); /** * @notice Emitted when funds are withdrawn from Arks * @param owner The address of the owner who initiated the withdrawal * @param receiver The address of the receiver of the withdrawn funds * @param totalWithdrawn The total amount of funds withdrawn */ event FleetCommanderWithdrawnFromArks( address indexed owner, address receiver, uint256 totalWithdrawn ); /** * @notice Emitted when funds are redeemed from Arks * @param owner The address of the owner who initiated the redemption * @param receiver The address of the receiver of the redeemed funds * @param totalRedeemed The total amount of funds redeemed */ event FleetCommanderRedeemedFromArks( address indexed owner, address receiver, uint256 totalRedeemed ); /** * @notice Emitted when referee deposits into the FleetCommander * @param referee The address of the referee who was referred * @param referralCode The referral code of the referrer */ event FleetCommanderReferral( address indexed referee, bytes indexed referralCode ); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {IArkErrors} from "../errors/IArkErrors.sol"; import {IArkEvents} from "../events/IArkEvents.sol"; import {IArkAccessManaged} from "./IArkAccessManaged.sol"; import {IArkConfigProvider} from "./IArkConfigProvider.sol"; /** * @title IArk * @notice Interface for the Ark contract, which manages funds and interacts with Rafts * @dev Inherits from IArkAccessManaged for access control and IArkEvents for event definitions */ interface IArk is IArkAccessManaged, IArkEvents, IArkErrors, IArkConfigProvider { /** * @notice Returns the current underlying balance of the Ark * @return The total assets in the Ark, in token precision */ function totalAssets() external view returns (uint256); /** * @notice Triggers a harvest operation to collect rewards * @param additionalData Optional bytes that might be required by a specific protocol to harvest * @return rewardTokens The reward token addresses * @return rewardAmounts The reward amounts */ function harvest( bytes calldata additionalData ) external returns (address[] memory rewardTokens, uint256[] memory rewardAmounts); /** * @notice Sweeps tokens from the Ark * @param tokens The tokens to sweep * @return sweptTokens The swept tokens * @return sweptAmounts The swept amounts */ function sweep( address[] calldata tokens ) external returns (address[] memory sweptTokens, uint256[] memory sweptAmounts); /** * @notice Deposits (boards) tokens into the Ark * @dev This function is called by the Fleet Commander to deposit assets into the Ark. * It transfers tokens from the caller to this contract and then calls the internal _board function. * @param amount The amount of assets to board * @param boardData Additional data required for boarding, specific to the Ark implementation * @custom:security-note This function is only callable by authorized entities */ function board(uint256 amount, bytes calldata boardData) external; /** * @notice Withdraws (disembarks) tokens from the Ark * @param amount The amount of tokens to withdraw * @param disembarkData Additional data that might be required by a specific protocol to withdraw funds */ function disembark(uint256 amount, bytes calldata disembarkData) external; /** * @notice Moves tokens from one ark to another * @param amount The amount of tokens to move * @param receiver The address of the Ark the funds will be boarded to * @param boardData Additional data that might be required by a specific protocol to board funds * @param disembarkData Additional data that might be required by a specific protocol to disembark funds */ function move( uint256 amount, address receiver, bytes calldata boardData, bytes calldata disembarkData ) external; /** * @notice Internal function to get the total assets that are withdrawable * @return uint256 The total assets that are withdrawable * @dev _withdrawableTotalAssets is an internal function that should be implemented by derived contracts to define * specific withdrawability logic * @dev the ark is withdrawable if it doesnt require keeper data and _isWithdrawable returns true */ function withdrawableTotalAssets() external view returns (uint256); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; /** * @title IArkAccessManaged * @notice Extends the ProtocolAccessManaged contract with Ark specific AccessControl * Used to specifically tie one FleetCommander to each Ark * * @dev One Ark specific role is defined: * - Commander: is the fleet commander contract itself and couples an * Ark to specific Fleet Commander * * The Commander role is still declared on the access manager to centralise * role definitions. */ interface IArkAccessManaged {}
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {IArkConfigProviderErrors} from "../errors/IArkConfigProviderErrors.sol"; import {IArkAccessManaged} from "./IArkAccessManaged.sol"; import {IArkConfigProviderEvents} from "../events/IArkConfigProviderEvents.sol"; import {ArkConfig} from "../types/ArkTypes.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Percentage} from "@summerfi/percentage-solidity/contracts/Percentage.sol"; /** * @title IArkConfigProvider * @notice Interface for configuration of Ark contracts * @dev Inherits from IArkAccessManaged for access control and IArkConfigProviderEvents for event definitions */ interface IArkConfigProvider is IArkAccessManaged, IArkConfigProviderErrors, IArkConfigProviderEvents { /** * @notice Retrieves the current fleet config */ function getConfig() external view returns (ArkConfig memory); /** * @dev Returns the name of the Ark. * @return The name of the Ark as a string. */ function name() external view returns (string memory); /** * @notice Returns the details of the Ark * @return The details of the Ark as a string */ function details() external view returns (string memory); /** * @notice Returns the deposit cap for this Ark * @return The maximum amount of tokens that can be deposited into the Ark */ function depositCap() external view returns (uint256); /** * @notice Returns the maximum percentage of TVL that can be deposited into the Ark * @return The maximum percentage of TVL that can be deposited into the Ark */ function maxDepositPercentageOfTVL() external view returns (Percentage); /** * @notice Returns the maximum amount that can be moved to this Ark in one rebalance * @return maximum amount that can be moved to this Ark in one rebalance */ function maxRebalanceInflow() external view returns (uint256); /** * @notice Returns the maximum amount that can be moved from this Ark in one rebalance * @return maximum amount that can be moved from this Ark in one rebalance */ function maxRebalanceOutflow() external view returns (uint256); /** * @notice Returns whether the Ark requires keeper data to board/disembark * @return true if the Ark requires keeper data, false otherwise */ function requiresKeeperData() external view returns (bool); /** * @notice Returns the ERC20 token managed by this Ark * @return The IERC20 interface of the managed token */ function asset() external view returns (IERC20); /** * @notice Returns the address of the Fleet commander managing the ark * @return address Address of Fleet commander managing the ark if a Commander is assigned, address(0) otherwise */ function commander() external view returns (address); /** * @notice Sets a new maximum allocation for the Ark * @param newDepositCap The new maximum allocation amount */ function setDepositCap(uint256 newDepositCap) external; /** * @notice Sets a new maximum deposit percentage of TVL for the Ark * @param newMaxDepositPercentageOfTVL The new maximum deposit percentage of TVL */ function setMaxDepositPercentageOfTVL( Percentage newMaxDepositPercentageOfTVL ) external; /** * @notice Sets a new maximum amount that can be moved from the Ark in one rebalance * @param newMaxRebalanceOutflow The new maximum amount that can be moved from the Ark */ function setMaxRebalanceOutflow(uint256 newMaxRebalanceOutflow) external; /** * @notice Sets a new maximum amount that can be moved to the Ark in one rebalance * @param newMaxRebalanceInflow The new maximum amount that can be moved to the Ark */ function setMaxRebalanceInflow(uint256 newMaxRebalanceInflow) external; /** * @notice Registers the Fleet commander for the Ark * @dev This function is used to register the Fleet commander for the Ark * it's called by the FleetCommander when ark is added to the fleet */ function registerFleetCommander() external; /** * @notice Unregisters the Fleet commander for the Ark * @dev This function is used to unregister the Fleet commander for the Ark * it's called by the FleetCommander when ark is removed from the fleet * all balance checks are done within the FleetCommander */ function unregisterFleetCommander() external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {IFleetCommanderErrors} from "../errors/IFleetCommanderErrors.sol"; import {IFleetCommanderEvents} from "../events/IFleetCommanderEvents.sol"; import {RebalanceData} from "../types/FleetCommanderTypes.sol"; import {IFleetCommanderConfigProvider} from "./IFleetCommanderConfigProvider.sol"; import {IERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; import {Percentage} from "@summerfi/percentage-solidity/contracts/Percentage.sol"; /** * @title IFleetCommander Interface * @notice Interface for the FleetCommander contract, which manages asset allocation across multiple Arks */ interface IFleetCommander is IERC4626, IFleetCommanderEvents, IFleetCommanderErrors, IFleetCommanderConfigProvider { /** * @notice Returns the total assets that are currently withdrawable from the FleetCommander. * @dev If cached data is available, it will be used. Otherwise, it will be calculated on demand (and cached) * @return uint256 The total amount of assets that can be withdrawn. */ function withdrawableTotalAssets() external view returns (uint256); /** * @notice Returns the total assets that are managed the FleetCommander. * @dev If cached data is available, it will be used. Otherwise, it will be calculated on demand (and cached) * @return uint256 The total amount of assets that can be withdrawn. */ function totalAssets() external view returns (uint256); /** * @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the * Vault, directly from Buffer. * @param owner The address of the owner of the assets * @return uint256 The maximum amount that can be withdrawn. */ function maxBufferWithdraw(address owner) external view returns (uint256); /** * @notice Returns the maximum amount of the underlying asset that can be redeemed from the owner balance in the * Vault, directly from Buffer. * @param owner The address of the owner of the assets * @return uint256 The maximum amount that can be redeemed. */ function maxBufferRedeem(address owner) external view returns (uint256); /* FUNCTIONS - PUBLIC - USER */ /** * @notice Deposits a specified amount of assets into the contract for a given receiver. * @param assets The amount of assets to be deposited. * @param receiver The address of the receiver who will receive the deposited assets. * @param referralCode An optional referral code that can be used for tracking or rewards. */ function deposit( uint256 assets, address receiver, bytes memory referralCode ) external returns (uint256); /** * @notice Forces a withdrawal of assets from the FleetCommander * @param assets The amount of assets to forcefully withdraw * @param receiver The address that will receive the withdrawn assets * @param owner The address of the owner of the assets * @return shares The amount of shares redeemed */ function withdrawFromArks( uint256 assets, address receiver, address owner ) external returns (uint256 shares); /** * @notice Withdraws a specified amount of assets from the FleetCommander * @dev This function first attempts to withdraw from the buffer. If the buffer doesn't have enough assets, * it will withdraw from the arks. It also handles the case where the maximum possible amount is requested. * @param assets The amount of assets to withdraw. If set to type(uint256).max, it will withdraw the maximum * possible amount. * @param receiver The address that will receive the withdrawn assets * @param owner The address of the owner of the shares * @return shares The number of shares burned in exchange for the withdrawn assets */ function withdraw( uint256 assets, address receiver, address owner ) external returns (uint256 shares); /** * @notice Redeems a specified amount of shares from the FleetCommander * @dev This function first attempts to redeem from the buffer. If the buffer doesn't have enough assets, * it will redeem from the arks. It also handles the case where the maximum possible amount is requested. * @param shares The number of shares to redeem. If set to type(uint256).max, it will redeem all shares owned by the * owner. * @param receiver The address that will receive the redeemed assets * @param owner The address of the owner of the shares * @return assets The amount of assets received in exchange for the redeemed shares */ function redeem( uint256 shares, address receiver, address owner ) external returns (uint256 assets); /** * @notice Redeems shares for assets from the FleetCommander * @param shares The amount of shares to redeem * @param receiver The address that will receive the assets * @param owner The address of the owner of the shares * @return assets The amount of assets forcefully withdrawn */ function redeemFromArks( uint256 shares, address receiver, address owner ) external returns (uint256 assets); /** * @notice Redeems shares for assets directly from the Buffer * @param shares The amount of shares to redeem * @param receiver The address that will receive the assets * @param owner The address of the owner of the shares * @return assets The amount of assets redeemed */ function redeemFromBuffer( uint256 shares, address receiver, address owner ) external returns (uint256 assets); /** * @notice Forces a withdrawal of assets directly from the Buffer * @param assets The amount of assets to withdraw * @param receiver The address that will receive the withdrawn assets * @param owner The address of the owner of the assets * @return shares The amount of shares redeemed */ function withdrawFromBuffer( uint256 assets, address receiver, address owner ) external returns (uint256 shares); /** * @notice Accrues and distributes tips * @return uint256 The amount of tips accrued */ function tip() external returns (uint256); /** * @notice Rebalances the assets across Arks, including buffer adjustments * @param data Array of RebalanceData structs * @dev RebalanceData struct contains: * - fromArk: The address of the Ark to move assets from * - toArk: The address of the Ark to move assets to * - amount: The amount of assets to move * - boardData: Additional data for the board operation * - disembarkData: Additional data for the disembark operation * @dev Using type(uint256).max as the amount will move all assets from the fromArk to the toArk * @dev For standard rebalancing: * - Operations cannot involve the buffer Ark directly * @dev For buffer adjustments: * - type(uint256).max is only allowed when moving TO the buffer * - When withdrawing FROM buffer, total amount cannot reduce balance below minFundsBufferBalance * @dev The number of operations in a single rebalance call is limited to MAX_REBALANCE_OPERATIONS * @dev Rebalance is subject to a cooldown period between calls * @dev Only callable by accounts with the Keeper role */ function rebalance(RebalanceData[] calldata data) external; /* FUNCTIONS - EXTERNAL - GOVERNANCE */ /** * @notice Sets a new tip rate for the FleetCommander * @dev Only callable by the governor * @dev The tip rate is set as a Percentage. Percentages use 18 decimals of precision * For example, for a 5% rate, you'd pass 5 * 1e18 (5 000 000 000 000 000 000) * @param newTipRate The new tip rate as a Percentage */ function setTipRate(Percentage newTipRate) external; /** * @notice Sets a new minimum pause time for the FleetCommander * @dev Only callable by the governor * @param newMinimumPauseTime The new minimum pause time in seconds */ function setMinimumPauseTime(uint256 newMinimumPauseTime) external; /** * @notice Updates the rebalance cooldown period * @param newCooldown The new cooldown period in seconds */ function updateRebalanceCooldown(uint256 newCooldown) external; /** * @notice Forces a rebalance operation * @param data Array of typed rebalance data struct * @dev has no cooldown enforced but only callable by privileged role */ function forceRebalance(RebalanceData[] calldata data) external; /** * @notice Pauses the FleetCommander * @dev This function is used to pause the FleetCommander in case of critical issues or emergencies * @dev Only callable by the guardian or governor */ function pause() external; /** * @notice Unpauses the FleetCommander * @dev This function is used to resume normal operations after a pause * @dev Only callable by the guardian or governor */ function unpause() external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {IFleetCommanderConfigProviderErrors} from "../errors/IFleetCommanderConfigProviderErrors.sol"; import {IFleetCommanderConfigProviderEvents} from "../events/IFleetCommanderConfigProviderEvents.sol"; import {FleetConfig} from "../types/FleetCommanderTypes.sol"; import {Percentage} from "@summerfi/percentage-solidity/contracts/Percentage.sol"; /** * @title IFleetCommander Interface * @notice Interface for the FleetCommander contract, which manages asset allocation across multiple Arks */ interface IFleetCommanderConfigProvider is IFleetCommanderConfigProviderErrors, IFleetCommanderConfigProviderEvents { /** * @notice Retrieves the ark address at the specified index * @param index The index of the ark in the arks array * @return The address of the ark at the specified index */ function arks(uint256 index) external view returns (address); /** * @notice Retrieves the arks currently linked to fleet (excluding the buffer ark) */ function getActiveArks() external view returns (address[] memory); /** * @notice Retrieves the current fleet config */ function getConfig() external view returns (FleetConfig memory); /** * @notice Retrieves the buffer ark address */ function bufferArk() external view returns (address); /** * @notice Checks if the ark is part of the fleet or is the buffer ark * @param ark The address of the Ark * @return bool Returns true if the ark is active or the buffer ark, false otherwise. */ function isArkActiveOrBufferArk(address ark) external view returns (bool); /* FUNCTIONS - EXTERNAL - GOVERNANCE */ /** * @notice Adds a new Ark * @param ark The address of the new Ark */ function addArk(address ark) external; /** * @notice Removes an existing Ark * @param ark The address of the Ark to remove */ function removeArk(address ark) external; /** * @notice Sets a new deposit cap for Fleet * @param newDepositCap The new deposit cap */ function setFleetDepositCap(uint256 newDepositCap) external; /** * @notice Sets a new deposit cap for an Ark * @param ark The address of the Ark * @param newDepositCap The new deposit cap */ function setArkDepositCap(address ark, uint256 newDepositCap) external; /** * @notice Sets the max deposit percentage of TVL for an Ark * @param ark The address of the Ark * @param newMaxDepositPercentageOfTVL The new max deposit percentage of TVL */ function setArkMaxDepositPercentageOfTVL( address ark, Percentage newMaxDepositPercentageOfTVL ) external; /** * @dev Sets the minimum buffer balance for the fleet commander. * @param newMinimumBalance The new minimum buffer balance to be set. */ function setMinimumBufferBalance(uint256 newMinimumBalance) external; /** * @dev Sets the minimum number of allowe rebalance operations. * @param newMaxRebalanceOperations The new maximum allowed rebalance operations. */ function setMaxRebalanceOperations( uint256 newMaxRebalanceOperations ) external; /** * @notice Sets the maxRebalanceOutflow for an Ark * @dev Only callable by the governor * @param ark The address of the Ark * @param newMaxRebalanceOutflow The new maxRebalanceOutflow value */ function setArkMaxRebalanceOutflow( address ark, uint256 newMaxRebalanceOutflow ) external; /** * @notice Sets the maxRebalanceInflow for an Ark * @dev Only callable by the governor * @param ark The address of the Ark * @param newMaxRebalanceInflow The new maxRebalanceInflow value */ function setArkMaxRebalanceInflow( address ark, uint256 newMaxRebalanceInflow ) external; /** * @notice Deploys and sets the staking rewards manager contract address */ function updateStakingRewardsManager() external; /** * @notice Enables or disables transfers of fleet commander shares * @dev Only callable by the governor when not paused */ function setFleetTokenTransferability() external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {IStakingRewardsManagerBase} from "@summerfi/rewards-contracts/interfaces/IStakingRewardsManagerBase.sol"; /** * @title IFleetCommanderRewardsManager * @notice Interface for the FleetStakingRewardsManager contract * @dev Extends IStakingRewardsManagerBase with Fleet-specific functionality */ interface IFleetCommanderRewardsManager is IStakingRewardsManagerBase { /** * @notice Returns the address of the FleetCommander contract * @return The address of the FleetCommander */ function fleetCommander() external view returns (address); /** * @notice Thrown when a non-AdmiralsQuarters contract tries * to unstake on behalf */ error CallerNotAdmiralsQuarters(); /** * @notice Thrown when AdmiralsQuarters tries to unstake for * someone other than msg.sender */ error InvalidUnstakeRecipient(); /* @notice Thrown when trying to add a staking token as a reward token */ error CantAddStakingTokenAsReward(); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; /** * @title IFleetCommanderRewardsManagerFactory * @notice Interface for the FleetCommanderRewardsManagerFactory contract * @dev Defines the interface for creating new FleetCommanderRewardsManager instances */ interface IFleetCommanderRewardsManagerFactory { /** * @notice Event emitted when a new rewards manager is created * @param rewardsManager Address of the newly created rewards manager * @param fleetCommander Address of the fleet commander associated with the rewards manager */ event RewardsManagerCreated( address indexed rewardsManager, address indexed fleetCommander ); /** * @notice Creates a new FleetCommanderRewardsManager instance * @param accessManager Address of the access manager to associate with the rewards manager * @param fleetCommander Address of the fleet commander to associate with the rewards manager * @return Address of the newly created rewards manager */ function createRewardsManager( address accessManager, address fleetCommander ) external returns (address); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Percentage} from "@summerfi/percentage-solidity/contracts/Percentage.sol"; /** * @title ArkParams * @notice Constructor parameters for the Ark contract * * @dev This struct is used to initialize an Ark contract with all necessary parameters */ struct ArkParams { /** * @notice The name of the Ark * @dev This should be a unique, human-readable identifier for the Ark */ string name; /** * @notice Additional details about the Ark * @dev This can be used to store additional information about the Ark */ string details; /** * @notice The address of the access manager contract * @dev This contract manages roles and permissions for the Ark */ address accessManager; /** * @notice The address of the configuration manager contract * @dev This contract stores global configuration parameters */ address configurationManager; /** * @notice The address of the ERC20 token managed by this Ark * @dev This is the underlying asset that the Ark will handle */ address asset; /** * @notice The maximum amount of tokens that can be deposited into the Ark * @dev This cap helps to manage risk and exposure */ uint256 depositCap; /** * @notice The maximum amount of tokens that can be moved from this Ark in a single transaction * @dev This limit helps to prevent large, sudden outflows */ uint256 maxRebalanceOutflow; /** * @notice The maximum amount of tokens that can be moved to this Ark in a single transaction * @dev This limit helps to prevent large, sudden inflows */ uint256 maxRebalanceInflow; /** * @notice Whether the Ark requires Keepr data to be passed in with rebalance transactions * @dev This flag is used to determine whether Keepr data is required for rebalance transactions */ bool requiresKeeperData; /** * @notice The maximum percentage of Total Value Locked (TVL) that can be deposited into this Ark * @dev This value is represented as a percentage with 18 decimal places (1e18 = 100%) * For example, 0.5e18 represents 50% of TVL */ Percentage maxDepositPercentageOfTVL; } /** * @title ArkConfig * @notice Configuration of the Ark contract * @dev This struct stores the current configuration of an Ark, which can be updated during its lifecycle */ struct ArkConfig { /** * @notice The address of the commander (typically a FleetCommander contract) * @dev The commander has special permissions to manage the Ark */ address commander; /** * @notice The address of the associated Raft contract * @dev The Raft contract handles reward distribution and other protocol-wide functions */ address raft; /** * @notice The ERC20 token interface for the asset managed by this Ark * @dev This allows direct interaction with the token contract */ IERC20 asset; /** * @notice The current maximum amount of tokens that can be deposited into the Ark * @dev This can be adjusted by the commander to manage capacity */ uint256 depositCap; /** * @notice The current maximum amount of tokens that can be moved from this Ark in a single transaction * @dev This can be adjusted to manage liquidity and risk */ uint256 maxRebalanceOutflow; /** * @notice The current maximum amount of tokens that can be moved to this Ark in a single transaction * @dev This can be adjusted to manage inflows and capacity */ uint256 maxRebalanceInflow; /** * @notice The name of the Ark * @dev This is typically set at initialization and not changed */ string name; /** * @notice Additional details about the Ark * @dev This can be used to store additional information about the Ark */ string details; /** * @notice Whether the Ark requires Keeper data to be passed in with rebalance transactions * @dev This flag is used to determine whether Keeper data is required for rebalance transactions */ bool requiresKeeperData; /** * @notice The maximum percentage of Total Value Locked (TVL) that can be deposited into this Ark * @dev This value is represented as a percentage with 18 decimal places (1e18 = 100%) * For example, 0.5e18 represents 50% of TVL */ Percentage maxDepositPercentageOfTVL; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; import {IArk} from "../interfaces/IArk.sol"; import {IFleetCommanderRewardsManager} from "../interfaces/IFleetCommanderRewardsManager.sol"; import {Percentage} from "@summerfi/percentage-solidity/contracts/Percentage.sol"; /** * @notice Configuration parameters for the FleetCommander contract */ struct FleetCommanderParams { string name; string details; string symbol; address configurationManager; address accessManager; address asset; uint256 initialMinimumBufferBalance; uint256 initialRebalanceCooldown; uint256 depositCap; Percentage initialTipRate; } /** * @title FleetConfig * @notice Configuration parameters for the FleetCommander contract * @dev This struct encapsulates the mutable configuration settings of a FleetCommander. * These parameters can be updated during the contract's lifecycle to adjust its behavior. */ struct FleetConfig { /** * @notice The buffer Ark associated with this FleetCommander * @dev This Ark is used as a temporary holding area for funds before they are allocated * to other Arks or when they need to be quickly accessed for withdrawals. */ IArk bufferArk; /** * @notice The minimum balance that should be maintained in the buffer Ark * @dev This value is used to ensure there's always a certain amount of funds readily * available for withdrawals or rebalancing operations. It's denominated in the * smallest unit of the underlying asset (e.g., wei for ETH). */ uint256 minimumBufferBalance; /** * @notice The maximum total value of assets that can be deposited into the FleetCommander * @dev This cap helps manage the total assets under management and can be used to * implement controlled growth strategies. It's denominated in the smallest unit * of the underlying asset. */ uint256 depositCap; /** * @notice The maximum number of rebalance operations in a single rebalance */ uint256 maxRebalanceOperations; /** * @notice The address of the staking rewards contract */ address stakingRewardsManager; } /** * @notice Data structure for the rebalance event * @param fromArk The address of the Ark from which assets are moved * @param toArk The address of the Ark to which assets are moved * @param amount The amount of assets being moved * @param boardData The data to be passed to the `board` function of the `toArk` * @param disembarkData The data to be passed to the `disembark` function of the `fromArk` * @dev if the `boardData` or `disembarkData` is not needed, it should be an empty byte array */ struct RebalanceData { address fromArk; address toArk; uint256 amount; bytes boardData; bytes disembarkData; } /** * @title ArkData * @dev Struct to store information about an Ark. * This struct holds the address of the Ark and the total assets it holds. * @dev used in the caching mechanism for the FleetCommander */ struct ArkData { /// @notice The address of the Ark. address arkAddress; /// @notice The total assets held by the Ark. uint256 totalAssets; }
{ "optimizer": { "enabled": true, "runs": 50 }, "evmVersion": "cancun", "viaIR": true, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rewardsManager","type":"address"},{"indexed":true,"internalType":"address","name":"fleetCommander","type":"address"}],"name":"RewardsManagerCreated","type":"event"},{"inputs":[{"internalType":"address","name":"accessManager","type":"address"},{"internalType":"address","name":"fleetCommander","type":"address"}],"name":"createRewardsManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60808060405234601557611deb908161001a8239f35b5f80fdfe60808060405260043610156011575f80fd5b5f3560e01c630f07f829146023575f80fd5b3460e857604036600319011260e8576004356001600160a01b038116919082900360e8576024356001600160a01b038116929083900360e857611cc980830183811067ffffffffffffffff82111760d45760409284926100ed843981528460208201520301905ff090811560c95760209160018060a01b031660405191817f0b3397f9446e8b85cf96fe3194aeec84e2fd23ab014cf85f82805b36aef207aa5f80a38152f35b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fdfe60e0806040523461013957604081611cc9803803809161001f8285610163565b8339810103126101395761003e60206100378361019a565b920161019a565b906001600160a01b03168015610150576040516301ffc9a760e01b815263261c910560e21b6004820152602081602481855afa908115610145575f91610106575b50156100f45760805260c08190526001600160a01b031660a052604051611b1a90816101af82396080518181816105dc01528181610d68015281816110c2015261144b015260a05181818161062e015281816108c901528181611710015261177a015260c05181818161031e0152610a9c0152f35b6347bd7c1d60e01b5f5260045260245ffd5b90506020813d60201161013d575b8161012160209383610163565b8101031261013957518015158103610139575f61007f565b5f80fd5b3d9150610114565b6040513d5f823e3d90fd5b6347bd7c1d60e01b5f525f60045260245ffd5b601f909101601f19168101906001600160401b0382119082101761018657604052565b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036101395756fe60806040526004361015610011575f80fd5b5f3560e01c8063055de275146111e057806318160ddd146111c3578063211dc32d146111975780632378bea61461107457806324ea54f41461103a5780632e17de78146110135780633d18b91214610f515780633d509c9714610d2157806348e5d9f814610cbd5780635eeb2e0f14610be2578063638634ee14610bbf578063647654b414610a1b57806366e943f1146109e157806369b3054b146109805780637035ab981461093057806370a08231146108f857806372f702f3146108b45780637bb7bed114610864578063a3cd8ac414610586578063a694fc3a1461055f578063a89f38a314610525578063b5fd73f8146104ed578063bcd11014146104ca578063bf199e62146104ae578063c00007b0146103dd578063c0b534c2146103a3578063ccc574901461037c578063cecc238f1461034d578063e24bf4fe14610309578063e70b9e27146102b9578063e9fad8ee146101e0578063ebc136d0146101b35763f122977714610184575f80fd5b346101af5760203660031901126101af5760206101a76101a26112b6565b6114a7565b604051908152f35b5f80fd5b346101af5760203660031901126101af5760206101d66101d16112b6565b6113fb565b6040519015158152f35b346101af575f3660031901126101af576101f8611535565b5f805433915b81811061022e575f5f516020611ac55f395f51905f525d335f52600660205261022c60405f205433336116af565b005b8061023a600192611840565b838060a01b0391549060031b1c16610251336115f6565b805f52600460205260405f20855f5260205260405f205480610276575b5050016101fe565b815f52600460205260405f20865f526020525f604081205561029981338461187f565b6040519081525f516020611a855f395f51905f5260203392a3848061026e565b346101af5760403660031901126101af576102d26112b6565b6102da6112cc565b6001600160a01b039182165f908152600460209081526040808320949093168252928352819020549051908152f35b346101af575f3660031901126101af576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101af5760403660031901126101af5761022c6103696112b6565b610372816115f6565b602435903361175f565b346101af575f3660031901126101af5760206040515f516020611aa55f395f51905f528152f35b346101af575f3660031901126101af5760206040517f025d8bbf3268be680d2605ebf6da15063b9915615bf1087dab336efc1bf970cb8152f35b346101af5760203660031901126101af576103f66112b6565b6103fe611535565b6001600160a01b03165f818152600160205260409020541561049f57610423336115f6565b5f81815260046020908152604080832033845290915290205480610454575b5f5f516020611ac55f395f51905f525d005b815f52600460205260405f2060018060a01b0333165f526020525f604081205561047f81338461187f565b6040519081525f516020611a855f395f51905f5260203392a38080610442565b63064fed9960e31b5f5260045ffd5b346101af575f3660031901126101af5760205f54604051908152f35b346101af5760203660031901126101af5760206101a76104e86112b6565b6113a0565b346101af5760203660031901126101af5760206101d66001600160a01b036105136112b6565b165f52600160205260405f2054151590565b346101af575f3660031901126101af5760206040517fb00be3d6a5434b97b328543d1486d56adcb7e74080170d1cdd7e0306c3d9ba3d8152f35b346101af5760203660031901126101af57610579336115f6565b61022c600435333361175f565b346101af5760603660031901126101af5761059f6112b6565b6024359060443591604051632474521560e21b81525f516020611aa55f395f51905f52600482015233602482015260208160448160018060a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa908115610859575f9161082a575b5015610817575f545f5b8181106107cf5750506001600160a01b0382811693907f00000000000000000000000000000000000000000000000000000000000000001684146107c057835f52600260205260405f209281156107b1576301da9c0082116107a257610687855f52600160205260405f2054151590565b1561073b575060028301540361072c575b6106a481303386611997565b670de0b6b3a7640000810290808204670de0b6b3a76400001481151715610718578261070d6020936106fc60027fac24935fd910bc682b5ccb1a07b718cadf8cf2f6d1404c4f3ddc3662dae40e299701548092611375565b60018401554260038401554261149a565b9055604051908152a2005b634e487b7160e01b5f52601160045260245ffd5b6333fec8c760e21b5f5260045ffd5b9061074585611a33565b156107935760028401819055604080516001600160a01b0393909316835260208301919091527f68e3e9397770d9dfa88c0953a34e1a70a05416b3895957f5b689787a920791c491a1610698565b632b3d7ad960e01b5f5260045ffd5b631fb7569160e21b5f5260045ffd5b637ffcfc9f60e11b5f5260045ffd5b6303b66bab60e31b5f5260045ffd5b806107db600192611840565b838060a01b0391549060031b1c16805f526002602052600361080e60405f2092610804816114a7565b6004850155611330565b91015501610616565b630ea7d7ed60e21b5f523360045260245ffd5b61084c915060203d602011610852575b61084481836112e2565b810190611318565b8461060c565b503d61083a565b6040513d5f823e3d90fd5b346101af5760203660031901126101af576004355f548110156108a55761088c602091611840565b905460405160039290921b1c6001600160a01b03168152f35b634e23d03560e01b5f5260045ffd5b346101af575f3660031901126101af576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101af5760203660031901126101af576001600160a01b036109196112b6565b165f526006602052602060405f2054604051908152f35b346101af5760403660031901126101af576109496112b6565b6109516112cc565b6001600160a01b039182165f908152600360209081526040808320949093168252928352819020549051908152f35b346101af5760403660031901126101af5760043560038110156101af576020906109a86112cc565b604051908382019260f81b835260018060601b03199060601b166021820152601581526109d66035826112e2565b519020604051908152f35b346101af575f3660031901126101af5760206040517f0d186688925976bbe6755ae984501c8e3e2b103a7af59fd803ab9c6d891ae7e08152f35b346101af5760603660031901126101af57610a346112b6565b6024359060443580151581036101af57610a4d826115f6565b6001600160a01b038216923384141580610baf575b610ba057610a718130856116af565b604051635d043b2960e11b81526004810191909152602481018490523060448201526020816064815f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af1801561085957610b71575b50610ad857005b5f54905f5b828110610ae657005b80610af2600192611840565b838060a01b0391549060031b1c16610b09846115f6565b805f52600460205260405f20865f526020528560405f205480610b30575b50505001610add565b60205f516020611a855f395f51905f5291845f526004825260405f20845f5282525f6040812055610b6281898761187f565b604051908152a3858581610b27565b6020813d602011610b98575b81610b8a602093836112e2565b810103126101af5751610ad1565b3d9150610b7d565b638e866cef60e01b5f5260045ffd5b50610bb9336113fb565b15610a62565b346101af5760203660031901126101af5760206101a7610bdd6112b6565b611330565b346101af5760403660031901126101af57610bfb6112b6565b610c036112cc565b610c0b611535565b6001600160a01b03165f818152600160205260409020549091901561049f57610c33816115f6565b815f52600460205260405f2060018060a01b0382165f5260205260405f20549081610c6a575f5f516020611ac55f395f51905f525d005b60205f516020611a855f395f51905f5291845f526004825260405f2060018060a01b0382165f5282525f6040812055610ca484828761187f565b6040519384526001600160a01b031692a3808080610442565b346101af5760203660031901126101af576001600160a01b03610cde6112b6565b165f52600260205260a060405f20805490600181015490600281015460046003830154920154926040519485526020850152604084015260608301526080820152f35b346101af5760203660031901126101af57610d3a6112b6565b604051632474521560e21b81525f516020611aa55f395f51905f5260048201523360248201526020816044817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610859575f91610f32575b5015610817576001600160a01b03165f818152600160205260409020541561049f57805f52600260205260405f2054421115610f23576040516370a0823160e01b8152306004820152602081602481855afa908115610859575f91610ef1575b5060405163313ce56760e01b8152602081600481865afa5f9181610eb3575b50610e815750655af3107a40005b8111610e6f5750610e3d816118c2565b1561049f5760207f66257bcef574219c04f7c05f7a1c78d599da10491294c92a5805c48b4cdf500991604051908152a1005b633007cea360e01b5f5260045260245ffd5b60ff1660048111610e9457506001610e2d565b6003190160ff81116107185760ff16604d811161071857600a0a610e2d565b9091506020813d602011610ee9575b81610ecf602093836112e2565b810103126101af575160ff811681036101af579084610e1f565b3d9150610ec2565b90506020813d602011610f1b575b81610f0c602093836112e2565b810103126101af575182610e00565b3d9150610eff565b63d9ba1add60e01b5f5260045ffd5b610f4b915060203d6020116108525761084481836112e2565b82610da0565b346101af575f3660031901126101af57610f69611535565b5f805433915b818110610f88575f5f516020611ac55f395f51905f525d005b80610f94600192611840565b838060a01b0391549060031b1c16610fab336115f6565b805f52600460205260405f20855f5260205260405f205480610fd0575b505001610f6f565b815f52600460205260405f20865f526020525f6040812055610ff381338461187f565b6040519081525f516020611a855f395f51905f5260203392a38480610fc8565b346101af5760203660031901126101af5761102d336115f6565b61022c60043533336116af565b346101af575f3660031901126101af5760206040517f55435dd261a4b9b3364963f7738a7a662ad9c84396d64be3365284bb7f0a50418152f35b346101af5760403660031901126101af5761108d6112b6565b604051632474521560e21b81525f516020611aa55f395f51905f526004820152336024828101919091523591906020816044817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610859575f91611178575b5015610817576001600160a01b03165f818152600160205260409020549091901561049f5780156107b1576301da9c0081116107a257815f52600260205260405f208054421115610f2357817fad2f86b01ed93b4b3a150d448c61a4f5d8d38075d3c0c64cc0a26fd6e1f495459260026020930155604051908152a2005b611191915060203d6020116108525761084481836112e2565b836110fa565b346101af5760403660031901126101af5760206101a76111b56112b6565b6111bd6112cc565b9061156a565b346101af575f3660031901126101af576020600554604051908152f35b346101af5760203660031901126101af576111f96112b6565b611201611535565b5f80546001600160a01b0383169290915b82811061122b575f5f516020611ac55f395f51905f525d005b80611237600192611840565b838060a01b0391549060031b1c1661124e846115f6565b805f52600460205260405f20865f526020528560405f205480611275575b50505001611212565b60205f516020611a855f395f51905f5291845f526004825260405f20845f5282525f60408120556112a781898761187f565b604051908152a385858161126c565b600435906001600160a01b03821682036101af57565b602435906001600160a01b03821682036101af57565b90601f8019910116810190811067ffffffffffffffff82111761130457604052565b634e487b7160e01b5f52604160045260245ffd5b908160209103126101af575180151581036101af5790565b6001600160a01b03165f8181526002602052604090205442101561135357504290565b5f52600260205260405f205490565b8181029291811591840414171561071857565b811561137f570490565b634e487b7160e01b5f52601260045260245ffd5b9190820391821161071857565b6001600160a01b03165f9081526002602052604090208054670de0b6b3a764000091428211156113e65760016113da6113e2934290611393565b910154611362565b0490565b6113e291506002600182015491015490611362565b604051632474521560e21b81527fb00be3d6a5434b97b328543d1486d56adcb7e74080170d1cdd7e0306c3d9ba3d60048201526001600160a01b03918216602482015290602090829060449082907f0000000000000000000000000000000000000000000000000000000000000000165afa908115610859575f9161147e575090565b611497915060203d6020116108525761084481836112e2565b90565b9190820180921161071857565b600554801561151757906115116114979261150c60018060a01b038416805f5260026020526114f66114e0600460405f20015496611330565b825f526002602052600360405f20015490611393565b905f526002602052600160405f20015490611362565b611375565b9061149a565b506001600160a01b03165f9081526002602052604090206004015490565b5f516020611ac55f395f51905f525c61155b5760015f516020611ac55f395f51905f525d565b633ee5aeb560e01b5f5260045ffd5b906114979160018060a01b0381165f526006602052670de0b6b3a76400006115d060405f20546115ca61159c866114a7565b6001600160a01b039687165f8181526003602090815260408083209a8a168352999052979097205490611393565b90611362565b04915f52600460205260405f209060018060a01b03165f5260205260405f20549061149a565b5f54905f5b82811061160757505050565b80611613600192611840565b838060a01b0391549060031b1c16805f52600260205260405f20611636826114a7565b9060048101918255600361164984611330565b910155838060a01b038516611661575b5050016115fb565b61166b828661156a565b825f52600460205260405f20858060a01b0387165f5260205260405f205554905f52600360205260405f20838060a01b0385165f5260205260405f20555f80611659565b82156117505760207fd8654fcc8cf5b36d30b3f5e4688fc78118e6d68de60b9994e09902268b57c3e3916116e585600554611393565b60055560018060a01b031692835f526006825260405f20611707868254611393565b905561173d85827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031661187f565b6040519485526001600160a01b031693a3565b6310356e5960e21b5f5260045ffd5b6001600160a01b0390911691908215611831578115611822577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168015611813577f5dac0c1b1112564a045ba943c9d50270893e8e826c49be8e7073adc713ab7bd791611800846020936117dd8260055461149a565b600555875f526006855260405f206117f683825461149a565b9055833091611997565b6040519384526001600160a01b031692a3565b63417198d360e01b5f5260045ffd5b636a76ff9f60e01b5f5260045ffd5b63da52720960e01b5f5260045ffd5b5f54811015611856575f805260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b8054821015611856575f5260205f2001905f90565b60405163a9059cbb60e01b60208201526001600160a01b039290921660248301526044808301939093529181526118c0916118bb6064836112e2565b6119db565b565b5f818152600160205260409020548015611991575f198101818111610718575f545f1981019190821161071857818103611945575b5050505f548015611931575f190161190f815f61186a565b8154905f199060031b1b191690555f555f5260016020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b61197b611955611965935f61186a565b90549060031b1c9283925f61186a565b819391549060031b91821b915f19901b19161790565b90555f52600160205260405f20555f80806118f7565b50505f90565b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064808301939093529181526118c0916118bb6084836112e2565b905f602091828151910182855af115610859575f513d611a2a57506001600160a01b0381163b155b611a0a5750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415611a03565b805f52600160205260405f2054155f14611a7f575f54600160401b81101561130457611a696119658260018594015f555f61186a565b90555f54905f52600160205260405f2055600190565b505f9056fe540798df468d7b23d11f156fdb954cb19ad414d150722a7b6d55ba369dea792e7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f559b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a26469706673582212205cefa826478424526656b628e969a6398067d157451f039937e52f031cb2632f64736f6c634300081c0033a2646970667358221220a47cd46281d81bf6a87e31c660716891d5cf2064acd13522dde82d032dcf3b6564736f6c634300081c0033
Deployed Bytecode
0x60808060405260043610156011575f80fd5b5f3560e01c630f07f829146023575f80fd5b3460e857604036600319011260e8576004356001600160a01b038116919082900360e8576024356001600160a01b038116929083900360e857611cc980830183811067ffffffffffffffff82111760d45760409284926100ed843981528460208201520301905ff090811560c95760209160018060a01b031660405191817f0b3397f9446e8b85cf96fe3194aeec84e2fd23ab014cf85f82805b36aef207aa5f80a38152f35b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fdfe60e0806040523461013957604081611cc9803803809161001f8285610163565b8339810103126101395761003e60206100378361019a565b920161019a565b906001600160a01b03168015610150576040516301ffc9a760e01b815263261c910560e21b6004820152602081602481855afa908115610145575f91610106575b50156100f45760805260c08190526001600160a01b031660a052604051611b1a90816101af82396080518181816105dc01528181610d68015281816110c2015261144b015260a05181818161062e015281816108c901528181611710015261177a015260c05181818161031e0152610a9c0152f35b6347bd7c1d60e01b5f5260045260245ffd5b90506020813d60201161013d575b8161012160209383610163565b8101031261013957518015158103610139575f61007f565b5f80fd5b3d9150610114565b6040513d5f823e3d90fd5b6347bd7c1d60e01b5f525f60045260245ffd5b601f909101601f19168101906001600160401b0382119082101761018657604052565b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036101395756fe60806040526004361015610011575f80fd5b5f3560e01c8063055de275146111e057806318160ddd146111c3578063211dc32d146111975780632378bea61461107457806324ea54f41461103a5780632e17de78146110135780633d18b91214610f515780633d509c9714610d2157806348e5d9f814610cbd5780635eeb2e0f14610be2578063638634ee14610bbf578063647654b414610a1b57806366e943f1146109e157806369b3054b146109805780637035ab981461093057806370a08231146108f857806372f702f3146108b45780637bb7bed114610864578063a3cd8ac414610586578063a694fc3a1461055f578063a89f38a314610525578063b5fd73f8146104ed578063bcd11014146104ca578063bf199e62146104ae578063c00007b0146103dd578063c0b534c2146103a3578063ccc574901461037c578063cecc238f1461034d578063e24bf4fe14610309578063e70b9e27146102b9578063e9fad8ee146101e0578063ebc136d0146101b35763f122977714610184575f80fd5b346101af5760203660031901126101af5760206101a76101a26112b6565b6114a7565b604051908152f35b5f80fd5b346101af5760203660031901126101af5760206101d66101d16112b6565b6113fb565b6040519015158152f35b346101af575f3660031901126101af576101f8611535565b5f805433915b81811061022e575f5f516020611ac55f395f51905f525d335f52600660205261022c60405f205433336116af565b005b8061023a600192611840565b838060a01b0391549060031b1c16610251336115f6565b805f52600460205260405f20855f5260205260405f205480610276575b5050016101fe565b815f52600460205260405f20865f526020525f604081205561029981338461187f565b6040519081525f516020611a855f395f51905f5260203392a3848061026e565b346101af5760403660031901126101af576102d26112b6565b6102da6112cc565b6001600160a01b039182165f908152600460209081526040808320949093168252928352819020549051908152f35b346101af575f3660031901126101af576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101af5760403660031901126101af5761022c6103696112b6565b610372816115f6565b602435903361175f565b346101af575f3660031901126101af5760206040515f516020611aa55f395f51905f528152f35b346101af575f3660031901126101af5760206040517f025d8bbf3268be680d2605ebf6da15063b9915615bf1087dab336efc1bf970cb8152f35b346101af5760203660031901126101af576103f66112b6565b6103fe611535565b6001600160a01b03165f818152600160205260409020541561049f57610423336115f6565b5f81815260046020908152604080832033845290915290205480610454575b5f5f516020611ac55f395f51905f525d005b815f52600460205260405f2060018060a01b0333165f526020525f604081205561047f81338461187f565b6040519081525f516020611a855f395f51905f5260203392a38080610442565b63064fed9960e31b5f5260045ffd5b346101af575f3660031901126101af5760205f54604051908152f35b346101af5760203660031901126101af5760206101a76104e86112b6565b6113a0565b346101af5760203660031901126101af5760206101d66001600160a01b036105136112b6565b165f52600160205260405f2054151590565b346101af575f3660031901126101af5760206040517fb00be3d6a5434b97b328543d1486d56adcb7e74080170d1cdd7e0306c3d9ba3d8152f35b346101af5760203660031901126101af57610579336115f6565b61022c600435333361175f565b346101af5760603660031901126101af5761059f6112b6565b6024359060443591604051632474521560e21b81525f516020611aa55f395f51905f52600482015233602482015260208160448160018060a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa908115610859575f9161082a575b5015610817575f545f5b8181106107cf5750506001600160a01b0382811693907f00000000000000000000000000000000000000000000000000000000000000001684146107c057835f52600260205260405f209281156107b1576301da9c0082116107a257610687855f52600160205260405f2054151590565b1561073b575060028301540361072c575b6106a481303386611997565b670de0b6b3a7640000810290808204670de0b6b3a76400001481151715610718578261070d6020936106fc60027fac24935fd910bc682b5ccb1a07b718cadf8cf2f6d1404c4f3ddc3662dae40e299701548092611375565b60018401554260038401554261149a565b9055604051908152a2005b634e487b7160e01b5f52601160045260245ffd5b6333fec8c760e21b5f5260045ffd5b9061074585611a33565b156107935760028401819055604080516001600160a01b0393909316835260208301919091527f68e3e9397770d9dfa88c0953a34e1a70a05416b3895957f5b689787a920791c491a1610698565b632b3d7ad960e01b5f5260045ffd5b631fb7569160e21b5f5260045ffd5b637ffcfc9f60e11b5f5260045ffd5b6303b66bab60e31b5f5260045ffd5b806107db600192611840565b838060a01b0391549060031b1c16805f526002602052600361080e60405f2092610804816114a7565b6004850155611330565b91015501610616565b630ea7d7ed60e21b5f523360045260245ffd5b61084c915060203d602011610852575b61084481836112e2565b810190611318565b8461060c565b503d61083a565b6040513d5f823e3d90fd5b346101af5760203660031901126101af576004355f548110156108a55761088c602091611840565b905460405160039290921b1c6001600160a01b03168152f35b634e23d03560e01b5f5260045ffd5b346101af575f3660031901126101af576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101af5760203660031901126101af576001600160a01b036109196112b6565b165f526006602052602060405f2054604051908152f35b346101af5760403660031901126101af576109496112b6565b6109516112cc565b6001600160a01b039182165f908152600360209081526040808320949093168252928352819020549051908152f35b346101af5760403660031901126101af5760043560038110156101af576020906109a86112cc565b604051908382019260f81b835260018060601b03199060601b166021820152601581526109d66035826112e2565b519020604051908152f35b346101af575f3660031901126101af5760206040517f0d186688925976bbe6755ae984501c8e3e2b103a7af59fd803ab9c6d891ae7e08152f35b346101af5760603660031901126101af57610a346112b6565b6024359060443580151581036101af57610a4d826115f6565b6001600160a01b038216923384141580610baf575b610ba057610a718130856116af565b604051635d043b2960e11b81526004810191909152602481018490523060448201526020816064815f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af1801561085957610b71575b50610ad857005b5f54905f5b828110610ae657005b80610af2600192611840565b838060a01b0391549060031b1c16610b09846115f6565b805f52600460205260405f20865f526020528560405f205480610b30575b50505001610add565b60205f516020611a855f395f51905f5291845f526004825260405f20845f5282525f6040812055610b6281898761187f565b604051908152a3858581610b27565b6020813d602011610b98575b81610b8a602093836112e2565b810103126101af5751610ad1565b3d9150610b7d565b638e866cef60e01b5f5260045ffd5b50610bb9336113fb565b15610a62565b346101af5760203660031901126101af5760206101a7610bdd6112b6565b611330565b346101af5760403660031901126101af57610bfb6112b6565b610c036112cc565b610c0b611535565b6001600160a01b03165f818152600160205260409020549091901561049f57610c33816115f6565b815f52600460205260405f2060018060a01b0382165f5260205260405f20549081610c6a575f5f516020611ac55f395f51905f525d005b60205f516020611a855f395f51905f5291845f526004825260405f2060018060a01b0382165f5282525f6040812055610ca484828761187f565b6040519384526001600160a01b031692a3808080610442565b346101af5760203660031901126101af576001600160a01b03610cde6112b6565b165f52600260205260a060405f20805490600181015490600281015460046003830154920154926040519485526020850152604084015260608301526080820152f35b346101af5760203660031901126101af57610d3a6112b6565b604051632474521560e21b81525f516020611aa55f395f51905f5260048201523360248201526020816044817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610859575f91610f32575b5015610817576001600160a01b03165f818152600160205260409020541561049f57805f52600260205260405f2054421115610f23576040516370a0823160e01b8152306004820152602081602481855afa908115610859575f91610ef1575b5060405163313ce56760e01b8152602081600481865afa5f9181610eb3575b50610e815750655af3107a40005b8111610e6f5750610e3d816118c2565b1561049f5760207f66257bcef574219c04f7c05f7a1c78d599da10491294c92a5805c48b4cdf500991604051908152a1005b633007cea360e01b5f5260045260245ffd5b60ff1660048111610e9457506001610e2d565b6003190160ff81116107185760ff16604d811161071857600a0a610e2d565b9091506020813d602011610ee9575b81610ecf602093836112e2565b810103126101af575160ff811681036101af579084610e1f565b3d9150610ec2565b90506020813d602011610f1b575b81610f0c602093836112e2565b810103126101af575182610e00565b3d9150610eff565b63d9ba1add60e01b5f5260045ffd5b610f4b915060203d6020116108525761084481836112e2565b82610da0565b346101af575f3660031901126101af57610f69611535565b5f805433915b818110610f88575f5f516020611ac55f395f51905f525d005b80610f94600192611840565b838060a01b0391549060031b1c16610fab336115f6565b805f52600460205260405f20855f5260205260405f205480610fd0575b505001610f6f565b815f52600460205260405f20865f526020525f6040812055610ff381338461187f565b6040519081525f516020611a855f395f51905f5260203392a38480610fc8565b346101af5760203660031901126101af5761102d336115f6565b61022c60043533336116af565b346101af575f3660031901126101af5760206040517f55435dd261a4b9b3364963f7738a7a662ad9c84396d64be3365284bb7f0a50418152f35b346101af5760403660031901126101af5761108d6112b6565b604051632474521560e21b81525f516020611aa55f395f51905f526004820152336024828101919091523591906020816044817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610859575f91611178575b5015610817576001600160a01b03165f818152600160205260409020549091901561049f5780156107b1576301da9c0081116107a257815f52600260205260405f208054421115610f2357817fad2f86b01ed93b4b3a150d448c61a4f5d8d38075d3c0c64cc0a26fd6e1f495459260026020930155604051908152a2005b611191915060203d6020116108525761084481836112e2565b836110fa565b346101af5760403660031901126101af5760206101a76111b56112b6565b6111bd6112cc565b9061156a565b346101af575f3660031901126101af576020600554604051908152f35b346101af5760203660031901126101af576111f96112b6565b611201611535565b5f80546001600160a01b0383169290915b82811061122b575f5f516020611ac55f395f51905f525d005b80611237600192611840565b838060a01b0391549060031b1c1661124e846115f6565b805f52600460205260405f20865f526020528560405f205480611275575b50505001611212565b60205f516020611a855f395f51905f5291845f526004825260405f20845f5282525f60408120556112a781898761187f565b604051908152a385858161126c565b600435906001600160a01b03821682036101af57565b602435906001600160a01b03821682036101af57565b90601f8019910116810190811067ffffffffffffffff82111761130457604052565b634e487b7160e01b5f52604160045260245ffd5b908160209103126101af575180151581036101af5790565b6001600160a01b03165f8181526002602052604090205442101561135357504290565b5f52600260205260405f205490565b8181029291811591840414171561071857565b811561137f570490565b634e487b7160e01b5f52601260045260245ffd5b9190820391821161071857565b6001600160a01b03165f9081526002602052604090208054670de0b6b3a764000091428211156113e65760016113da6113e2934290611393565b910154611362565b0490565b6113e291506002600182015491015490611362565b604051632474521560e21b81527fb00be3d6a5434b97b328543d1486d56adcb7e74080170d1cdd7e0306c3d9ba3d60048201526001600160a01b03918216602482015290602090829060449082907f0000000000000000000000000000000000000000000000000000000000000000165afa908115610859575f9161147e575090565b611497915060203d6020116108525761084481836112e2565b90565b9190820180921161071857565b600554801561151757906115116114979261150c60018060a01b038416805f5260026020526114f66114e0600460405f20015496611330565b825f526002602052600360405f20015490611393565b905f526002602052600160405f20015490611362565b611375565b9061149a565b506001600160a01b03165f9081526002602052604090206004015490565b5f516020611ac55f395f51905f525c61155b5760015f516020611ac55f395f51905f525d565b633ee5aeb560e01b5f5260045ffd5b906114979160018060a01b0381165f526006602052670de0b6b3a76400006115d060405f20546115ca61159c866114a7565b6001600160a01b039687165f8181526003602090815260408083209a8a168352999052979097205490611393565b90611362565b04915f52600460205260405f209060018060a01b03165f5260205260405f20549061149a565b5f54905f5b82811061160757505050565b80611613600192611840565b838060a01b0391549060031b1c16805f52600260205260405f20611636826114a7565b9060048101918255600361164984611330565b910155838060a01b038516611661575b5050016115fb565b61166b828661156a565b825f52600460205260405f20858060a01b0387165f5260205260405f205554905f52600360205260405f20838060a01b0385165f5260205260405f20555f80611659565b82156117505760207fd8654fcc8cf5b36d30b3f5e4688fc78118e6d68de60b9994e09902268b57c3e3916116e585600554611393565b60055560018060a01b031692835f526006825260405f20611707868254611393565b905561173d85827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031661187f565b6040519485526001600160a01b031693a3565b6310356e5960e21b5f5260045ffd5b6001600160a01b0390911691908215611831578115611822577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168015611813577f5dac0c1b1112564a045ba943c9d50270893e8e826c49be8e7073adc713ab7bd791611800846020936117dd8260055461149a565b600555875f526006855260405f206117f683825461149a565b9055833091611997565b6040519384526001600160a01b031692a3565b63417198d360e01b5f5260045ffd5b636a76ff9f60e01b5f5260045ffd5b63da52720960e01b5f5260045ffd5b5f54811015611856575f805260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b8054821015611856575f5260205f2001905f90565b60405163a9059cbb60e01b60208201526001600160a01b039290921660248301526044808301939093529181526118c0916118bb6064836112e2565b6119db565b565b5f818152600160205260409020548015611991575f198101818111610718575f545f1981019190821161071857818103611945575b5050505f548015611931575f190161190f815f61186a565b8154905f199060031b1b191690555f555f5260016020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b61197b611955611965935f61186a565b90549060031b1c9283925f61186a565b819391549060031b91821b915f19901b19161790565b90555f52600160205260405f20555f80806118f7565b50505f90565b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064808301939093529181526118c0916118bb6084836112e2565b905f602091828151910182855af115610859575f513d611a2a57506001600160a01b0381163b155b611a0a5750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415611a03565b805f52600160205260405f2054155f14611a7f575f54600160401b81101561130457611a696119658260018594015f555f61186a565b90555f54905f52600160205260405f2055600190565b505f9056fe540798df468d7b23d11f156fdb954cb19ad414d150722a7b6d55ba369dea792e7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f559b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a26469706673582212205cefa826478424526656b628e969a6398067d157451f039937e52f031cb2632f64736f6c634300081c0033a2646970667358221220a47cd46281d81bf6a87e31c660716891d5cf2064acd13522dde82d032dcf3b6564736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 35 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.