Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Method | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|---|
Mint Wst ETH | 613957 | 2 hrs ago | 0 ETH | ||||
Fund | 613945 | 2 hrs ago | 1 ETH | ||||
Mint St ETH | 613934 | 2 hrs ago | 0 ETH | ||||
Fund | 613930 | 2 hrs ago | 1 ETH | ||||
Grant Roles | 613928 | 2 hrs ago | 0 ETH | ||||
Mint St ETH | 613791 | 3 hrs ago | 0 ETH | ||||
Initialize | 613779 | 3 hrs ago | 0 ETH | ||||
Fund | 613629 | 3 hrs ago | 10 ETH | ||||
Fund | 613497 | 4 hrs ago | 330 ETH | ||||
Grant Roles | 611883 | 10 hrs ago | 0 ETH | ||||
Mint St ETH | 607544 | 25 hrs ago | 0 ETH | ||||
Fund | 607533 | 25 hrs ago | 1,500 ETH | ||||
Burn St ETH | 595228 | 2 days ago | 0 ETH | ||||
Mint St ETH | 595207 | 2 days ago | 0 ETH | ||||
Mint Wst ETH | 594638 | 3 days ago | 0 ETH | ||||
Mint Wst ETH | 594622 | 3 days ago | 0 ETH | ||||
Fund | 594374 | 3 days ago | 320 ETH | ||||
Grant Roles | 594360 | 3 days ago | 0 ETH | ||||
Mint St ETH | 594351 | 3 days ago | 0 ETH | ||||
Mint St ETH | 594314 | 3 days ago | 0 ETH | ||||
Mint St ETH | 594309 | 3 days ago | 0 ETH | ||||
Fund | 594304 | 3 days ago | 1,023 ETH | ||||
Revoke Role | 594251 | 3 days ago | 0 ETH | ||||
DEFAULT_ADMIN_RO... | 594251 | 3 days ago | 0 ETH | ||||
Grant Role | 594251 | 3 days ago | 0 ETH |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
Dashboard
Compiler Version
v0.8.25+commit.b61c2a91
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md pragma solidity 0.8.25; import {SafeERC20} from "@openzeppelin/contracts-v5.2/token/ERC20/utils/SafeERC20.sol"; import {Math256} from "contracts/common/lib/Math256.sol"; import {IERC20} from "@openzeppelin/contracts-v5.2/token/ERC20/IERC20.sol"; import {IERC721} from "@openzeppelin/contracts-v5.2/token/ERC721/IERC721.sol"; import {IDepositContract} from "contracts/0.8.25/interfaces/IDepositContract.sol"; import {IStakingVault, StakingVaultDeposit} from "../interfaces/IStakingVault.sol"; import {NodeOperatorFee} from "./NodeOperatorFee.sol"; import {VaultHub} from "../VaultHub.sol"; import {ILido as IStETH} from "contracts/0.8.25/interfaces/ILido.sol"; import {IPredepositGuarantee} from "../interfaces/IPredepositGuarantee.sol"; interface IWstETH is IERC20 { function wrap(uint256) external returns (uint256); function unwrap(uint256) external returns (uint256); } /** * @title Dashboard * @notice This contract is a UX-layer for StakingVault and meant to be used as its owner. * This contract improves the vault UX by bundling all functions from the StakingVault and VaultHub * in this single contract. It provides administrative functions for managing the StakingVault, * including funding, withdrawing, minting, burning, and rebalancing operations. */ contract Dashboard is NodeOperatorFee { bytes32 public constant RECOVER_ASSETS_ROLE = keccak256("vaults.Dashboard.RecoverAssets"); /** * @notice The stETH token contract */ IStETH public immutable STETH; /** * @notice The wstETH token contract */ IWstETH public immutable WSTETH; /** * @notice ETH address convention per EIP-7528 */ address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; /** * @notice Constructor sets the stETH, and WSTETH token addresses, * and passes the address of the vault hub up the inheritance chain. * @param _stETH Address of the stETH token contract. * @param _wstETH Address of the wstETH token contract. * @param _vaultHub Address of the vault hub contract. */ constructor(address _stETH, address _wstETH, address _vaultHub) NodeOperatorFee(_vaultHub) { if (_stETH == address(0)) revert ZeroArgument("_stETH"); if (_wstETH == address(0)) revert ZeroArgument("_wstETH"); STETH = IStETH(_stETH); WSTETH = IWstETH(_wstETH); } /** * @notice Calls the parent's initializer and approves the max allowance for WSTETH for gas savings * @param _defaultAdmin The address of the default admin * @param _nodeOperatorManager The address of the node operator manager * @param _nodeOperatorFeeBP The node operator fee in basis points * @param _confirmExpiry The confirmation expiry time in seconds */ function initialize( address _defaultAdmin, address _nodeOperatorManager, uint256 _nodeOperatorFeeBP, uint256 _confirmExpiry ) public { super._initialize(_defaultAdmin, _nodeOperatorManager, _nodeOperatorFeeBP, _confirmExpiry); // reduces gas cost for `mintWsteth` // invariant: dashboard does not hold stETH on its balance STETH.approve(address(WSTETH), type(uint256).max); } // ==================== View Functions ==================== /** * @notice Returns the vault socket data for the staking vault. * @return VaultSocket struct containing vault data */ function vaultSocket() public view returns (VaultHub.VaultSocket memory) { return VAULT_HUB.vaultSocket(address(_stakingVault())); } /** * @notice Returns the stETH share limit of the vault * @return The share limit as a uint96 */ function shareLimit() external view returns (uint96) { return vaultSocket().shareLimit; } /** * @notice Returns the number of stETHshares minted * @return The shares minted as a uint96 */ function liabilityShares() public view returns (uint96) { return vaultSocket().liabilityShares; } /** * @notice Returns the reserve ratio of the vault in basis points * @return The reserve ratio in basis points as a uint16 */ function reserveRatioBP() public view returns (uint16) { return vaultSocket().reserveRatioBP; } /** * @notice Returns the rebalance threshold of the vault in basis points. * @return The rebalance threshold in basis points as a uint16. */ function forcedRebalanceThresholdBP() external view returns (uint16) { return vaultSocket().forcedRebalanceThresholdBP; } /** * @notice Returns the treasury fee basis points. * @return The treasury fee in basis points as a uint16. */ function treasuryFeeBP() external view returns (uint16) { return vaultSocket().treasuryFeeBP; } /** * @notice Returns the total value of the vault in ether. * @return The total value as a uint256. */ function totalValue() external view returns (uint256) { return _stakingVault().totalValue(); } /** * @notice Returns the overall capacity for stETH shares that can be minted by the vault * @return The maximum number of mintable stETH shares. */ function totalMintingCapacity() public view returns (uint256) { return _totalMintingCapacity(0); } /** * @notice Returns the remaining capacity for stETH shares that can be minted * by the vault if additional ether is funded * @param _etherToFund the amount of ether to be funded, can be zero * @return the number of shares that can be minted using additional ether */ function remainingMintingCapacity(uint256 _etherToFund) external view returns (uint256) { uint256 totalShares = _totalMintingCapacity(_etherToFund); uint256 liabilityShares_ = vaultSocket().liabilityShares; if (totalShares < liabilityShares_) return 0; return totalShares - liabilityShares_; } /** * @notice Returns the unreserved amount of ether, * i.e. the amount of total value that is not locked in the StakingVault * and not reserved for node operator fee. * This amount does not account for the current balance of the StakingVault and * can return a value greater than the actual balance of the StakingVault. * @return uint256: the amount of unreserved ether. */ function unreserved() public view returns (uint256) { IStakingVault stakingVault_ = _stakingVault(); uint256 reserved = stakingVault_.locked() + nodeOperatorUnclaimedFee(); uint256 totalValue_ = stakingVault_.totalValue(); return reserved > totalValue_ ? 0 : totalValue_ - reserved; } /** * @notice Returns the amount of ether that can be instantly withdrawn from the staking vault. * @dev This is the amount of ether that is not locked in the StakingVault and not reserved for node operator fee. * @dev This method overrides the Dashboard's withdrawableEther() method * @return The amount of ether that can be withdrawn. */ function withdrawableEther() external view returns (uint256) { return Math256.min(address(_stakingVault()).balance, unreserved()); } // ==================== Vault Management Functions ==================== /** * @dev Automatically funds the staking vault with ether */ receive() external payable { _fund(msg.value); } /** * @notice Transfers ownership of the staking vault to a new owner. * @param _newOwner Address of the new owner. */ function transferStakingVaultOwnership(address _newOwner) external { _transferStakingVaultOwnership(_newOwner); } /** * @notice Disconnects the staking vault from the vault hub. */ function voluntaryDisconnect() external { _voluntaryDisconnect(); } /** * @notice Funds the staking vault with ether */ function fund() external payable { _fund(msg.value); } /** * @notice Withdraws ether from the staking vault to a recipient * @param _recipient Address of the recipient * @param _ether Amount of ether to withdraw */ function withdraw(address _recipient, uint256 _ether) external { uint256 unreserved_ = unreserved(); if (_ether > unreserved_) { revert WithdrawalAmountExceedsUnreserved(_ether, unreserved_); } _withdraw(_recipient, _ether); } /** * @notice Update the locked amount of the staking vault * @param _amount Amount of ether to lock */ function lock(uint256 _amount) external { _lock(_amount); } /** * @notice Mints stETH shares backed by the vault to the recipient. * @param _recipient Address of the recipient * @param _amountOfShares Amount of stETH shares to mint */ function mintShares( address _recipient, uint256 _amountOfShares ) external payable fundable autolock(_amountOfShares) { _mintShares(_recipient, _amountOfShares); } /** * @notice Mints stETH tokens backed by the vault to the recipient. * !NB: this will revert with`VaultHub.ZeroArgument("_amountOfShares")` if the amount of stETH is less than 1 share * @param _recipient Address of the recipient * @param _amountOfStETH Amount of stETH to mint */ function mintStETH( address _recipient, uint256 _amountOfStETH ) external payable fundable autolock(STETH.getSharesByPooledEth(_amountOfStETH)) { _mintShares(_recipient, STETH.getSharesByPooledEth(_amountOfStETH)); } /** * @notice Mints wstETH tokens backed by the vault to a recipient. * @param _recipient Address of the recipient * @param _amountOfWstETH Amount of tokens to mint */ function mintWstETH( address _recipient, uint256 _amountOfWstETH ) external payable fundable autolock(_amountOfWstETH) { _mintShares(address(this), _amountOfWstETH); uint256 mintedStETH = STETH.getPooledEthBySharesRoundUp(_amountOfWstETH); uint256 wrappedWstETH = WSTETH.wrap(mintedStETH); SafeERC20.safeTransfer(WSTETH, _recipient, wrappedWstETH); } /** * @notice Burns stETH shares from the sender backed by the vault. Expects corresponding amount of stETH approved to this contract. * @param _amountOfShares Amount of stETH shares to burn */ function burnShares(uint256 _amountOfShares) external { STETH.transferSharesFrom(msg.sender, address(VAULT_HUB), _amountOfShares); _burnShares(_amountOfShares); } /** * @notice Burns stETH tokens from the sender backed by the vault. Expects stETH amount approved to this contract. * !NB: this will revert with `VaultHub.ZeroArgument("_amountOfShares")` if the amount of stETH is less than 1 share * @param _amountOfStETH Amount of stETH tokens to burn */ function burnStETH(uint256 _amountOfStETH) external { _burnStETH(_amountOfStETH); } /** * @notice Burns wstETH tokens from the sender backed by the vault. Expects wstETH amount approved to this contract. * !NB: this will revert with `VaultHub.ZeroArgument("_amountOfShares")` on 1 wei of wstETH due to rounding inside wstETH unwrap method * @param _amountOfWstETH Amount of wstETH tokens to burn */ function burnWstETH(uint256 _amountOfWstETH) external { _burnWstETH(_amountOfWstETH); } /** * @notice Rebalances the vault by transferring ether * @param _ether Amount of ether to rebalance */ function rebalanceVault(uint256 _ether) external payable fundable { _rebalanceVault(_ether); } /** * @notice Withdraws ether from vault and deposits directly to provided validators bypassing the default PDG process, * allowing validators to be proven post-factum via `proveUnknownValidatorsToPDG` * clearing them for future deposits via `PDG.depositToBeaconChain` * @param _deposits array of IStakingVault.Deposit structs containing deposit data * @return totalAmount total amount of ether deposited to beacon chain * @dev requires the caller to have the `UNGUARANTEED_BEACON_CHAIN_DEPOSIT_ROLE` * @dev can be used as PDG shortcut if the node operator is trusted to not frontrun provided deposits */ function unguaranteedDepositToBeaconChain( StakingVaultDeposit[] calldata _deposits ) public returns (uint256 totalAmount) { IStakingVault stakingVault_ = _stakingVault(); IDepositContract depositContract = stakingVault_.DEPOSIT_CONTRACT(); for (uint256 i = 0; i < _deposits.length; i++) { totalAmount += _deposits[i].amount; } if (totalAmount > unreserved()) { revert WithdrawalAmountExceedsUnreserved(totalAmount, unreserved()); } _withdrawForUnguaranteedDepositToBeaconChain(totalAmount); _setAccruedRewardsAdjustment(accruedRewardsAdjustment + totalAmount); bytes memory withdrawalCredentials = bytes.concat(stakingVault_.withdrawalCredentials()); StakingVaultDeposit calldata deposit; for (uint256 i = 0; i < _deposits.length; i++) { deposit = _deposits[i]; depositContract.deposit{value: deposit.amount}( deposit.pubkey, withdrawalCredentials, deposit.signature, deposit.depositDataRoot ); emit UnguaranteedDeposit(address(stakingVault_), deposit.pubkey, deposit.amount); } } /** * @notice Proves validators with correct vault WC if they are unknown to PDG * @param _witnesses array of IPredepositGuarantee.ValidatorWitness structs containing proof data for validators * @dev requires the caller to have the `PDG_PROVE_VALIDATOR_ROLE` */ function proveUnknownValidatorsToPDG(IPredepositGuarantee.ValidatorWitness[] calldata _witnesses) external { _proveUnknownValidatorsToPDG(_witnesses); } /** * @notice Compensates ether of disproven validator's predeposit from PDG to the recipient. * Can be called if validator which was predeposited via `PDG.predeposit` with vault funds * was frontrun by NO's with non-vault WC (effectively NO's stealing the predeposit) and then * proof of the validator's invalidity has been provided via `PDG.proveInvalidValidatorWC`. * @param _pubkey of validator that was proven invalid in PDG * @param _recipient address to receive the `PDG.PREDEPOSIT_AMOUNT` * @dev PDG will revert if _recipient is vault address, use fund() instead to return ether to vault * @dev requires the caller to have the `PDG_COMPENSATE_PREDEPOSIT_ROLE` */ function compensateDisprovenPredepositFromPDG(bytes calldata _pubkey, address _recipient) external { _compensateDisprovenPredepositFromPDG(_pubkey, _recipient); } /** * @notice Recovers ERC20 tokens or ether from the dashboard contract to sender * @param _token Address of the token to recover or 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee for ether * @param _recipient Address of the recovery recipient */ function recoverERC20(address _token, address _recipient, uint256 _amount) external onlyRole(RECOVER_ASSETS_ROLE) { if (_token == address(0)) revert ZeroArgument("_token"); if (_recipient == address(0)) revert ZeroArgument("_recipient"); if (_amount == 0) revert ZeroArgument("_amount"); if (_token == ETH) { (bool success, ) = payable(_recipient).call{value: _amount}(""); if (!success) revert EthTransferFailed(_recipient, _amount); } else { SafeERC20.safeTransfer(IERC20(_token), _recipient, _amount); } emit ERC20Recovered(_recipient, _token, _amount); } /** * @notice Transfers a given token_id of an ERC721-compatible NFT (defined by the token contract address) * from the dashboard contract to sender * * @param _token an ERC721-compatible token * @param _tokenId token id to recover * @param _recipient Address of the recovery recipient */ function recoverERC721( address _token, uint256 _tokenId, address _recipient ) external onlyRole(RECOVER_ASSETS_ROLE) { if (_token == address(0)) revert ZeroArgument("_token"); if (_recipient == address(0)) revert ZeroArgument("_recipient"); IERC721(_token).safeTransferFrom(address(this), _recipient, _tokenId); emit ERC721Recovered(_recipient, _token, _tokenId); } /** * @notice Pauses beacon chain deposits on the StakingVault. */ function pauseBeaconChainDeposits() external { _pauseBeaconChainDeposits(); } /** * @notice Resumes beacon chain deposits on the StakingVault. */ function resumeBeaconChainDeposits() external { _resumeBeaconChainDeposits(); } /** * @notice Signals to node operators that specific validators should exit from the beacon chain. It DOES NOT * directly trigger the exit - node operators must monitor for request events and handle the exits. * @param _pubkeys Concatenated validator public keys (48 bytes each). * @dev Emits `ValidatorExitRequested` event for each validator public key through the `StakingVault`. * This is a voluntary exit request - node operators can choose whether to act on it or not. */ function requestValidatorExit(bytes calldata _pubkeys) external { _requestValidatorExit(_pubkeys); } /** * @notice Initiates a withdrawal from validator(s) on the beacon chain using EIP-7002 triggerable withdrawals * Both partial withdrawals (disabled for unhealthy `StakingVault`) and full validator exits are supported. * @param _pubkeys Concatenated validator public keys (48 bytes each). * @param _amounts Withdrawal amounts in wei for each validator key and must match _pubkeys length. * Set amount to 0 for a full validator exit. * For partial withdrawals, amounts will be trimmed to keep MIN_ACTIVATION_BALANCE on the validator to avoid deactivation * @param _refundRecipient Address to receive any fee refunds, if zero, refunds go to msg.sender. * @dev A withdrawal fee must be paid via msg.value. * Use `StakingVault.calculateValidatorWithdrawalFee()` to determine the required fee for the current block. */ function triggerValidatorWithdrawal( bytes calldata _pubkeys, uint64[] calldata _amounts, address _refundRecipient ) external payable { _triggerValidatorWithdrawal(_pubkeys, _amounts, _refundRecipient); } /** * @notice Authorizes the Lido Vault Hub to manage the staking vault. */ function authorizeLidoVaultHub() external { _authorizeLidoVaultHub(); } /** * @notice Deauthorizes the Lido Vault Hub from managing the staking vault. */ function deauthorizeLidoVaultHub() external { _deauthorizeLidoVaultHub(); } /** * @notice Ossifies the staking vault. WARNING: This operation is irreversible, * once ossified, the vault cannot be upgraded or attached to VaultHub. * This is a one-way operation. * @dev Pins the current vault implementation to prevent further upgrades. */ function ossifyStakingVault() external { _ossifyStakingVault(); } /** * @notice Updates the address of the depositor for the staking vault. * @param _depositor Address of the new depositor. */ function setDepositor(address _depositor) external { _setDepositor(_depositor); } /** * @notice Zeroes the locked amount of the staking vault. * Can only be called on disconnected from the vault hub vaults. */ function resetLocked() external { _resetLocked(); } /** * @notice Requests a change of tier on the OperatorGrid. * @param _tierId The tier to change to. */ function requestTierChange(uint256 _tierId) external { _requestTierChange(_tierId); } // ==================== Internal Functions ==================== /** * @dev Modifier to fund the staking vault if msg.value > 0 */ modifier fundable() { if (msg.value > 0) { _fund(msg.value); } _; } /** * @dev Modifier to increase the locked amount if necessary * @param _newShares The number of new shares to mint */ modifier autolock(uint256 _newShares) { VaultHub.VaultSocket memory socket = vaultSocket(); // Calculate the locked amount required to accommodate the new shares uint256 requiredLock = (STETH.getPooledEthBySharesRoundUp(socket.liabilityShares + _newShares) * TOTAL_BASIS_POINTS) / (TOTAL_BASIS_POINTS - socket.reserveRatioBP); // If the required locked amount is greater than the current, increase the locked amount if (requiredLock > _stakingVault().locked()) { _lock(requiredLock); } _; } /** * @notice Returns the value of the staking vault with the node operator fee subtracted, * because the fee cannot be used to mint shares. * @return The amount of ether in wei that can be used to mint shares. */ function _mintableValue() internal view returns (uint256) { return _stakingVault().totalValue() - nodeOperatorUnclaimedFee(); } /** * @notice Mints shares within the mintable total value, * and reverts if the resulting backing is greater than the mintable total value. * @param _recipient The address of the recipient. * @param _amountOfShares The amount of shares to mint. */ function _mintSharesWithinMintingCapacity(address _recipient, uint256 _amountOfShares) internal { _mintShares(_recipient, _amountOfShares); uint256 locked = _stakingVault().locked(); uint256 mintableValue = _mintableValue(); if (locked > mintableValue) { revert MintingCapacityExceeded(locked, mintableValue); } } /** * @dev Burns stETH tokens from the sender backed by the vault * @param _amountOfStETH Amount of tokens to burn */ function _burnStETH(uint256 _amountOfStETH) internal { uint256 _amountOfShares = STETH.getSharesByPooledEth(_amountOfStETH); STETH.transferSharesFrom(msg.sender, address(VAULT_HUB), _amountOfShares); _burnShares(_amountOfShares); } /** * @dev Burns wstETH tokens from the sender backed by the vault * @param _amountOfWstETH Amount of tokens to burn */ function _burnWstETH(uint256 _amountOfWstETH) internal { SafeERC20.safeTransferFrom(WSTETH, msg.sender, address(this), _amountOfWstETH); uint256 unwrappedStETH = WSTETH.unwrap(_amountOfWstETH); uint256 unwrappedShares = STETH.getSharesByPooledEth(unwrappedStETH); STETH.transferShares(address(VAULT_HUB), unwrappedShares); _burnShares(unwrappedShares); } /** * @dev calculates the maximum number of stETH shares that can be minted by the vault * @param _additionalFunding additional ether that may be funded to the vault */ function _totalMintingCapacity(uint256 _additionalFunding) internal view returns (uint256) { uint256 maxMintableStETH = ((_mintableValue() + _additionalFunding) * (TOTAL_BASIS_POINTS - vaultSocket().reserveRatioBP)) / TOTAL_BASIS_POINTS; return Math256.min(STETH.getSharesByPooledEth(maxMintableStETH), vaultSocket().shareLimit); } // ==================== Events ==================== /** * @notice Emitted when ether was withdrawn from the staking vault and deposited to validators directly bypassing PDG * @param stakingVault the address of owned staking vault * @param pubkey of the validator to be deposited * @param amount of ether deposited to validator */ event UnguaranteedDeposit(address indexed stakingVault, bytes indexed pubkey, uint256 amount); /** * @notice Emitted when the ERC20 `token` or ether is recovered (i.e. transferred) * @param to The address of the recovery recipient * @param token The address of the recovered ERC20 token (0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee for ether) * @param amount The amount of the token recovered */ event ERC20Recovered(address indexed to, address indexed token, uint256 amount); /** * @notice Emitted when the ERC721-compatible `token` (NFT) recovered (i.e. transferred) * @param to The address of the recovery recipient * @param token The address of the recovered ERC721 token * @param tokenId id of token recovered */ event ERC721Recovered(address indexed to, address indexed token, uint256 tokenId); // ==================== Errors ==================== /** * @notice Emitted when the unreserved amount of ether is exceeded * @param amount The amount of ether that was attempted to be withdrawn * @param unreserved The amount of unreserved ether available */ error WithdrawalAmountExceedsUnreserved(uint256 amount, uint256 unreserved); /** * @notice Error thrown when recovery of ETH fails on transfer to recipient */ error EthTransferFailed(address recipient, uint256 amount); /** * @notice Error thrown when mintable total value is breached */ error MintingCapacityExceeded(uint256 locked, uint256 mintableValue); }
// 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.1.0) (access/extensions/AccessControlEnumerable.sol) pragma solidity ^0.8.20; import {IAccessControlEnumerable} from "./IAccessControlEnumerable.sol"; import {AccessControl} from "../AccessControl.sol"; import {EnumerableSet} from "../../utils/structs/EnumerableSet.sol"; /** * @dev Extension of {AccessControl} that allows enumerating the members of each role. */ abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { using EnumerableSet for EnumerableSet.AddressSet; mapping(bytes32 role => EnumerableSet.AddressSet) private _roleMembers; /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns one of the accounts that have `role`. `index` must be a * value between 0 and {getRoleMemberCount}, non-inclusive. * * Role bearers are not sorted in any particular way, and their ordering may * change at any point. * * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure * you perform all queries on the same block. See the following * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] * for more information. */ function getRoleMember(bytes32 role, uint256 index) public view virtual returns (address) { return _roleMembers[role].at(index); } /** * @dev Returns the number of accounts that have `role`. Can be used * together with {getRoleMember} to enumerate all bearers of a role. */ function getRoleMemberCount(bytes32 role) public view virtual returns (uint256) { return _roleMembers[role].length(); } /** * @dev Return all accounts that have `role` * * 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 getRoleMembers(bytes32 role) public view virtual returns (address[] memory) { return _roleMembers[role].values(); } /** * @dev Overload {AccessControl-_grantRole} to track enumerable memberships */ function _grantRole(bytes32 role, address account) internal virtual override returns (bool) { bool granted = super._grantRole(role, account); if (granted) { _roleMembers[role].add(account); } return granted; } /** * @dev Overload {AccessControl-_revokeRole} to track enumerable memberships */ function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) { bool revoked = super._revokeRole(role, account); if (revoked) { _roleMembers[role].remove(account); } return revoked; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (access/extensions/IAccessControlEnumerable.sol) pragma solidity ^0.8.20; import {IAccessControl} from "../IAccessControl.sol"; /** * @dev External interface of AccessControlEnumerable declared to support ERC-165 detection. */ interface IAccessControlEnumerable is IAccessControl { /** * @dev Returns one of the accounts that have `role`. `index` must be a * value between 0 and {getRoleMemberCount}, non-inclusive. * * Role bearers are not sorted in any particular way, and their ordering may * change at any point. * * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure * you perform all queries on the same block. See the following * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] * for more information. */ function getRoleMember(bytes32 role, uint256 index) external view returns (address); /** * @dev Returns the number of accounts that have `role`. Can be used * together with {getRoleMember} to enumerate all bearers of a role. */ function getRoleMemberCount(bytes32 role) external view returns (uint256); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.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.1.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.2.0) (proxy/Clones.sol) pragma solidity ^0.8.20; import {Create2} from "../utils/Create2.sol"; import {Errors} from "../utils/Errors.sol"; /** * @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for * deploying minimal proxy contracts, also known as "clones". * * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies * > a minimal bytecode implementation that delegates all calls to a known, fixed address. * * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the * deterministic method. */ library Clones { error CloneArgumentsTooLong(); /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create opcode, which should never revert. */ function clone(address implementation) internal returns (address instance) { return clone(implementation, 0); } /** * @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency * to the new contract. * * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory) * to always have enough balance for new deployments. Consider exposing this function under a payable method. */ function clone(address implementation, uint256 value) internal returns (address instance) { if (address(this).balance < value) { revert Errors.InsufficientBalance(address(this).balance, value); } assembly ("memory-safe") { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create(value, 0x09, 0x37) } if (instance == address(0)) { revert Errors.FailedDeployment(); } } /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create2 opcode and a `salt` to deterministically deploy * the clone. Using the same `implementation` and `salt` multiple times will revert, since * the clones cannot be deployed twice at the same address. */ function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { return cloneDeterministic(implementation, salt, 0); } /** * @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with * a `value` parameter to send native currency to the new contract. * * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory) * to always have enough balance for new deployments. Consider exposing this function under a payable method. */ function cloneDeterministic( address implementation, bytes32 salt, uint256 value ) internal returns (address instance) { if (address(this).balance < value) { revert Errors.InsufficientBalance(address(this).balance, value); } assembly ("memory-safe") { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create2(value, 0x09, 0x37, salt) } if (instance == address(0)) { revert Errors.FailedDeployment(); } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { assembly ("memory-safe") { let ptr := mload(0x40) mstore(add(ptr, 0x38), deployer) mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) mstore(add(ptr, 0x14), implementation) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) mstore(add(ptr, 0x58), salt) mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) predicted := and(keccak256(add(ptr, 0x43), 0x55), 0xffffffffffffffffffffffffffffffffffffffff) } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt ) internal view returns (address predicted) { return predictDeterministicAddress(implementation, salt, address(this)); } /** * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom * immutable arguments. These are provided through `args` and cannot be changed after deployment. To * access the arguments within the implementation, use {fetchCloneArgs}. * * This function uses the create opcode, which should never revert. */ function cloneWithImmutableArgs(address implementation, bytes memory args) internal returns (address instance) { return cloneWithImmutableArgs(implementation, args, 0); } /** * @dev Same as {xref-Clones-cloneWithImmutableArgs-address-bytes-}[cloneWithImmutableArgs], but with a `value` * parameter to send native currency to the new contract. * * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory) * to always have enough balance for new deployments. Consider exposing this function under a payable method. */ function cloneWithImmutableArgs( address implementation, bytes memory args, uint256 value ) internal returns (address instance) { if (address(this).balance < value) { revert Errors.InsufficientBalance(address(this).balance, value); } bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args); assembly ("memory-safe") { instance := create(value, add(bytecode, 0x20), mload(bytecode)) } if (instance == address(0)) { revert Errors.FailedDeployment(); } } /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation` with custom * immutable arguments. These are provided through `args` and cannot be changed after deployment. To * access the arguments within the implementation, use {fetchCloneArgs}. * * This function uses the create2 opcode and a `salt` to deterministically deploy the clone. Using the same * `implementation`, `args` and `salt` multiple times will revert, since the clones cannot be deployed twice * at the same address. */ function cloneDeterministicWithImmutableArgs( address implementation, bytes memory args, bytes32 salt ) internal returns (address instance) { return cloneDeterministicWithImmutableArgs(implementation, args, salt, 0); } /** * @dev Same as {xref-Clones-cloneDeterministicWithImmutableArgs-address-bytes-bytes32-}[cloneDeterministicWithImmutableArgs], * but with a `value` parameter to send native currency to the new contract. * * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory) * to always have enough balance for new deployments. Consider exposing this function under a payable method. */ function cloneDeterministicWithImmutableArgs( address implementation, bytes memory args, bytes32 salt, uint256 value ) internal returns (address instance) { bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args); return Create2.deploy(value, salt, bytecode); } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}. */ function predictDeterministicAddressWithImmutableArgs( address implementation, bytes memory args, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args); return Create2.computeAddress(salt, keccak256(bytecode), deployer); } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}. */ function predictDeterministicAddressWithImmutableArgs( address implementation, bytes memory args, bytes32 salt ) internal view returns (address predicted) { return predictDeterministicAddressWithImmutableArgs(implementation, args, salt, address(this)); } /** * @dev Get the immutable args attached to a clone. * * - If `instance` is a clone that was deployed using `clone` or `cloneDeterministic`, this * function will return an empty array. * - If `instance` is a clone that was deployed using `cloneWithImmutableArgs` or * `cloneDeterministicWithImmutableArgs`, this function will return the args array used at * creation. * - If `instance` is NOT a clone deployed using this library, the behavior is undefined. This * function should only be used to check addresses that are known to be clones. */ function fetchCloneArgs(address instance) internal view returns (bytes memory) { bytes memory result = new bytes(instance.code.length - 45); // revert if length is too short assembly ("memory-safe") { extcodecopy(instance, add(result, 32), 45, mload(result)) } return result; } /** * @dev Helper that prepares the initcode of the proxy with immutable args. * * An assembly variant of this function requires copying the `args` array, which can be efficiently done using * `mcopy`. Unfortunately, that opcode is not available before cancun. A pure solidity implementation using * abi.encodePacked is more expensive but also more portable and easier to review. * * NOTE: https://eips.ethereum.org/EIPS/eip-170[EIP-170] limits the length of the contract code to 24576 bytes. * With the proxy code taking 45 bytes, that limits the length of the immutable args to 24531 bytes. */ function _cloneCodeWithImmutableArgs( address implementation, bytes memory args ) private pure returns (bytes memory) { if (args.length > 24531) revert CloneArgumentsTooLong(); return abi.encodePacked( hex"61", uint16(args.length + 45), hex"3d81600a3d39f3363d3d373d3d3d363d73", implementation, hex"5af43d82803e903d91602b57fd5bf3", args ); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[ERC-2612]. * * Adds the {permit} method, which can be used to change an account's ERC-20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.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.2.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC1363} from "../../../interfaces/IERC1363.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. * * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client" * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior. */ 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. * * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client" * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior. */ 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. * * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being * set here. */ 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.1.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.20; import {IERC165} from "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC-721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon * a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC-721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or * {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon * a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the address zero. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// 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 // OpenZeppelin Contracts (last updated v5.1.0) (utils/Create2.sol) pragma solidity ^0.8.20; import {Errors} from "./Errors.sol"; /** * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer. * `CREATE2` can be used to compute in advance the address where a smart * contract will be deployed, which allows for interesting new mechanisms known * as 'counterfactual interactions'. * * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more * information. */ library Create2 { /** * @dev There's no code to deploy. */ error Create2EmptyBytecode(); /** * @dev Deploys a contract using `CREATE2`. The address where the contract * will be deployed can be known in advance via {computeAddress}. * * The bytecode for a contract can be obtained from Solidity with * `type(contractName).creationCode`. * * Requirements: * * - `bytecode` must not be empty. * - `salt` must have not been used for `bytecode` already. * - the factory must have a balance of at least `amount`. * - if `amount` is non-zero, `bytecode` must have a `payable` constructor. */ function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) { if (address(this).balance < amount) { revert Errors.InsufficientBalance(address(this).balance, amount); } if (bytecode.length == 0) { revert Create2EmptyBytecode(); } assembly ("memory-safe") { addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt) // if no address was created, and returndata is not empty, bubble revert if and(iszero(addr), not(iszero(returndatasize()))) { let p := mload(0x40) returndatacopy(p, 0, returndatasize()) revert(p, returndatasize()) } } if (addr == address(0)) { revert Errors.FailedDeployment(); } } /** * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the * `bytecodeHash` or `salt` will result in a new destination address. */ function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) { return computeAddress(salt, bytecodeHash, address(this)); } /** * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}. */ function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) { assembly ("memory-safe") { let ptr := mload(0x40) // Get free memory pointer // | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... | // |-------------------|---------------------------------------------------------------------------| // | bytecodeHash | CCCCCCCCCCCCC...CC | // | salt | BBBBBBBBBBBBB...BB | // | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA | // | 0xFF | FF | // |-------------------|---------------------------------------------------------------------------| // | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC | // | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ | mstore(add(ptr, 0x40), bytecodeHash) mstore(add(ptr, 0x20), salt) mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff mstore8(start, 0xff) addr := and(keccak256(start, 85), 0xffffffffffffffffffffffffffffffffffffffff) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/Hashes.sol) pragma solidity ^0.8.20; /** * @dev Library of standard hash functions. * * _Available since v5.1._ */ library Hashes { /** * @dev Commutative Keccak256 hash of a sorted pair of bytes32. Frequently used when working with merkle proofs. * * NOTE: Equivalent to the `standardNodeHash` in our https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. */ function commutativeKeccak256(bytes32 a, bytes32 b) internal pure returns (bytes32) { return a < b ? _efficientKeccak256(a, b) : _efficientKeccak256(b, a); } /** * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory. */ function _efficientKeccak256(bytes32 a, bytes32 b) private pure returns (bytes32 value) { assembly ("memory-safe") { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/MerkleProof.sol) // This file was procedurally generated from scripts/generate/templates/MerkleProof.js. pragma solidity ^0.8.20; import {Hashes} from "./Hashes.sol"; /** * @dev These functions deal with verification of Merkle Tree proofs. * * The tree and the proofs can be generated using our * https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. * You will find a quickstart guide in the readme. * * WARNING: You should avoid using leaf values that are 64 bytes long prior to * hashing, or use a hash function other than keccak256 for hashing leaves. * This is because the concatenation of a sorted pair of internal nodes in * the Merkle tree could be reinterpreted as a leaf value. * OpenZeppelin's JavaScript library generates Merkle trees that are safe * against this attack out of the box. * * IMPORTANT: Consider memory side-effects when using custom hashing functions * that access memory in an unsafe way. * * NOTE: This library supports proof verification for merkle trees built using * custom _commutative_ hashing functions (i.e. `H(a, b) == H(b, a)`). Proving * leaf inclusion in trees built using non-commutative hashing functions requires * additional logic that is not supported by this library. */ library MerkleProof { /** *@dev The multiproof provided is not valid. */ error MerkleProofInvalidMultiproof(); /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. * * This version handles proofs in memory with the default hashing function. */ function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leaves & pre-images are assumed to be sorted. * * This version handles proofs in memory with the default hashing function. */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]); } return computedHash; } /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. * * This version handles proofs in memory with a custom hashing function. */ function verify( bytes32[] memory proof, bytes32 root, bytes32 leaf, function(bytes32, bytes32) view returns (bytes32) hasher ) internal view returns (bool) { return processProof(proof, leaf, hasher) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leaves & pre-images are assumed to be sorted. * * This version handles proofs in memory with a custom hashing function. */ function processProof( bytes32[] memory proof, bytes32 leaf, function(bytes32, bytes32) view returns (bytes32) hasher ) internal view returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = hasher(computedHash, proof[i]); } return computedHash; } /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. * * This version handles proofs in calldata with the default hashing function. */ function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProofCalldata(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leaves & pre-images are assumed to be sorted. * * This version handles proofs in calldata with the default hashing function. */ function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]); } return computedHash; } /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. * * This version handles proofs in calldata with a custom hashing function. */ function verifyCalldata( bytes32[] calldata proof, bytes32 root, bytes32 leaf, function(bytes32, bytes32) view returns (bytes32) hasher ) internal view returns (bool) { return processProofCalldata(proof, leaf, hasher) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leaves & pre-images are assumed to be sorted. * * This version handles proofs in calldata with a custom hashing function. */ function processProofCalldata( bytes32[] calldata proof, bytes32 leaf, function(bytes32, bytes32) view returns (bytes32) hasher ) internal view returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = hasher(computedHash, proof[i]); } return computedHash; } /** * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}. * * This version handles multiproofs in memory with the default hashing function. * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. * * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`. * The `leaves` must be validated independently. See {processMultiProof}. */ function multiProofVerify( bytes32[] memory proof, bool[] memory proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProof(proof, proofFlags, leaves) == root; } /** * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false * respectively. * * This version handles multiproofs in memory with the default hashing function. * * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). * * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op, * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not * validating the leaves elsewhere. */ function processMultiProof( bytes32[] memory proof, bool[] memory proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the Merkle tree. uint256 leavesLen = leaves.length; uint256 proofFlagsLen = proofFlags.length; // Check proof validity. if (leavesLen + proof.length != proofFlagsLen + 1) { revert MerkleProofInvalidMultiproof(); } // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](proofFlagsLen); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < proofFlagsLen; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = Hashes.commutativeKeccak256(a, b); } if (proofFlagsLen > 0) { if (proofPos != proof.length) { revert MerkleProofInvalidMultiproof(); } unchecked { return hashes[proofFlagsLen - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}. * * This version handles multiproofs in memory with a custom hashing function. * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. * * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`. * The `leaves` must be validated independently. See {processMultiProof}. */ function multiProofVerify( bytes32[] memory proof, bool[] memory proofFlags, bytes32 root, bytes32[] memory leaves, function(bytes32, bytes32) view returns (bytes32) hasher ) internal view returns (bool) { return processMultiProof(proof, proofFlags, leaves, hasher) == root; } /** * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false * respectively. * * This version handles multiproofs in memory with a custom hashing function. * * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). * * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op, * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not * validating the leaves elsewhere. */ function processMultiProof( bytes32[] memory proof, bool[] memory proofFlags, bytes32[] memory leaves, function(bytes32, bytes32) view returns (bytes32) hasher ) internal view returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the Merkle tree. uint256 leavesLen = leaves.length; uint256 proofFlagsLen = proofFlags.length; // Check proof validity. if (leavesLen + proof.length != proofFlagsLen + 1) { revert MerkleProofInvalidMultiproof(); } // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](proofFlagsLen); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < proofFlagsLen; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = hasher(a, b); } if (proofFlagsLen > 0) { if (proofPos != proof.length) { revert MerkleProofInvalidMultiproof(); } unchecked { return hashes[proofFlagsLen - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}. * * This version handles multiproofs in calldata with the default hashing function. * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. * * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`. * The `leaves` must be validated independently. See {processMultiProofCalldata}. */ function multiProofVerifyCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProofCalldata(proof, proofFlags, leaves) == root; } /** * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false * respectively. * * This version handles multiproofs in calldata with the default hashing function. * * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). * * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op, * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not * validating the leaves elsewhere. */ function processMultiProofCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the Merkle tree. uint256 leavesLen = leaves.length; uint256 proofFlagsLen = proofFlags.length; // Check proof validity. if (leavesLen + proof.length != proofFlagsLen + 1) { revert MerkleProofInvalidMultiproof(); } // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](proofFlagsLen); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < proofFlagsLen; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = Hashes.commutativeKeccak256(a, b); } if (proofFlagsLen > 0) { if (proofPos != proof.length) { revert MerkleProofInvalidMultiproof(); } unchecked { return hashes[proofFlagsLen - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}. * * This version handles multiproofs in calldata with a custom hashing function. * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. * * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`. * The `leaves` must be validated independently. See {processMultiProofCalldata}. */ function multiProofVerifyCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32 root, bytes32[] memory leaves, function(bytes32, bytes32) view returns (bytes32) hasher ) internal view returns (bool) { return processMultiProofCalldata(proof, proofFlags, leaves, hasher) == root; } /** * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false * respectively. * * This version handles multiproofs in calldata with a custom hashing function. * * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). * * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op, * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not * validating the leaves elsewhere. */ function processMultiProofCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32[] memory leaves, function(bytes32, bytes32) view returns (bytes32) hasher ) internal view returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the Merkle tree. uint256 leavesLen = leaves.length; uint256 proofFlagsLen = proofFlags.length; // Check proof validity. if (leavesLen + proof.length != proofFlagsLen + 1) { revert MerkleProofInvalidMultiproof(); } // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](proofFlagsLen); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < proofFlagsLen; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = hasher(a, b); } if (proofFlagsLen > 0) { if (proofPos != proof.length) { revert MerkleProofInvalidMultiproof(); } unchecked { return hashes[proofFlagsLen - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol) 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.1.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.1.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.1.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-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md pragma solidity 0.8.25; interface IDepositContract { function get_deposit_root() external view returns (bytes32 rootHash); function deposit( bytes calldata pubkey, // 48 bytes bytes calldata withdrawal_credentials, // 32 bytes bytes calldata signature, // 96 bytes bytes32 deposit_data_root ) external payable; }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md pragma solidity 0.8.25; import {IERC20} from "@openzeppelin/contracts-v5.2/token/ERC20/IERC20.sol"; import {IERC20Permit} from "@openzeppelin/contracts-v5.2/token/ERC20/extensions/IERC20Permit.sol"; interface ILido is IERC20, IERC20Permit { function getSharesByPooledEth(uint256) external view returns (uint256); function getPooledEthByShares(uint256) external view returns (uint256); function getPooledEthBySharesRoundUp(uint256) external view returns (uint256); function transferSharesFrom(address, address, uint256) external returns (uint256); function transferShares(address, uint256) external returns (uint256); function rebalanceExternalEtherToInternal() external payable; function getTotalPooledEther() external view returns (uint256); function getExternalEther() external view returns (uint256); function getExternalShares() external view returns (uint256); function mintExternalShares(address, uint256) external; function burnExternalShares(uint256) external; function getTotalShares() external view returns (uint256); function getBeaconStat() external view returns (uint256 depositedValidators, uint256 beaconValidators, uint256 beaconBalance); function processClStateUpdate( uint256 _reportTimestamp, uint256 _preClValidators, uint256 _reportClValidators, uint256 _reportClBalance ) external; function collectRewardsAndProcessWithdrawals( uint256 _reportTimestamp, uint256 _reportClBalance, uint256 _adjustedPreCLBalance, uint256 _withdrawalsToWithdraw, uint256 _elRewardsToWithdraw, uint256 _lastWithdrawalRequestToFinalize, uint256 _simulatedShareRate, uint256 _etherToLockOnWithdrawalQueue ) external; function emitTokenRebase( uint256 _reportTimestamp, uint256 _timeElapsed, uint256 _preTotalShares, uint256 _preTotalEther, uint256 _postTotalShares, uint256 _postTotalEther, uint256 _postInternalShares, uint256 _postInternalEther, uint256 _sharesMintedAsFees ) external; function mintShares(address _recipient, uint256 _sharesAmount) external; }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md pragma solidity 0.8.25; import {AccessControlEnumerable} from "@openzeppelin/contracts-v5.2/access/extensions/AccessControlEnumerable.sol"; /** * @title AccessControlConfirmable * @author Lido * @notice An extension of AccessControlEnumerable that allows exectuing functions by mutual confirmation. * @dev This contract extends AccessControlEnumerable and adds a confirmation mechanism in the form of a modifier. */ abstract contract AccessControlConfirmable is AccessControlEnumerable { /** * @notice Tracks confirmations * - callData: msg.data of the call (selector + arguments) * - role: role that confirmed the action * - expiryTimestamp: timestamp of the confirmation. */ mapping(bytes callData => mapping(bytes32 role => uint256 expiryTimestamp)) public confirmations; /** * @notice Minimal confirmation expiry in seconds. */ uint256 public constant MIN_CONFIRM_EXPIRY = 1 days; /** * @notice Maximal confirmation expiry in seconds. */ uint256 public constant MAX_CONFIRM_EXPIRY = 30 days; /** * @notice Confirmation expiry in seconds; after this period, the confirmation expires and no longer counts. * @dev We cannot set this to 0 because this means that all confirmations have to be in the same block, * which can never be guaranteed. And, more importantly, if the `_setConfirmExpiry` is restricted by * the `onlyConfirmed` modifier, the confirmation expiry will be tricky to change. * This is why this variable is private, set to a default value of 1 day and cannot be set to 0. */ uint256 private confirmExpiry = MIN_CONFIRM_EXPIRY; /** * @notice Returns the confirmation expiry. * @return The confirmation expiry in seconds. */ function getConfirmExpiry() public view returns (uint256) { return confirmExpiry; } /** * @dev Restricts execution of the function unless confirmed by all specified roles. * Confirmation, in this context, is a call to the same function with the same arguments. * * The confirmation process works as follows: * 1. When a role member calls the function: * - Their confirmation is counted immediately * - If not enough confirmations exist, their confirmation is recorded * - If they're not a member of any of the specified roles, the call reverts * * 2. Confirmation counting: * - Counts the current caller's confirmations if they're a member of any of the specified roles * - Counts existing confirmations that are not expired, i.e. expiry is not exceeded * * 3. Execution: * - If all members of the specified roles have confirmed, executes the function * - On successful execution, clears all confirmations for this call * - If not enough confirmations, stores the current confirmations * - Thus, if the caller has all the roles, the function is executed immediately * * 4. Gas Optimization: * - Confirmations are stored in a deferred manner using a memory array * - Confirmation storage writes only occur if the function cannot be executed immediately * - This prevents unnecessary storage writes when all confirmations are present, * because the confirmations are cleared anyway after the function is executed, * - i.e. this optimization is beneficial for the deciding caller and * saves 1 storage write for each role the deciding caller has * * @param _roles Array of role identifiers that must confirm the call in order to execute it * * @notice Confirmations past their expiry are not counted and must be recast * @notice Only members of the specified roles can submit confirmations * @notice The order of confirmations does not matter * */ modifier onlyConfirmed(bytes32[] memory _roles) { if (_roles.length == 0) revert ZeroConfirmingRoles(); uint256 numberOfRoles = _roles.length; uint256 numberOfConfirms = 0; bool[] memory deferredConfirms = new bool[](numberOfRoles); bool isRoleMember = false; uint256 expiryTimestamp = block.timestamp + confirmExpiry; for (uint256 i = 0; i < numberOfRoles; ++i) { bytes32 role = _roles[i]; if (super.hasRole(role, msg.sender)) { isRoleMember = true; numberOfConfirms++; deferredConfirms[i] = true; emit RoleMemberConfirmed(msg.sender, role, expiryTimestamp, msg.data); } else if (confirmations[msg.data][role] >= block.timestamp) { numberOfConfirms++; } } if (!isRoleMember) revert SenderNotMember(); if (numberOfConfirms == numberOfRoles) { for (uint256 i = 0; i < numberOfRoles; ++i) { bytes32 role = _roles[i]; delete confirmations[msg.data][role]; } _; } else { for (uint256 i = 0; i < numberOfRoles; ++i) { if (deferredConfirms[i]) { bytes32 role = _roles[i]; confirmations[msg.data][role] = expiryTimestamp; } } } } /** * @dev Sets the confirmation expiry. * Confirmation expiry is a period during which the confirmation is counted. Once expired, * the confirmation no longer counts and must be recasted for the confirmation to go through. * @dev Does not retroactively apply to existing confirmations. * @param _newConfirmExpiry The new confirmation expiry in seconds. */ function _setConfirmExpiry(uint256 _newConfirmExpiry) internal { if (_newConfirmExpiry < MIN_CONFIRM_EXPIRY || _newConfirmExpiry > MAX_CONFIRM_EXPIRY) revert ConfirmExpiryOutOfBounds(); uint256 oldConfirmExpiry = confirmExpiry; confirmExpiry = _newConfirmExpiry; emit ConfirmExpirySet(msg.sender, oldConfirmExpiry, _newConfirmExpiry); } /** * @dev Emitted when the confirmation expiry is set. * @param oldConfirmExpiry The old confirmation expiry. * @param newConfirmExpiry The new confirmation expiry. */ event ConfirmExpirySet(address indexed sender, uint256 oldConfirmExpiry, uint256 newConfirmExpiry); /** * @dev Emitted when a role member confirms. * @param member The address of the confirming member. * @param role The role of the confirming member. * @param expiryTimestamp The timestamp of the confirmation. * @param data The msg.data of the confirmation (selector + arguments). */ event RoleMemberConfirmed(address indexed member, bytes32 indexed role, uint256 expiryTimestamp, bytes data); /** * @dev Thrown when attempting to set confirmation expiry out of bounds. */ error ConfirmExpiryOutOfBounds(); /** * @dev Thrown when a caller without a required role attempts to confirm. */ error SenderNotMember(); /** * @dev Thrown when the roles array is empty. */ error ZeroConfirmingRoles(); }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md pragma solidity 0.8.25; import {PausableUntil} from "contracts/common/utils/PausableUntil.sol"; import {AccessControlEnumerableUpgradeable} from "contracts/openzeppelin/5.2/upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; /** * @title PausableUntilWithRoles * @notice a `PausableUntil` implementation using OpenZeppelin's `AccessControlEnumerableUpgradeable` * @dev the inheriting contract must use `whenNotPaused` modifier from `PausableUntil` to block some functions on pause */ abstract contract PausableUntilWithRoles is PausableUntil, AccessControlEnumerableUpgradeable { /// @notice role that allows to pause the contract bytes32 public constant PAUSE_ROLE = keccak256("PausableUntilWithRoles.PauseRole"); /// @notice role that allows to resume the contract bytes32 public constant RESUME_ROLE = keccak256("PausableUntilWithRoles.ResumeRole"); /** * @notice Resume the contract * @dev Reverts if contracts is not paused * @dev Reverts if sender has no `RESUME_ROLE` */ function resume() external onlyRole(RESUME_ROLE) { _resume(); } /** * @notice Pause the contract for a specified period * @param _duration pause duration in seconds (use `PAUSE_INFINITELY` for unlimited) * @dev Reverts if contract is already paused * @dev Reverts if sender has no `PAUSE_ROLE` * @dev Reverts if zero duration is passed */ function pauseFor(uint256 _duration) external onlyRole(PAUSE_ROLE) { _pauseFor(_duration); } /** * @notice Pause the contract until a specified timestamp * @param _pauseUntilInclusive the last second to pause until inclusive * @dev Reverts if the timestamp is in the past * @dev Reverts if sender has no `PAUSE_ROLE` * @dev Reverts if contract is already paused */ function pauseUntil(uint256 _pauseUntilInclusive) external onlyRole(PAUSE_ROLE) { _pauseUntil(_pauseUntilInclusive); } }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md pragma solidity 0.8.25; import {IStakingVault} from "../interfaces/IStakingVault.sol"; import {Permissions} from "./Permissions.sol"; /** * @title NodeOperatorFee * @author Lido * @notice This contract manages the node operator fee and claiming mechanism. * It reserves a portion of the staking rewards for the node operator, and allows * the node operator to claim their fee. * * Key features: * - Tracks node operator fees based on staking rewards * - Provides fee claiming mechanism via role-based access control * - Restricts withdrawals based on the fee reserved for node operators * - Requires both the node operator and default admin to confirm the fee changes * * Node operator fees are calculated as a percentage of the staking rewards accrued * between the last claimed report and the latest report in the StakingVault. * If the fee was never claimed, the percentage is calculated based on the total * rewards accrued since the StakingVault was created. */ contract NodeOperatorFee is Permissions { /** * @notice Total basis points; 1bp = 0.01%, 100_00bp = 100.00%. */ uint256 internal constant TOTAL_BASIS_POINTS = 100_00; /** * @notice Bitwise AND mask that clamps the value to positive int128 range */ uint256 private constant ADJUSTMENT_CLAMP_MASK = uint256(uint128(type(int128).max)); /** * @notice Maximum value that can be set via manual adjustment */ uint256 public constant MANUAL_ACCRUED_REWARDS_ADJUSTMENT_LIMIT = 10_000_000 ether; /** * @notice Node operator manager role: * - confirms confirm expiry; * - confirms node operator fee changes; * - confirms the transfer of the StakingVault ownership; * - is the admin role for NODE_OPERATOR_FEE_CLAIM_ROLE. */ bytes32 public constant NODE_OPERATOR_MANAGER_ROLE = keccak256("vaults.NodeOperatorFee.NodeOperatorManagerRole"); /** * @notice Claims node operator fee. */ bytes32 public constant NODE_OPERATOR_FEE_CLAIM_ROLE = keccak256("vaults.NodeOperatorFee.FeeClaimRole"); /** * @notice Adjusts rewards to allow fee correction during side deposits or consolidations */ bytes32 public constant NODE_OPERATOR_REWARDS_ADJUST_ROLE = keccak256("vaults.NodeOperatorFee.RewardsAdjustRole"); /** * @notice Node operator fee in basis points; cannot exceed 100.00%. * The node operator's unclaimed fee in ether is returned by `nodeOperatorUnclaimedFee()`. */ uint256 public nodeOperatorFeeBP; /** * @notice The last report for which node operator fee was claimed. Updated on each claim. */ IStakingVault.Report public nodeOperatorFeeClaimedReport; /** * @notice Adjustment to allow fee correction during side deposits or consolidations. * - can be increased manually by `increaseAccruedRewardsAdjustment` by NODE_OPERATOR_REWARDS_ADJUST_ROLE * - can be set via `setAccruedRewardsAdjustment` by `confirmingRoles()` * - increased automatically with `unguaranteedDepositToBeaconChain` by total ether amount of deposits * - reset to zero after `claimNodeOperatorFee` * This amount will be deducted from rewards during NO fee calculation and can be used effectively write off NO's accrued fees. * */ uint256 public accruedRewardsAdjustment; /** * @notice Passes the address of the vault hub up the inheritance chain. * @param _vaultHub The address of the vault hub. */ constructor(address _vaultHub) Permissions(_vaultHub) {} /** * @dev Calls the parent's initializer, sets the node operator fee, assigns the node operator manager role, * and makes the node operator manager the admin for the node operator roles. * @param _defaultAdmin The address of the default admin * @param _nodeOperatorManager The address of the node operator manager * @param _nodeOperatorFeeBP The node operator fee in basis points * @param _confirmExpiry The confirmation expiry time in seconds */ function _initialize( address _defaultAdmin, address _nodeOperatorManager, uint256 _nodeOperatorFeeBP, uint256 _confirmExpiry ) internal { if (_nodeOperatorManager == address(0)) revert ZeroArgument("_nodeOperatorManager"); super._initialize(_defaultAdmin, _confirmExpiry); _setNodeOperatorFeeBP(_nodeOperatorFeeBP); _grantRole(NODE_OPERATOR_MANAGER_ROLE, _nodeOperatorManager); _setRoleAdmin(NODE_OPERATOR_MANAGER_ROLE, NODE_OPERATOR_MANAGER_ROLE); _setRoleAdmin(NODE_OPERATOR_FEE_CLAIM_ROLE, NODE_OPERATOR_MANAGER_ROLE); _setRoleAdmin(NODE_OPERATOR_REWARDS_ADJUST_ROLE, NODE_OPERATOR_MANAGER_ROLE); } /** * @notice Returns the roles that can: * - change the confirm expiry; * - set the node operator fee; * - transfer the ownership of the StakingVault. * @return roles is an array of roles that form the confirming roles. */ function confirmingRoles() public pure override returns (bytes32[] memory roles) { roles = new bytes32[](2); roles[0] = DEFAULT_ADMIN_ROLE; roles[1] = NODE_OPERATOR_MANAGER_ROLE; } /** * @notice Returns the accumulated unclaimed node operator fee in ether, * calculated as: U = ((R - A) * F) / T * where: * - U is the node operator unclaimed fee; * - R is the StakingVault rewards accrued since the last node operator fee claim; * - F is `nodeOperatorFeeBP`; * - A is `accruedRewardsAdjustment`; * - T is the total basis points, 10,000. * @return uint256: the amount of unclaimed fee in ether. */ function nodeOperatorUnclaimedFee() public view returns (uint256) { IStakingVault.Report memory latestReport = _stakingVault().latestReport(); IStakingVault.Report storage _lastClaimedReport = nodeOperatorFeeClaimedReport; // cast down safely clamping to int128.max int128 adjustment = int128(int256(accruedRewardsAdjustment & ADJUSTMENT_CLAMP_MASK)); int128 rewardsAccrued = int128(latestReport.totalValue) - int128(_lastClaimedReport.totalValue) - (latestReport.inOutDelta - _lastClaimedReport.inOutDelta) - adjustment; return rewardsAccrued > 0 ? (uint256(uint128(rewardsAccrued)) * nodeOperatorFeeBP) / TOTAL_BASIS_POINTS : 0; } /** * @notice Sets the confirm expiry. * Confirm expiry is a period during which the confirm is counted. Once the period is over, * the confirm is considered expired, no longer counts and must be recasted. * @param _newConfirmExpiry The new confirm expiry in seconds. */ function setConfirmExpiry(uint256 _newConfirmExpiry) external onlyConfirmed(confirmingRoles()) { _setConfirmExpiry(_newConfirmExpiry); } /** * @notice Sets the node operator fee. * The node operator fee is the percentage (in basis points) of node operator's share of the StakingVault rewards. * The node operator fee cannot exceed 100%. * Note that the function reverts if the node operator fee is unclaimed and all the confirms must be recasted to execute it again, * which is why the deciding confirm must make sure that `nodeOperatorUnclaimedFee()` is 0 before calling this function. * @param _newNodeOperatorFeeBP The new node operator fee in basis points. */ function setNodeOperatorFeeBP(uint256 _newNodeOperatorFeeBP) external onlyConfirmed(confirmingRoles()) { _setNodeOperatorFeeBP(_newNodeOperatorFeeBP); } /** * @notice Claims the node operator fee. * Note that the authorized role is NODE_OPERATOR_FEE_CLAIMER_ROLE, not NODE_OPERATOR_MANAGER_ROLE, * although NODE_OPERATOR_MANAGER_ROLE is the admin role for NODE_OPERATOR_FEE_CLAIMER_ROLE. * @param _recipient The address to which the node operator fee will be sent. */ function claimNodeOperatorFee(address _recipient) external onlyRole(NODE_OPERATOR_FEE_CLAIM_ROLE) { if (_recipient == address(0)) revert ZeroArgument("_recipient"); uint256 fee = nodeOperatorUnclaimedFee(); if (fee == 0) revert NoUnclaimedFee(); if (accruedRewardsAdjustment != 0) _setAccruedRewardsAdjustment(0); nodeOperatorFeeClaimedReport = _stakingVault().latestReport(); _stakingVault().withdraw(_recipient, fee); } /** * @notice Increases accrued rewards adjustment to correct fee calculation due to non-rewards ether on CL * Note that the authorized role is NODE_OPERATOR_FEE_CLAIM_ROLE, not NODE_OPERATOR_MANAGER_ROLE, * although NODE_OPERATOR_MANAGER_ROLE is the admin role for NODE_OPERATOR_FEE_CLAIM_ROLE. * @param _adjustmentIncrease amount to increase adjustment by * @dev will revert if final adjustment is more than `MANUAL_ACCRUED_REWARDS_ADJUSTMENT_LIMIT` */ function increaseAccruedRewardsAdjustment( uint256 _adjustmentIncrease ) external onlyRole(NODE_OPERATOR_REWARDS_ADJUST_ROLE) { uint256 newAdjustment = accruedRewardsAdjustment + _adjustmentIncrease; // sanity check, though value will be cast safely during fee calculation if (newAdjustment > MANUAL_ACCRUED_REWARDS_ADJUSTMENT_LIMIT) revert IncreasedOverLimit(); _setAccruedRewardsAdjustment(newAdjustment); } /** * @notice set `accruedRewardsAdjustment` to a new proposed value if `confirmingRoles()` agree * @param _newAdjustment ew adjustment amount * @param _currentAdjustment current adjustment value for invalidating old confirmations * @dev will revert if new adjustment is more than `MANUAL_ACCRUED_REWARDS_ADJUSTMENT_LIMIT` */ function setAccruedRewardsAdjustment( uint256 _newAdjustment, uint256 _currentAdjustment ) external onlyConfirmed(confirmingRoles()) { if (accruedRewardsAdjustment != _currentAdjustment) revert InvalidatedAdjustmentVote(accruedRewardsAdjustment, _currentAdjustment); if (_newAdjustment > MANUAL_ACCRUED_REWARDS_ADJUSTMENT_LIMIT) revert IncreasedOverLimit(); _setAccruedRewardsAdjustment(_newAdjustment); } function _setNodeOperatorFeeBP(uint256 _newNodeOperatorFeeBP) internal { if (_newNodeOperatorFeeBP > TOTAL_BASIS_POINTS) revert FeeValueExceed100Percent(); if (nodeOperatorUnclaimedFee() > 0) revert NodeOperatorFeeUnclaimed(); uint256 oldNodeOperatorFeeBP = nodeOperatorFeeBP; // If fee is changing from 0, update the claimed report to current to prevent retroactive fees if (oldNodeOperatorFeeBP == 0 && _newNodeOperatorFeeBP > 0) { nodeOperatorFeeClaimedReport = _stakingVault().latestReport(); } nodeOperatorFeeBP = _newNodeOperatorFeeBP; emit NodeOperatorFeeBPSet(msg.sender, oldNodeOperatorFeeBP, _newNodeOperatorFeeBP); } /** * @notice sets InOut adjustment for correct fee calculation * @param _newAdjustment new adjustment value */ function _setAccruedRewardsAdjustment(uint256 _newAdjustment) internal { uint256 oldAdjustment = accruedRewardsAdjustment; if (_newAdjustment == oldAdjustment) revert SameAdjustment(); accruedRewardsAdjustment = _newAdjustment; emit AccruedRewardsAdjustmentSet(_newAdjustment, oldAdjustment); } // ==================== Events ==================== /** * @dev Emitted when the node operator fee is set. * @param oldNodeOperatorFeeBP The old node operator fee. * @param newNodeOperatorFeeBP The new node operator fee. */ event NodeOperatorFeeBPSet(address indexed sender, uint256 oldNodeOperatorFeeBP, uint256 newNodeOperatorFeeBP); /** * @dev Emitted when the new rewards adjustment is set. * @param newAdjustment the new adjustment value * @param oldAdjustment previous adjustment value */ event AccruedRewardsAdjustmentSet(uint256 newAdjustment, uint256 oldAdjustment); // ==================== Errors ==================== /** * @dev Error emitted when the node operator fee is unclaimed. */ error NodeOperatorFeeUnclaimed(); /** * @dev Error emitted when the fee is 0. */ error NoUnclaimedFee(); /** * @dev Error emitted when the combined feeBPs exceed 100%. */ error FeeValueExceed100Percent(); /** * @dev Error emitted when the increased adjustment exceeds the `MANUAL_ACCRUED_REWARDS_ADJUSTMENT_LIMIT`. */ error IncreasedOverLimit(); /** * @dev Error emitted when the adjustment setting vote is not valid due to changed state */ error InvalidatedAdjustmentVote(uint256 currentAdjustment, uint256 currentAtPropositionAdjustment); /** * @dev Error emitted when trying to set same value for adjustment */ error SameAdjustment(); }
// SPDX-License-Identifier: GPL-3.0 // SPDX-FileCopyrightText: 2025 Lido <[email protected]> // See contracts/COMPILERS.md pragma solidity 0.8.25; import {Clones} from "@openzeppelin/contracts-v5.2/proxy/Clones.sol"; import {AccessControlConfirmable} from "contracts/0.8.25/utils/AccessControlConfirmable.sol"; import {OwnableUpgradeable} from "contracts/openzeppelin/5.2/upgradeable/access/OwnableUpgradeable.sol"; import {IStakingVault} from "../interfaces/IStakingVault.sol"; import {IPredepositGuarantee} from "../interfaces/IPredepositGuarantee.sol"; import {VaultHub} from "../VaultHub.sol"; import {OperatorGrid} from "../OperatorGrid.sol"; /** * @title Permissions * @author Lido * @notice Provides granular permissions for StakingVault operations. */ abstract contract Permissions is AccessControlConfirmable { /** * @notice Struct containing an account and a role for granting/revoking roles. */ struct RoleAssignment { address account; bytes32 role; } /** * @notice Permission for funding the StakingVault. */ bytes32 public constant FUND_ROLE = keccak256("vaults.Permissions.Fund"); /** * @notice Permission for withdrawing funds from the StakingVault. */ bytes32 public constant WITHDRAW_ROLE = keccak256("vaults.Permissions.Withdraw"); /** * @notice Permission for locking ether on StakingVault. */ bytes32 public constant LOCK_ROLE = keccak256("vaults.Permissions.Lock"); /** * @notice Permission for minting stETH shares backed by the StakingVault. */ bytes32 public constant MINT_ROLE = keccak256("vaults.Permissions.Mint"); /** * @notice Permission for burning stETH shares backed by the StakingVault. */ bytes32 public constant BURN_ROLE = keccak256("vaults.Permissions.Burn"); /** * @notice Permission for rebalancing the StakingVault. */ bytes32 public constant REBALANCE_ROLE = keccak256("vaults.Permissions.Rebalance"); /** * @notice Permission for pausing beacon chain deposits on the StakingVault. */ bytes32 public constant PAUSE_BEACON_CHAIN_DEPOSITS_ROLE = keccak256("vaults.Permissions.PauseDeposits"); /** * @notice Permission for resuming beacon chain deposits on the StakingVault. */ bytes32 public constant RESUME_BEACON_CHAIN_DEPOSITS_ROLE = keccak256("vaults.Permissions.ResumeDeposits"); /** * @notice Permission for requesting validator exit from the StakingVault. */ bytes32 public constant REQUEST_VALIDATOR_EXIT_ROLE = keccak256("vaults.Permissions.RequestValidatorExit"); /** * @notice Permission for triggering validator withdrawal from the StakingVault using EIP-7002 triggerable exit. */ bytes32 public constant TRIGGER_VALIDATOR_WITHDRAWAL_ROLE = keccak256("vaults.Permissions.TriggerValidatorWithdrawal"); /** * @notice Permission for voluntary disconnecting the StakingVault. */ bytes32 public constant VOLUNTARY_DISCONNECT_ROLE = keccak256("vaults.Permissions.VoluntaryDisconnect"); /** * @notice Permission for getting compensation for disproven validator predeposit from PDG */ bytes32 public constant PDG_COMPENSATE_PREDEPOSIT_ROLE = keccak256("vaults.Permissions.PDGCompensatePredeposit"); /** * @notice Permission for proving valid vault validators unknown to the PDG */ bytes32 public constant PDG_PROVE_VALIDATOR_ROLE = keccak256("vaults.Permissions.PDGProveValidator"); /** * @notice Permission for unguarnateed deposit to trusted validators */ bytes32 public constant UNGUARANTEED_BEACON_CHAIN_DEPOSIT_ROLE = keccak256("vaults.Permissions.UnguaranteedBeaconChainDeposit"); /** * @dev Permission for deauthorizing Lido VaultHub from the StakingVault. */ bytes32 public constant LIDO_VAULTHUB_DEAUTHORIZATION_ROLE = keccak256("vaults.Permissions.LidoVaultHubDeauthorization"); /** * @dev Permission for granting authorization to Lido VaultHub on the StakingVault. */ bytes32 public constant LIDO_VAULTHUB_AUTHORIZATION_ROLE = keccak256("vaults.Permissions.LidoVaultHubAuthorization"); /** * @dev Permission for ossifying the StakingVault. */ bytes32 public constant OSSIFY_ROLE = keccak256("vaults.Permissions.Ossify"); /** * @dev Permission for setting depositor on the StakingVault. */ bytes32 public constant SET_DEPOSITOR_ROLE = keccak256("vaults.Permissions.SetDepositor"); /** * @dev Permission for resetting locked amount on the disconnected StakingVault. */ bytes32 public constant RESET_LOCKED_ROLE = keccak256("vaults.Permissions.ResetLocked"); /** * @dev Permission for requesting change of tier on the OperatorGrid. */ bytes32 public constant REQUEST_TIER_CHANGE_ROLE = keccak256("vaults.Permissions.RequestTierChange"); /** * @notice Address of the implementation contract * @dev Used to prevent initialization in the implementation */ address private immutable _SELF; VaultHub public immutable VAULT_HUB; /** * @notice Indicates whether the contract has been initialized */ bool public initialized; /** * @notice Constructor sets the address of the implementation contract. */ constructor(address _vaultHub) { if (_vaultHub == address(0)) revert ZeroArgument("_vaultHub"); _SELF = address(this); VAULT_HUB = VaultHub(_vaultHub); } /** * @notice Modifier to prevent reinitialization of the contract. * @dev Extracted to modifier to avoid Slither warning. */ modifier initializer() { if (initialized) revert AlreadyInitialized(); if (address(this) == _SELF) revert NonProxyCallsForbidden(); initialized = true; _; emit Initialized(); } /** * @dev Sets the ACL default admin and confirmation expiry time. * @param _defaultAdmin The address of the default admin * @param _confirmExpiry The confirmation expiry time in seconds */ function _initialize(address _defaultAdmin, uint256 _confirmExpiry) internal initializer { if (_defaultAdmin == address(0)) revert ZeroArgument("_defaultAdmin"); _grantRole(DEFAULT_ADMIN_ROLE, _defaultAdmin); _setConfirmExpiry(_confirmExpiry); } /** * @notice Returns the address of the underlying StakingVault. * @return The address of the StakingVault. */ function stakingVault() external view returns (IStakingVault) { return _stakingVault(); } // ==================== Role Management Functions ==================== /** * @notice Mass-grants multiple roles to multiple accounts. * @param _assignments An array of role assignments. * @dev Performs the role admin checks internally. * @dev If an account is already a member of a role, doesn't revert, emits no events. */ function grantRoles(RoleAssignment[] calldata _assignments) external { if (_assignments.length == 0) revert ZeroArgument("_assignments"); for (uint256 i = 0; i < _assignments.length; i++) { grantRole(_assignments[i].role, _assignments[i].account); } } /** * @notice Mass-revokes multiple roles from multiple accounts. * @param _assignments An array of role assignments. * @dev Performs the role admin checks internally. * @dev If an account is not a member of a role, doesn't revert, emits no events. */ function revokeRoles(RoleAssignment[] calldata _assignments) external { if (_assignments.length == 0) revert ZeroArgument("_assignments"); for (uint256 i = 0; i < _assignments.length; i++) { revokeRole(_assignments[i].role, _assignments[i].account); } } /** * @dev Returns an array of roles that need to confirm the call * used for the `onlyConfirmed` modifier. * At this level, only the DEFAULT_ADMIN_ROLE is needed to confirm the call * but in inherited contracts, the function can be overridden to add more roles, * which are introduced further in the inheritance chain. * @return The roles that need to confirm the call. */ function confirmingRoles() public pure virtual returns (bytes32[] memory); /** * @dev Checks the FUND_ROLE and funds the StakingVault. * @param _ether The amount of ether to fund the StakingVault with. */ function _fund(uint256 _ether) internal onlyRole(FUND_ROLE) { _stakingVault().fund{value: _ether}(); } /** * @dev Checks the WITHDRAW_ROLE and withdraws funds from the StakingVault. * @param _recipient The address to withdraw the funds to. * @param _ether The amount of ether to withdraw from the StakingVault. * @dev The zero checks for recipient and ether are performed in the StakingVault contract. */ function _withdraw(address _recipient, uint256 _ether) internal virtual onlyRole(WITHDRAW_ROLE) { _stakingVault().withdraw(_recipient, _ether); } /** * @dev Checks the LOCK_ROLE and increases the locked amount on the StakingVault. * @param _locked The amount of locked ether, must be greater or equal to the current locked amount. */ function _lock(uint256 _locked) internal onlyRole(LOCK_ROLE) { _stakingVault().lock(_locked); } /** * @dev Checks the MINT_ROLE and mints shares backed by the StakingVault. * @param _recipient The address to mint the shares to. * @param _shares The amount of shares to mint. * @dev The zero checks for parameters are performed in the VaultHub contract. */ function _mintShares(address _recipient, uint256 _shares) internal onlyRole(MINT_ROLE) { VAULT_HUB.mintShares(address(_stakingVault()), _recipient, _shares); } /** * @dev Checks the BURN_ROLE and burns shares backed by the StakingVault. * @param _shares The amount of shares to burn. * @dev The zero check for parameters is performed in the VaultHub contract. */ function _burnShares(uint256 _shares) internal onlyRole(BURN_ROLE) { VAULT_HUB.burnShares(address(_stakingVault()), _shares); } /** * @dev Checks the REBALANCE_ROLE and rebalances the StakingVault. * @param _ether The amount of ether to rebalance the StakingVault with. * @dev The zero check for parameters is performed in the StakingVault contract. */ function _rebalanceVault(uint256 _ether) internal onlyRole(REBALANCE_ROLE) { _stakingVault().rebalance(_ether); } /** * @dev Checks the PAUSE_BEACON_CHAIN_DEPOSITS_ROLE and pauses beacon chain deposits on the StakingVault. */ function _pauseBeaconChainDeposits() internal onlyRole(PAUSE_BEACON_CHAIN_DEPOSITS_ROLE) { _stakingVault().pauseBeaconChainDeposits(); } /** * @dev Checks the RESUME_BEACON_CHAIN_DEPOSITS_ROLE and resumes beacon chain deposits on the StakingVault. */ function _resumeBeaconChainDeposits() internal onlyRole(RESUME_BEACON_CHAIN_DEPOSITS_ROLE) { _stakingVault().resumeBeaconChainDeposits(); } /** * @dev Checks the REQUEST_VALIDATOR_EXIT_ROLE and requests validator exit on the StakingVault. * @dev The zero check for _pubkeys is performed in the StakingVault contract. */ function _requestValidatorExit(bytes calldata _pubkeys) internal onlyRole(REQUEST_VALIDATOR_EXIT_ROLE) { _stakingVault().requestValidatorExit(_pubkeys); } /** * @dev Checks the TRIGGER_VALIDATOR_WITHDRAWAL_ROLE and triggers validator withdrawal on the StakingVault using EIP-7002 triggerable exit. * @dev The zero checks for parameters are performed in the StakingVault contract. */ function _triggerValidatorWithdrawal( bytes calldata _pubkeys, uint64[] calldata _amounts, address _refundRecipient ) internal onlyRole(TRIGGER_VALIDATOR_WITHDRAWAL_ROLE) { _stakingVault().triggerValidatorWithdrawal{value: msg.value}(_pubkeys, _amounts, _refundRecipient); } /** * @dev Checks the VOLUNTARY_DISCONNECT_ROLE and voluntarily disconnects the StakingVault. */ function _voluntaryDisconnect() internal onlyRole(VOLUNTARY_DISCONNECT_ROLE) { VAULT_HUB.voluntaryDisconnect(address(_stakingVault())); } /** * @dev Checks the PDG_COMPENSATE_PREDEPOSIT_ROLE and claims disproven predeposit from PDG. * @param _pubkey The pubkey of the validator. * @param _recipient The address to compensate the disproven validator predeposit to. * @return The amount of ether compensated. */ function _compensateDisprovenPredepositFromPDG( bytes calldata _pubkey, address _recipient ) internal onlyRole(PDG_COMPENSATE_PREDEPOSIT_ROLE) returns (uint256) { return IPredepositGuarantee(_stakingVault().depositor()).compensateDisprovenPredeposit(_pubkey, _recipient); } /** * @dev Proves validators unknown to PDG that have correct vault WC */ function _proveUnknownValidatorsToPDG( IPredepositGuarantee.ValidatorWitness[] calldata _witnesses ) internal onlyRole(PDG_PROVE_VALIDATOR_ROLE) { IStakingVault vault = _stakingVault(); IPredepositGuarantee pdg = IPredepositGuarantee(vault.depositor()); for (uint256 i = 0; i < _witnesses.length; i++) { pdg.proveUnknownValidator(_witnesses[i], vault); } } /** * @dev Withdraws ether from vault to this contract for unguaranteed deposit to validators */ function _withdrawForUnguaranteedDepositToBeaconChain( uint256 _ether ) internal onlyRole(UNGUARANTEED_BEACON_CHAIN_DEPOSIT_ROLE) { _stakingVault().withdraw(address(this), _ether); } /** * @dev Checks the confirming roles and transfers the StakingVault ownership. * @param _newOwner The address to transfer the StakingVault ownership to. */ function _transferStakingVaultOwnership(address _newOwner) internal onlyConfirmed(confirmingRoles()) { OwnableUpgradeable(address(_stakingVault())).transferOwnership(_newOwner); } /** * @dev Checks the LIDO_VAULTHUB_AUTHORIZATION_ROLE and authorizes Lido VaultHub on the StakingVault. */ function _authorizeLidoVaultHub() internal onlyRole(LIDO_VAULTHUB_AUTHORIZATION_ROLE) { _stakingVault().authorizeLidoVaultHub(); } /** * @dev Checks the LIDO_VAULTHUB_DEAUTHORIZATION_ROLE and deauthorizes Lido VaultHub from the StakingVault. */ function _deauthorizeLidoVaultHub() internal onlyRole(LIDO_VAULTHUB_DEAUTHORIZATION_ROLE) { _stakingVault().deauthorizeLidoVaultHub(); } /** * @dev Checks the OSSIFY_ROLE and ossifies the StakingVault. */ function _ossifyStakingVault() internal onlyRole(OSSIFY_ROLE) { _stakingVault().ossifyStakingVault(); } /** * @dev Checks the SET_DEPOSITOR_ROLE and sets the depositor on the StakingVault. * @param _depositor The address to set the depositor to. */ function _setDepositor(address _depositor) internal onlyRole(SET_DEPOSITOR_ROLE) { _stakingVault().setDepositor(_depositor); } /** * @dev Checks the RESET_LOCKED_ROLE and resets the locked amount on the disconnected StakingVault. */ function _resetLocked() internal onlyRole(RESET_LOCKED_ROLE) { _stakingVault().resetLocked(); } /** * @dev Checks the REQUEST_TIER_CHANGE_ROLE and requests a change of the tier on the OperatorGrid. * @param _tierId The tier to change to. */ function _requestTierChange(uint256 _tierId) internal onlyRole(REQUEST_TIER_CHANGE_ROLE) { OperatorGrid(VAULT_HUB.operatorGrid()).requestTierChange(address(_stakingVault()), _tierId); } /** * @dev Loads the address of the underlying StakingVault. * @return addr The address of the StakingVault. */ function _stakingVault() internal view returns (IStakingVault) { bytes memory args = Clones.fetchCloneArgs(address(this)); address stakingVaultAddress; assembly { stakingVaultAddress := mload(add(args, 32)) } return IStakingVault(stakingVaultAddress); } /** * @notice Emitted when the contract is initialized */ event Initialized(); /** * @notice Error when direct calls to the implementation are forbidden */ error NonProxyCallsForbidden(); /** * @notice Error when the contract is already initialized. */ error AlreadyInitialized(); /** * @notice Error thrown for when a given value cannot be zero * @param argument Name of the argument */ error ZeroArgument(string argument); }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md pragma solidity 0.8.25; import {IStakingVault} from "./IStakingVault.sol"; /** * @title IPredepositGuarantee * @author Lido * @notice Interface for the `PredepositGuarantee` contract */ interface IPredepositGuarantee { /** * @notice user input for validator proof verification * @custom:proof array of merkle proofs from parent(pubkey,wc) node to Beacon block root * @custom:pubkey of validator to prove * @custom:validatorIndex of validator in CL state tree * @custom:childBlockTimestamp of EL block that has parent block beacon root in BEACON_ROOTS contract */ struct ValidatorWitness { bytes32[] proof; bytes pubkey; uint256 validatorIndex; uint64 childBlockTimestamp; } function compensateDisprovenPredeposit( bytes calldata _validatorPubkey, address _recipient ) external returns (uint256 compensatedEther); function proveUnknownValidator(ValidatorWitness calldata _witness, IStakingVault _stakingVault) external; }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md pragma solidity 0.8.25; import {IDepositContract} from "contracts/0.8.25/interfaces/IDepositContract.sol"; /** * @notice validator deposit from the `StakingVault` to the beacon chain * @dev withdrawal credentials are provided by the vault * @custom:pubkey The validator's BLS public key (48 bytes) * @custom:signature BLS signature of the deposit data (96 bytes) * @custom:amount Amount of ETH to deposit in wei (must be a multiple of 1 ETH) * @custom:depositDataRoot The root hash of the deposit data per ETH beacon spec */ struct StakingVaultDeposit { bytes pubkey; bytes signature; uint256 amount; bytes32 depositDataRoot; } /** * @title IStakingVault * @author Lido * @notice Interface for the `StakingVault` contract */ interface IStakingVault { /** * @notice Latest reported totalValue and inOutDelta * @custom:totalValue Aggregated validator balances plus the balance of `StakingVault` * @custom:inOutDelta Net difference between ether funded and withdrawn from `StakingVault` */ struct Report { uint128 totalValue; int128 inOutDelta; uint64 timestamp; } function DEPOSIT_CONTRACT() external view returns (IDepositContract); function initialize(address _owner, address _operator, address _depositor, bytes calldata _params) external; function version() external pure returns (uint64); function owner() external view returns (address); function getInitializedVersion() external view returns (uint64); function vaultHub() external view returns (address); function nodeOperator() external view returns (address); function depositor() external view returns (address); function locked() external view returns (uint256); function totalValue() external view returns (uint256); function unlocked() external view returns (uint256); function inOutDelta() external view returns (int256); function fund() external payable; function withdraw(address _recipient, uint256 _ether) external; function lock(uint256 _locked) external; function rebalance(uint256 _ether) external; function latestReport() external view returns (Report memory); function report(uint64 _timestamp, uint256 _totalValue, int256 _inOutDelta, uint256 _locked) external; function withdrawalCredentials() external view returns (bytes32); function beaconChainDepositsPaused() external view returns (bool); function pauseBeaconChainDeposits() external; function resumeBeaconChainDeposits() external; function depositToBeaconChain(StakingVaultDeposit[] calldata _deposits) external; function requestValidatorExit(bytes calldata _pubkeys) external; function calculateValidatorWithdrawalFee(uint256 _keysCount) external view returns (uint256); function triggerValidatorWithdrawal( bytes calldata _pubkeys, uint64[] calldata _amounts, address _refundRecipient ) external payable; function authorizeLidoVaultHub() external; function deauthorizeLidoVaultHub() external; function vaultHubAuthorized() external view returns (bool); function ossifyStakingVault() external; function ossified() external view returns (bool); function setDepositor(address _depositor) external; function resetLocked() external; function isReportFresh() external view returns (bool); }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md pragma solidity 0.8.25; import {AccessControlEnumerableUpgradeable} from "contracts/openzeppelin/5.2/upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; import {EnumerableSet} from "@openzeppelin/contracts-v5.2/utils/structs/EnumerableSet.sol"; import {ILidoLocator} from "contracts/common/interfaces/ILidoLocator.sol"; import {IStakingVault} from "./interfaces/IStakingVault.sol"; import {VaultHub} from "./VaultHub.sol"; struct TierParams { uint256 shareLimit; uint256 reserveRatioBP; uint256 forcedRebalanceThresholdBP; uint256 treasuryFeeBP; } /** * @title OperatorGrid * @author Lido * @notice * OperatorGrid is a contract that manages mint parameters for vaults when they are connected to the VaultHub. * These parameters include: * - shareLimit: maximum amount of shares that can be minted * - reserveRatioBP: reserve ratio in basis points * - forcedRebalanceThresholdBP: forced rebalance threshold in basis points * - treasuryFeeBP: treasury fee in basis points * * These parameters are determined by the Tier in which the Vault is registered. * */ contract OperatorGrid is AccessControlEnumerableUpgradeable { /* Key concepts: 1. Default Registration: - All Vaults are initially has default tier (DEFAULT_TIER_ID = 0) - The default tier has no group DEFAULT_TIER_ID = 0 ┌──────────────────────┐ │ Tier 1 │ │ tierShareLimit = z │ │ Vault_1 ... Vault_m │ └──────────────────────┘ 2. Tier Change Process: - To modify a vault's connection parameters to VaultHub, a tier change must be requested - Change requests must be approved by the target tier's Node Operator - All pending requests are tracked in the pendingRequests mapping Operator1.pendingRequests = [Vault_1, Vault_2, ...] 3. Confirmation Process: - Node Operator can confirm the tier change if: a) The target tier has sufficient capacity (shareLimit) b) Vault's node operator corresponds to the target tier group For detailed tier change scenarios and share accounting, see the ASCII diagrams in the `confirmTierChange` function. 4. Tier Capacity: - Tiers are not limited by the number of vaults - Tiers are limited by the sum of vaults' minted shares ┌──────────────────────────────────────────────────────┐ │ Group 1 = operator 1 │ │ ┌────────────────────────────────────────────────┐ │ │ │ groupShareLimit = 1kk │ │ │ └────────────────────────────────────────────────┘ │ │ ┌──────────────────────┐ ┌──────────────────────┐ │ │ │ Tier 1 │ │ Tier 2 │ │ │ │ tierShareLimit = x │ │ tierShareLimit = y │ │ │ │ Vault_2 ... Vault_k │ │ │ │ │ └──────────────────────┘ └──────────────────────┘ │ └──────────────────────────────────────────────────────┘ */ using EnumerableSet for EnumerableSet.AddressSet; bytes32 public constant REGISTRY_ROLE = keccak256("vaults.OperatorsGrid.Registry"); /// @notice Lido Locator contract ILidoLocator public immutable LIDO_LOCATOR; /// @notice Default group address uint256 public constant DEFAULT_TIER_ID = 0; address public constant DEFAULT_TIER_OPERATOR = address(uint160(type(uint160).max)); /// @dev basis points base uint256 internal constant TOTAL_BASIS_POINTS = 100_00; // ----------------------------- // STRUCTS // ----------------------------- struct Group { address operator; uint96 shareLimit; uint96 liabilityShares; uint128[] tierIds; } struct Tier { address operator; uint96 shareLimit; uint96 liabilityShares; uint16 reserveRatioBP; uint16 forcedRebalanceThresholdBP; uint16 treasuryFeeBP; } struct VaultTier { uint128 currentTierId; uint128 requestedTierId; } /** * @notice ERC-7201 storage namespace for the OperatorGrid * @dev ERC-7201 namespace is used to prevent upgrade collisions * @custom:storage-location erc7201:Lido.Vaults.OperatorGrid * @custom:tiers Tiers * @custom:vaultTier Vault tiers * @custom:groups Groups * @custom:pendingRequests Pending requests * @custom:nodeOperators Node operators */ struct ERC7201Storage { Tier[] tiers; mapping(address vault => VaultTier) vaultTier; mapping(address nodeOperator => Group) groups; mapping(address nodeOperator => EnumerableSet.AddressSet) pendingRequests; address[] nodeOperators; } /** * @notice Storage offset slot for ERC-7201 namespace * The storage namespace is used to prevent upgrade collisions * keccak256(abi.encode(uint256(keccak256("Lido.Vaults.OperatorGrid")) - 1)) & ~bytes32(uint256(0xff)) */ bytes32 private constant ERC7201_STORAGE_LOCATION = 0x6b64617c951381e2c1eff2be939fe368ab6d76b7d335df2e47ba2309eba1c700; /// @notice Initializes the contract with a LidoLocator /// @param _locator LidoLocator contract constructor(ILidoLocator _locator) { LIDO_LOCATOR = _locator; _disableInitializers(); } /// @notice Initializes the contract with an admin /// @param _admin Address of the admin /// @param _defaultTierParams Default tier params for the default tier function initialize(address _admin, TierParams calldata _defaultTierParams) external initializer { if (_admin == address(0)) revert ZeroArgument("_admin"); __AccessControlEnumerable_init(); _grantRole(DEFAULT_ADMIN_ROLE, _admin); ERC7201Storage storage $ = _getStorage(); //create default tier with default share limit $.tiers.push( Tier({ operator: DEFAULT_TIER_OPERATOR, shareLimit: uint96(_defaultTierParams.shareLimit), reserveRatioBP: uint16(_defaultTierParams.reserveRatioBP), forcedRebalanceThresholdBP: uint16(_defaultTierParams.forcedRebalanceThresholdBP), treasuryFeeBP: uint16(_defaultTierParams.treasuryFeeBP), liabilityShares: 0 }) ); } /// @notice Registers a new group /// @param _nodeOperator address of the node operator /// @param _shareLimit Maximum share limit for the group function registerGroup(address _nodeOperator, uint256 _shareLimit) external onlyRole(REGISTRY_ROLE) { if (_nodeOperator == address(0)) revert ZeroArgument("_nodeOperator"); ERC7201Storage storage $ = _getStorage(); if ($.groups[_nodeOperator].operator != address(0)) revert GroupExists(); $.groups[_nodeOperator] = Group({ operator: _nodeOperator, shareLimit: uint96(_shareLimit), liabilityShares: 0, tierIds: new uint128[](0) }); $.nodeOperators.push(_nodeOperator); emit GroupAdded(_nodeOperator, uint96(_shareLimit)); } /// @notice Updates the share limit of a group /// @param _nodeOperator address of the node operator /// @param _shareLimit New share limit value function updateGroupShareLimit(address _nodeOperator, uint256 _shareLimit) external onlyRole(REGISTRY_ROLE) { if (_nodeOperator == address(0)) revert ZeroArgument("_nodeOperator"); ERC7201Storage storage $ = _getStorage(); Group storage group_ = $.groups[_nodeOperator]; if (group_.operator == address(0)) revert GroupNotExists(); group_.shareLimit = uint96(_shareLimit); emit GroupShareLimitUpdated(_nodeOperator, uint96(_shareLimit)); } /// @notice Returns a group by node operator address /// @param _nodeOperator address of the node operator /// @return Group function group(address _nodeOperator) external view returns (Group memory) { return _getStorage().groups[_nodeOperator]; } /// @notice Returns a node operator address by index /// @param _index index of the node operator /// @return Node operator address function nodeOperatorAddress(uint256 _index) external view returns (address) { ERC7201Storage storage $ = _getStorage(); if (_index >= $.nodeOperators.length) revert NodeOperatorNotExists(); return $.nodeOperators[_index]; } /// @notice Returns a node operator count /// @return Node operator count function nodeOperatorCount() external view returns (uint256) { return _getStorage().nodeOperators.length; } /// @notice Registers a new tier /// @param _nodeOperator address of the node operator /// @param _tiers array of tiers to register function registerTiers( address _nodeOperator, TierParams[] calldata _tiers ) external onlyRole(REGISTRY_ROLE) { if (_nodeOperator == address(0)) revert ZeroArgument("_nodeOperator"); ERC7201Storage storage $ = _getStorage(); Group storage group_ = $.groups[_nodeOperator]; if (group_.operator == address(0)) revert GroupNotExists(); uint128 tierId = uint128($.tiers.length); uint256 length = _tiers.length; for (uint256 i = 0; i < length; i++) { _validateParams(tierId, _tiers[i].reserveRatioBP, _tiers[i].forcedRebalanceThresholdBP, _tiers[i].treasuryFeeBP); Tier memory tier_ = Tier({ operator: _nodeOperator, shareLimit: uint96(_tiers[i].shareLimit), reserveRatioBP: uint16(_tiers[i].reserveRatioBP), forcedRebalanceThresholdBP: uint16(_tiers[i].forcedRebalanceThresholdBP), treasuryFeeBP: uint16(_tiers[i].treasuryFeeBP), liabilityShares: 0 }); $.tiers.push(tier_); group_.tierIds.push(tierId); emit TierAdded( _nodeOperator, tierId, uint96(_tiers[i].shareLimit), uint16(_tiers[i].reserveRatioBP), uint16(_tiers[i].forcedRebalanceThresholdBP), uint16(_tiers[i].treasuryFeeBP) ); tierId++; } } /// @notice Returns a tier by ID /// @param _tierId id of the tier /// @return Tier function tier(uint256 _tierId) external view returns (Tier memory) { ERC7201Storage storage $ = _getStorage(); if (_tierId >= $.tiers.length) revert TierNotExists(); return $.tiers[_tierId]; } /// @notice Alters a tier /// @dev We do not enforce to update old vaults with the new tier params, only new ones. /// @param _tierId id of the tier /// @param _tierParams new tier params function alterTier(uint256 _tierId, TierParams calldata _tierParams) external onlyRole(REGISTRY_ROLE) { ERC7201Storage storage $ = _getStorage(); if (_tierId >= $.tiers.length) revert TierNotExists(); _validateParams(_tierId, _tierParams.reserveRatioBP, _tierParams.forcedRebalanceThresholdBP, _tierParams.treasuryFeeBP); Tier storage tier_ = $.tiers[_tierId]; tier_.shareLimit = uint96(_tierParams.shareLimit); tier_.reserveRatioBP = uint16(_tierParams.reserveRatioBP); tier_.forcedRebalanceThresholdBP = uint16(_tierParams.forcedRebalanceThresholdBP); tier_.treasuryFeeBP = uint16(_tierParams.treasuryFeeBP); emit TierUpdated(_tierId, tier_.shareLimit, tier_.reserveRatioBP, tier_.forcedRebalanceThresholdBP, tier_.treasuryFeeBP); } /// @notice Request to change tier /// @param _vault address of the vault /// @param _tierId id of the tier function requestTierChange(address _vault, uint256 _tierId) external { if (_vault == address(0)) revert ZeroArgument("_vault"); if (msg.sender != IStakingVault(_vault).owner()) revert NotAuthorized("requestTierChange", msg.sender); ERC7201Storage storage $ = _getStorage(); if (_tierId >= $.tiers.length) revert TierNotExists(); if (_tierId == DEFAULT_TIER_ID) revert CannotChangeToDefaultTier(); Tier memory requestedTier = $.tiers[_tierId]; address requestedTierOperator = requestedTier.operator; address nodeOperator = IStakingVault(_vault).nodeOperator(); if (nodeOperator != requestedTierOperator) revert TierNotInOperatorGroup(); uint128 tierId = uint128(_tierId); VaultTier storage vaultTier = $.vaultTier[_vault]; if (vaultTier.currentTierId == tierId) revert TierAlreadySet(); if (vaultTier.requestedTierId == tierId) revert TierAlreadyRequested(); vaultTier.requestedTierId = tierId; $.pendingRequests[nodeOperator].add(_vault); //returns true if the vault was not in the set emit TierChangeRequested(_vault, vaultTier.currentTierId, _tierId); } /// @notice Confirm tier change request /// @param _vault address of the vault /// @param _tierIdToConfirm id of the tier to confirm /// /* Legend: V = Vault1.liabilityShares Scheme1 - transfer Vault from default tier to Tier2 ┌────────────────────────────────┐ │ Group 1 │ │ │ ┌────────────────────┐ │ ┌───────────┐ ┌───────────┐ │ │ Tier 1 (default) │ confirm │ │ Tier 2 │ │ Tier 3 │ │ │ minted: -V │ ─────▶ │ │ minted:+V │ │ │ │ └────────────────────┘ │ └───────────┘ └───────────┘ │ │ │ │ Group1.liabilityShares: +V │ └────────────────────────────────┘ After confirmation: - Tier 1.liabilityShares = -V - Tier 2.liabilityShares = +V - Group1.liabilityShares = +V -------------------------------------------------------------------------- Scheme2 - transfer Vault from Tier2 to Tier3, no need to change group minted shares ┌────────────────────────────────┐ ┌────────────────────────────────┐ │ Group 1 │ │ Group 2 │ │ │ │ │ │ ┌───────────┐ ┌───────────┐ │ │ ┌───────────┐ │ │ │ Tier 2 │ │ Tier 3 │ │ │ │ Tier 4 │ │ │ │ minted:-V │ │ minted:+V │ │ │ │ │ │ │ └───────────┘ └───────────┘ │ │ └───────────┘ │ │ operator1 │ │ operator2 │ └────────────────────────────────┘ └────────────────────────────────┘ After confirmation: - Tier 2.liabilityShares = -V - Tier 3.liabilityShares = +V NB: Cannot change from Tier2 to Tier1, because Tier1 has no group. Reverts on `requestTierChange` NB: Cannot change from Tier2 to Tier4, because Tier4 has different operator. */ function confirmTierChange(address _vault, uint256 _tierIdToConfirm) external { if (_vault == address(0)) revert ZeroArgument("_vault"); address nodeOperator = IStakingVault(_vault).nodeOperator(); if (msg.sender != nodeOperator) revert NotAuthorized("confirmTierChange", msg.sender); if (_tierIdToConfirm == DEFAULT_TIER_ID) revert CannotChangeToDefaultTier(); ERC7201Storage storage $ = _getStorage(); VaultTier storage vaultTier = $.vaultTier[_vault]; uint128 requestedTierId = vaultTier.requestedTierId; if (requestedTierId != _tierIdToConfirm) revert InvalidTierId(requestedTierId, _tierIdToConfirm); Tier storage requestedTier = $.tiers[requestedTierId]; VaultHub vaultHub = VaultHub(LIDO_LOCATOR.vaultHub()); VaultHub.VaultSocket memory vaultSocket = vaultHub.vaultSocket(_vault); uint256 vaultLiabilityShares = vaultSocket.liabilityShares; //check if tier limit is exceeded if (requestedTier.liabilityShares + vaultLiabilityShares > requestedTier.shareLimit) revert TierLimitExceeded(); // if the vault was in the default tier: // - that mean that the vault has no group, so we decrease only the minted shares of the default tier // - but need to check requested group limit exceeded if (vaultTier.currentTierId == DEFAULT_TIER_ID) { Group storage requestedGroup = $.groups[nodeOperator]; if (requestedGroup.liabilityShares + vaultLiabilityShares > requestedGroup.shareLimit) revert GroupLimitExceeded(); requestedGroup.liabilityShares += uint96(vaultLiabilityShares); } Tier storage currentTier = $.tiers[vaultTier.currentTierId]; currentTier.liabilityShares -= uint96(vaultLiabilityShares); requestedTier.liabilityShares += uint96(vaultLiabilityShares); vaultTier.currentTierId = requestedTierId; vaultTier.requestedTierId = 0; $.pendingRequests[nodeOperator].remove(_vault); VaultHub(LIDO_LOCATOR.vaultHub()).updateConnection( _vault, requestedTier.shareLimit, requestedTier.reserveRatioBP, requestedTier.forcedRebalanceThresholdBP, requestedTier.treasuryFeeBP ); emit TierChanged(_vault, requestedTierId); } /// @notice Returns pending requests for a node operator /// @param _nodeOperator address of the node operator /// @return vault addresses function pendingRequests(address _nodeOperator) external view returns (address[] memory) { return _getStorage().pendingRequests[_nodeOperator].values(); } /// @notice Returns a pending request for a node operator /// @param _nodeOperator address of the node operator /// @param _index index of the pending request /// @return vault address function pendingRequest(address _nodeOperator, uint256 _index) external view returns (address) { return _getStorage().pendingRequests[_nodeOperator].at(_index); } /// @notice Returns a pending requests count for a node operator /// @param _nodeOperator address of the node operator /// @return pending requests count function pendingRequestsCount(address _nodeOperator) external view returns (uint256) { return _getStorage().pendingRequests[_nodeOperator].length(); } // ----------------------------- // MINT / BURN // ----------------------------- /// @notice Mint shares limit check /// @param vaultAddr address of the vault /// @param amount amount of shares will be minted function onMintedShares( address vaultAddr, uint256 amount ) external { if (msg.sender != LIDO_LOCATOR.vaultHub()) revert NotAuthorized("onMintedShares", msg.sender); ERC7201Storage storage $ = _getStorage(); VaultTier memory vaultTier = $.vaultTier[vaultAddr]; uint128 tierId = vaultTier.currentTierId; uint96 amount_ = uint96(amount); Tier storage tier_ = $.tiers[tierId]; uint96 tierLiabilityShares = tier_.liabilityShares; //cache if (tierLiabilityShares + amount_ > tier_.shareLimit) revert TierLimitExceeded(); tier_.liabilityShares = tierLiabilityShares + amount_; if (tierId != DEFAULT_TIER_ID) { Group storage group_ = $.groups[tier_.operator]; uint96 groupMintedShares = group_.liabilityShares; if (groupMintedShares + amount_ > group_.shareLimit) revert GroupLimitExceeded(); group_.liabilityShares = groupMintedShares + amount_; } } /// @notice Burn shares limit check /// @param vaultAddr address of the vault /// @param amount amount of shares to burn function onBurnedShares( address vaultAddr, uint256 amount ) external { if (msg.sender != LIDO_LOCATOR.vaultHub()) revert NotAuthorized("burnShares", msg.sender); ERC7201Storage storage $ = _getStorage(); VaultTier memory vaultTier = $.vaultTier[vaultAddr]; uint128 tierId = vaultTier.currentTierId; uint96 amount_ = uint96(amount); Tier storage tier_ = $.tiers[tierId]; // we skip the check for minted shared underflow, because it's done in the VaultHub.burnShares() tier_.liabilityShares -= amount_; if (tierId != DEFAULT_TIER_ID) { Group storage group_ = $.groups[tier_.operator]; group_.liabilityShares -= amount_; } } /// @notice Get vault limits /// @param vaultAddr address of the vault /// @return nodeOperator node operator of the vault /// @return tierId tier id of the vault /// @return shareLimit share limit of the vault /// @return reserveRatioBP reserve ratio of the vault /// @return forcedRebalanceThresholdBP forced rebalance threshold of the vault /// @return treasuryFeeBP treasury fee of the vault function vaultInfo(address vaultAddr) external view returns ( address nodeOperator, uint256 tierId, uint256 shareLimit, uint256 reserveRatioBP, uint256 forcedRebalanceThresholdBP, uint256 treasuryFeeBP ) { ERC7201Storage storage $ = _getStorage(); VaultTier memory vaultTier = $.vaultTier[vaultAddr]; tierId = vaultTier.currentTierId; Tier memory t = $.tiers[tierId]; nodeOperator = t.operator; shareLimit = t.shareLimit; reserveRatioBP = t.reserveRatioBP; forcedRebalanceThresholdBP = t.forcedRebalanceThresholdBP; treasuryFeeBP = t.treasuryFeeBP; } /// @notice Validates tier parameters /// @param _reserveRatioBP Reserve ratio /// @param _forcedRebalanceThresholdBP Forced rebalance threshold /// @param _treasuryFeeBP Treasury fee function _validateParams( uint256 _tierId, uint256 _reserveRatioBP, uint256 _forcedRebalanceThresholdBP, uint256 _treasuryFeeBP ) internal pure { if (_reserveRatioBP == 0) revert ZeroArgument("_reserveRatioBP"); if (_reserveRatioBP > TOTAL_BASIS_POINTS) revert ReserveRatioTooHigh(_tierId, _reserveRatioBP, TOTAL_BASIS_POINTS); if (_forcedRebalanceThresholdBP == 0) revert ZeroArgument("_forcedRebalanceThresholdBP"); if (_forcedRebalanceThresholdBP > _reserveRatioBP) revert ForcedRebalanceThresholdTooHigh(_tierId, _forcedRebalanceThresholdBP, _reserveRatioBP); if (_treasuryFeeBP > TOTAL_BASIS_POINTS) revert TreasuryFeeTooHigh(_tierId, _treasuryFeeBP, TOTAL_BASIS_POINTS); } function _getStorage() private pure returns (ERC7201Storage storage $) { assembly { $.slot := ERC7201_STORAGE_LOCATION } } // ----------------------------- // EVENTS // ----------------------------- event GroupAdded(address indexed nodeOperator, uint256 shareLimit); event GroupShareLimitUpdated(address indexed nodeOperator, uint256 shareLimit); event TierAdded(address indexed nodeOperator, uint256 indexed tierId, uint256 shareLimit, uint256 reserveRatioBP, uint256 forcedRebalanceThresholdBP, uint256 treasuryFee); event VaultAdded(address indexed vault); event TierChanged(address indexed vault, uint256 indexed tierId); event TierChangeRequested(address indexed vault, uint256 indexed currentTierId, uint256 indexed requestedTierId); event TierUpdated(uint256 indexed tierId, uint256 shareLimit, uint256 reserveRatioBP, uint256 forcedRebalanceThresholdBP, uint256 treasuryFee); // ----------------------------- // ERRORS // ----------------------------- error NotAuthorized(string operation, address sender); error ZeroArgument(string argument); error GroupExists(); error GroupNotExists(); error GroupLimitExceeded(); error GroupMintedSharesUnderflow(); error NodeOperatorNotExists(); error TierExists(); error TiersNotAvailable(); error TierLimitExceeded(); error TierMintedSharesUnderflow(); error TierNotExists(); error TierAlreadySet(); error TierAlreadyRequested(); error TierNotInOperatorGroup(); error InvalidTierId(uint256 requestedTierId, uint256 confirmedTierId); error CannotChangeToDefaultTier(); error ReserveRatioTooHigh(uint256 tierId, uint256 reserveRatioBP, uint256 maxReserveRatioBP); error ForcedRebalanceThresholdTooHigh(uint256 tierId, uint256 forcedRebalanceThresholdBP, uint256 reserveRatioBP); error TreasuryFeeTooHigh(uint256 tierId, uint256 treasuryFeeBP, uint256 maxTreasuryFeeBP); }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md pragma solidity 0.8.25; import {OwnableUpgradeable} from "contracts/openzeppelin/5.2/upgradeable/access/OwnableUpgradeable.sol"; import {MerkleProof} from "@openzeppelin/contracts-v5.2/utils/cryptography/MerkleProof.sol"; import {OperatorGrid} from "./OperatorGrid.sol"; import {IStakingVault} from "./interfaces/IStakingVault.sol"; import {ILidoLocator} from "contracts/common/interfaces/ILidoLocator.sol"; import {ILido} from "../interfaces/ILido.sol"; import {PausableUntilWithRoles} from "../utils/PausableUntilWithRoles.sol"; import {Math256} from "contracts/common/lib/Math256.sol"; /// @notice VaultHub is a contract that manages StakingVaults connected to the Lido protocol /// It allows to connect and disconnect vaults, mint and burn stETH using vaults as collateral /// Also, it passes the report from the accounting oracle to the vaults and charges fees /// @author folkyatina contract VaultHub is PausableUntilWithRoles { /// @custom:storage-location erc7201:VaultHub struct VaultHubStorage { /// @notice vault sockets with vaults connected to the hub /// @dev first socket is always zero. stone in the elevator VaultSocket[] sockets; /// @notice mapping from vault address to its socket /// @dev if vault is not connected to the hub, its index is zero mapping(address => uint256) vaultIndex; /// @notice allowed beacon addresses mapping(bytes32 => bool) vaultProxyCodehash; /// @notice root of the vaults data tree bytes32 vaultsDataTreeRoot; /// @notice CID of the vaults data tree string vaultsDataReportCid; /// @notice timestamp of the vaults data uint64 vaultsDataTimestamp; } struct VaultSocket { // ### 1st slot /// @notice vault address address vault; /// @notice total number of stETH shares that the vault owes to Lido uint96 liabilityShares; // ### 2nd slot /// @notice maximum number of stETH shares that can be minted by vault owner uint96 shareLimit; /// @notice share of ether that is locked on the vault as an additional reserve /// e.g RR=30% means that for 1stETH minted 1/(1-0.3)=1.428571428571428571 ETH is locked on the vault uint16 reserveRatioBP; /// @notice if vault's reserve decreases to this threshold, it should be force rebalanced uint16 forcedRebalanceThresholdBP; /// @notice treasury fee in basis points uint16 treasuryFeeBP; /// @notice if true, vault is disconnected and fee is not accrued bool pendingDisconnect; /// @notice cumulative amount of shares charged as fees for the vault uint96 feeSharesCharged; /// @notice unused gap in the slot 2 /// uint8 _unused_gap_; } struct VaultInfo { address vault; uint256 balance; int256 inOutDelta; bytes32 withdrawalCredentials; uint256 liabilityShares; } // keccak256(abi.encode(uint256(keccak256("VaultHub")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant VAULT_HUB_STORAGE_LOCATION = 0xb158a1a9015c52036ff69e7937a7bb424e82a8c4cbec5c5309994af06d825300; /// @notice role that allows to connect vaults to the hub bytes32 public constant VAULT_MASTER_ROLE = keccak256("Vaults.VaultHub.VaultMasterRole"); /// @notice role that allows to add factories and vault implementations to hub bytes32 public constant VAULT_REGISTRY_ROLE = keccak256("Vaults.VaultHub.VaultRegistryRole"); /// @dev basis points base uint256 internal constant TOTAL_BASIS_POINTS = 100_00; /// @notice length of the validator pubkey in bytes uint256 internal constant PUBLIC_KEY_LENGTH = 48; /// @notice amount of ETH that is locked on the vault on connect and can be withdrawn on disconnect only uint256 public constant CONNECT_DEPOSIT = 1 ether; /// @notice The time delta for report freshness check uint256 public constant REPORT_FRESHNESS_DELTA = 1 days; /// @notice limit for a single vault share limit relative to Lido TVL in basis points uint256 private immutable RELATIVE_SHARE_LIMIT_BP; /// @notice Lido stETH contract ILido public immutable LIDO; /// @notice Lido Locator contract ILidoLocator public immutable LIDO_LOCATOR; /// @param _locator Lido Locator contract /// @param _lido Lido stETH contract /// @param _relativeShareLimitBP Maximum share limit relative to TVL in basis points constructor( ILidoLocator _locator, ILido _lido, uint256 _relativeShareLimitBP ) { if (_relativeShareLimitBP == 0) revert ZeroArgument("_relativeShareLimitBP"); if (_relativeShareLimitBP > TOTAL_BASIS_POINTS) revert RelativeShareLimitBPTooHigh(_relativeShareLimitBP, TOTAL_BASIS_POINTS); LIDO_LOCATOR = _locator; LIDO = _lido; RELATIVE_SHARE_LIMIT_BP = _relativeShareLimitBP; _disableInitializers(); } function initialize(address _admin) external initializer { if (_admin == address(0)) revert ZeroArgument("_admin"); __VaultHub_init(_admin); } /// @param _admin admin address to manage the roles function __VaultHub_init(address _admin) internal onlyInitializing { __AccessControlEnumerable_init(); // the stone in the elevator _getVaultHubStorage().sockets.push(VaultSocket(address(0), 0, 0, 0, 0, 0, false, 0)); _grantRole(DEFAULT_ADMIN_ROLE, _admin); } function operatorGrid() external view returns (address) { return LIDO_LOCATOR.operatorGrid(); } /// @notice added vault proxy codehash to allowed list /// @param codehash vault proxy codehash function addVaultProxyCodehash(bytes32 codehash) public onlyRole(VAULT_REGISTRY_ROLE) { if (codehash == bytes32(0)) revert ZeroArgument("codehash"); VaultHubStorage storage $ = _getVaultHubStorage(); if ($.vaultProxyCodehash[codehash]) revert AlreadyExists(codehash); $.vaultProxyCodehash[codehash] = true; emit VaultProxyCodehashAdded(codehash); } /// @notice returns the number of vaults connected to the hub function vaultsCount() public view returns (uint256) { return _getVaultHubStorage().sockets.length - 1; } /// @param _index index of the vault /// @return vault address function vault(uint256 _index) public view returns (address) { return _getVaultHubStorage().sockets[_index + 1].vault; } /// @param _index index of the vault /// @return vault socket function vaultSocket(uint256 _index) external view returns (VaultSocket memory) { return _getVaultHubStorage().sockets[_index + 1]; } /// @param _vault vault address /// @return vault socket function vaultSocket(address _vault) external view returns (VaultSocket memory) { VaultHubStorage storage $ = _getVaultHubStorage(); return $.sockets[$.vaultIndex[_vault]]; } /// @notice returns batch of vaults info /// @param _offset offset of the vault in the batch (indexes start from 0) /// @param _limit limit of the batch /// @return batch of vaults info function batchVaultsInfo(uint256 _offset, uint256 _limit) external view returns (VaultInfo[] memory) { VaultHubStorage storage $ = _getVaultHubStorage(); uint256 limit = _offset + _limit > $.sockets.length - 1 ? $.sockets.length - 1 - _offset : _limit; VaultInfo[] memory batch = new VaultInfo[](limit); for (uint256 i = 0; i < limit; i++) { VaultSocket storage socket = $.sockets[i + 1 + _offset]; IStakingVault currentVault = IStakingVault(socket.vault); batch[i] = VaultInfo( address(currentVault), address(currentVault).balance, currentVault.inOutDelta(), currentVault.withdrawalCredentials(), socket.liabilityShares ); } return batch; } /// @notice checks if the vault is healthy by comparing its total value after applying rebalance threshold /// against current liability shares /// @param _vault vault address /// @return true if vault is healthy, false otherwise function isVaultHealthyAsOfLatestReport(address _vault) public view returns (bool) { VaultSocket storage socket = _connectedSocket(_vault); return _isVaultHealthyByThreshold( IStakingVault(_vault).totalValue(), socket.liabilityShares, socket.forcedRebalanceThresholdBP ); } function _isVaultHealthyByThreshold( uint256 _totalValue, uint256 _liabilityShares, uint256 _checkThreshold ) internal view returns (bool) { if (_liabilityShares == 0) return true; return ((_totalValue * (TOTAL_BASIS_POINTS - _checkThreshold)) / TOTAL_BASIS_POINTS) >= LIDO.getPooledEthBySharesRoundUp(_liabilityShares); } /// @notice estimate ether amount to make the vault healthy using rebalance /// @param _vault vault address /// @return amount to rebalance or UINT256_MAX if it's impossible to make the vault healthy using rebalance function rebalanceShortfall(address _vault) public view returns (uint256) { if (_vault == address(0)) revert ZeroArgument("_vault"); bool isHealthy = isVaultHealthyAsOfLatestReport(_vault); // Health vault do not need to rebalance if (isHealthy) { return 0; } VaultSocket storage socket = _connectedSocket(_vault); uint256 liabilityStETH = LIDO.getPooledEthBySharesRoundUp(socket.liabilityShares); uint256 reserveRatioBP = socket.reserveRatioBP; uint256 maxMintableRatio = (TOTAL_BASIS_POINTS - reserveRatioBP); uint256 totalValue = IStakingVault(_vault).totalValue(); // Impossible to rebalance a vault with deficit if (liabilityStETH >= totalValue) { // return MAX_UINT_256 return type(uint256).max; } // (liabilityStETH - X) / (vault.totalValue() - X) = maxMintableRatio / TOTAL_BASIS_POINTS // (liabilityStETH - X) * TOTAL_BASIS_POINTS = (vault.totalValue() - X) * maxMintableRatio // liabilityStETH * TOTAL_BASIS_POINTS - X * TOTAL_BASIS_POINTS = totalValue * maxMintableRatio - X * maxMintableRatio // X * maxMintableRatio - X * TOTAL_BASIS_POINTS = totalValue * maxMintableRatio - liabilityStETH * TOTAL_BASIS_POINTS // X * (maxMintableRatio - TOTAL_BASIS_POINTS) = vault.totalValue() * maxMintableRatio - liabilityStETH * TOTAL_BASIS_POINTS // X = (vault.totalValue() * maxMintableRatio - liabilityStETH * TOTAL_BASIS_POINTS) / (maxMintableRatio - TOTAL_BASIS_POINTS) // X = (liabilityStETH * TOTAL_BASIS_POINTS - vault.totalValue() * maxMintableRatio) / (TOTAL_BASIS_POINTS - maxMintableRatio) // reserveRatio = TOTAL_BASIS_POINTS - maxMintableRatio // X = (liabilityStETH * TOTAL_BASIS_POINTS - totalValue * maxMintableRatio) / reserveRatio return (liabilityStETH * TOTAL_BASIS_POINTS - totalValue * maxMintableRatio) / reserveRatioBP; } /// @notice connects a vault to the hub in permissionless way, get limits from the Operator Grid /// @param _vault vault address function connectVault(address _vault) external { ( /* address nodeOperator */, /* uint256 tierId */, uint256 shareLimit, uint256 reserveRatioBP, uint256 forcedRebalanceThresholdBP, uint256 treasuryFeeBP ) = OperatorGrid(LIDO_LOCATOR.operatorGrid()).vaultInfo(_vault); _connectVault(_vault, shareLimit, reserveRatioBP, forcedRebalanceThresholdBP, treasuryFeeBP); } /// @notice returns the latest report data /// @return timestamp of the report /// @return treeRoot of the report /// @return reportCid of the report function latestReportData() external view returns ( uint64 timestamp, bytes32 treeRoot, string memory reportCid ) { VaultHubStorage storage $ = _getVaultHubStorage(); return ( $.vaultsDataTimestamp, $.vaultsDataTreeRoot, $.vaultsDataReportCid ); } /// @notice connects a vault to the hub /// @param _vault vault address /// @param _shareLimit maximum number of stETH shares that can be minted by the vault /// @param _reserveRatioBP minimum reserve ratio in basis points /// @param _forcedRebalanceThresholdBP threshold to force rebalance on the vault in basis points /// @param _treasuryFeeBP treasury fee in basis points function _connectVault( address _vault, uint256 _shareLimit, uint256 _reserveRatioBP, uint256 _forcedRebalanceThresholdBP, uint256 _treasuryFeeBP ) internal { if (_reserveRatioBP == 0) revert ZeroArgument("_reserveRatioBP"); if (_reserveRatioBP > TOTAL_BASIS_POINTS) revert ReserveRatioTooHigh(_vault, _reserveRatioBP, TOTAL_BASIS_POINTS); if (_forcedRebalanceThresholdBP == 0) revert ZeroArgument("_forcedRebalanceThresholdBP"); if (_forcedRebalanceThresholdBP > _reserveRatioBP) revert ForcedRebalanceThresholdTooHigh(_vault, _forcedRebalanceThresholdBP, _reserveRatioBP); if (_treasuryFeeBP > TOTAL_BASIS_POINTS) revert TreasuryFeeTooHigh(_vault, _treasuryFeeBP, TOTAL_BASIS_POINTS); IStakingVault vault_ = IStakingVault(_vault); if (vault_.ossified()) revert VaultOssified(_vault); if (!vault_.vaultHubAuthorized()) revert VaultDeauthorized(_vault); _checkShareLimitUpperBound(_vault, _shareLimit); VaultHubStorage storage $ = _getVaultHubStorage(); if ($.vaultIndex[_vault] != 0) revert AlreadyConnected(_vault, $.vaultIndex[_vault]); bytes32 vaultProxyCodehash = address(_vault).codehash; if (!$.vaultProxyCodehash[vaultProxyCodehash]) revert VaultProxyNotAllowed(_vault); if (vault_.depositor() != LIDO_LOCATOR.predepositGuarantee()) revert VaultDepositorNotAllowed(vault_.depositor()); if (vault_.locked() < CONNECT_DEPOSIT) revert VaultInsufficientLocked(_vault, vault_.locked(), CONNECT_DEPOSIT); if (_vault.balance < CONNECT_DEPOSIT) revert VaultInsufficientBalance(_vault, _vault.balance, CONNECT_DEPOSIT); VaultSocket memory vsocket = VaultSocket( _vault, 0, // liabilityShares uint96(_shareLimit), uint16(_reserveRatioBP), uint16(_forcedRebalanceThresholdBP), uint16(_treasuryFeeBP), false, // pendingDisconnect 0 ); $.vaultIndex[_vault] = $.sockets.length; $.sockets.push(vsocket); // here we intentionally prohibit all reports having referenceSlot earlier than the current block; vault_.report(uint64(block.timestamp), _vault.balance, vault_.inOutDelta(), vault_.locked()); emit VaultConnectionSet(_vault, _shareLimit, _reserveRatioBP, _forcedRebalanceThresholdBP, _treasuryFeeBP); } /// @notice updates share limit for the vault /// Setting share limit to zero actually pause the vault's ability to mint /// and stops charging fees from the vault /// @param _vault vault address /// @param _shareLimit new share limit /// @dev msg.sender must have VAULT_MASTER_ROLE function updateShareLimit(address _vault, uint256 _shareLimit) external onlyRole(VAULT_MASTER_ROLE) { if (_vault == address(0)) revert ZeroArgument("_vault"); _checkShareLimitUpperBound(_vault, _shareLimit); VaultSocket storage socket = _connectedSocket(_vault); socket.shareLimit = uint96(_shareLimit); emit ShareLimitUpdated(_vault, _shareLimit); } /// @notice updates the vault's connection parameters /// @dev Reverts if the vault is not healthy as of latest report /// @param _vault vault address /// @param _shareLimit new share limit /// @param _reserveRatioBP new reserve ratio /// @param _forcedRebalanceThresholdBP new forced rebalance threshold /// @param _treasuryFeeBP new treasury fee function updateConnection( address _vault, uint256 _shareLimit, uint256 _reserveRatioBP, uint256 _forcedRebalanceThresholdBP, uint256 _treasuryFeeBP ) external { if (_vault == address(0)) revert ZeroArgument("_vault"); _checkShareLimitUpperBound(_vault, _shareLimit); if (msg.sender != LIDO_LOCATOR.operatorGrid()) revert NotAuthorized("updateConnection", msg.sender); VaultSocket storage socket = _connectedSocket(_vault); uint256 totalValue = IStakingVault(_vault).totalValue(); uint256 liabilityShares = socket.liabilityShares; // check healthy with new rebalance threshold if (!_isVaultHealthyByThreshold(totalValue, liabilityShares, _reserveRatioBP)) revert VaultMintingCapacityExceeded(_vault, totalValue, liabilityShares, _reserveRatioBP); socket.shareLimit = uint96(_shareLimit); socket.reserveRatioBP = uint16(_reserveRatioBP); socket.forcedRebalanceThresholdBP = uint16(_forcedRebalanceThresholdBP); socket.treasuryFeeBP = uint16(_treasuryFeeBP); emit VaultConnectionSet(_vault, _shareLimit, _reserveRatioBP, _forcedRebalanceThresholdBP, _treasuryFeeBP); } function updateReportData( uint64 _vaultsDataTimestamp, bytes32 _vaultsDataTreeRoot, string memory _vaultsDataReportCid ) external { if (msg.sender != LIDO_LOCATOR.accounting()) revert NotAuthorized("updateReportData", msg.sender); VaultHubStorage storage $ = _getVaultHubStorage(); $.vaultsDataTimestamp = _vaultsDataTimestamp; $.vaultsDataTreeRoot = _vaultsDataTreeRoot; $.vaultsDataReportCid = _vaultsDataReportCid; emit VaultsReportDataUpdated(_vaultsDataTimestamp, _vaultsDataTreeRoot, _vaultsDataReportCid); } /// @notice force disconnects a vault from the hub /// @param _vault vault address /// @dev msg.sender must have VAULT_MASTER_ROLE /// @dev vault's `liabilityShares` should be zero function disconnect(address _vault) external onlyRole(VAULT_MASTER_ROLE) { if (_vault == address(0)) revert ZeroArgument("_vault"); _disconnect(_vault); } /// @notice disconnects a vault from the hub /// @param _vault vault address /// @dev msg.sender should be vault's owner /// @dev vault's `liabilityShares` should be zero function voluntaryDisconnect(address _vault) external whenResumed { if (_vault == address(0)) revert ZeroArgument("_vault"); _vaultAuth(_vault, "disconnect"); _disconnect(_vault); } /// @notice mint StETH shares backed by vault external balance to the receiver address /// @param _vault vault address /// @param _recipient address of the receiver /// @param _amountOfShares amount of stETH shares to mint /// @dev msg.sender should be vault's owner function mintShares(address _vault, address _recipient, uint256 _amountOfShares) external whenResumed { if (_vault == address(0)) revert ZeroArgument("_vault"); if (_recipient == address(0)) revert ZeroArgument("_recipient"); if (_amountOfShares == 0) revert ZeroArgument("_amountOfShares"); _vaultAuth(_vault, "mint"); VaultSocket storage socket = _connectedSocket(_vault); uint256 vaultSharesAfterMint = socket.liabilityShares + _amountOfShares; uint256 shareLimit = socket.shareLimit; if (vaultSharesAfterMint > shareLimit) revert ShareLimitExceeded(_vault, shareLimit); IStakingVault vault_ = IStakingVault(_vault); if (!vault_.isReportFresh()) revert VaultReportStaled(_vault); uint256 maxMintableRatioBP = TOTAL_BASIS_POINTS - socket.reserveRatioBP; uint256 maxMintableEther = (vault_.totalValue() * maxMintableRatioBP) / TOTAL_BASIS_POINTS; uint256 stETHAfterMint = LIDO.getPooledEthBySharesRoundUp(vaultSharesAfterMint); if (stETHAfterMint > maxMintableEther) { revert InsufficientTotalValueToMint(_vault, vault_.totalValue()); } // Calculate the minimum ETH that needs to be locked in the vault to maintain the reserve ratio uint256 minLocked = (stETHAfterMint * TOTAL_BASIS_POINTS) / maxMintableRatioBP; if (minLocked > vault_.locked()) { revert VaultInsufficientLocked(_vault, vault_.locked(), minLocked); } socket.liabilityShares = uint96(vaultSharesAfterMint); LIDO.mintExternalShares(_recipient, _amountOfShares); OperatorGrid(LIDO_LOCATOR.operatorGrid()).onMintedShares(_vault, _amountOfShares); emit MintedSharesOnVault(_vault, _amountOfShares); } /// @notice burn steth shares from the balance of the VaultHub contract /// @param _vault vault address /// @param _amountOfShares amount of shares to burn /// @dev msg.sender should be vault's owner /// @dev VaultHub must have all the stETH on its balance function burnShares(address _vault, uint256 _amountOfShares) public whenResumed { if (_vault == address(0)) revert ZeroArgument("_vault"); if (_amountOfShares == 0) revert ZeroArgument("_amountOfShares"); _vaultAuth(_vault, "burn"); VaultSocket storage socket = _connectedSocket(_vault); uint256 liabilityShares = socket.liabilityShares; if (liabilityShares < _amountOfShares) revert InsufficientSharesToBurn(_vault, liabilityShares); socket.liabilityShares = uint96(liabilityShares - _amountOfShares); LIDO.burnExternalShares(_amountOfShares); OperatorGrid(LIDO_LOCATOR.operatorGrid()).onBurnedShares(_vault, _amountOfShares); emit BurnedSharesOnVault(_vault, _amountOfShares); } /// @notice separate burn function for EOA vault owners; requires vaultHub to be approved to transfer stETH /// @dev msg.sender should be vault's owner function transferAndBurnShares(address _vault, uint256 _amountOfShares) external { LIDO.transferSharesFrom(msg.sender, address(this), _amountOfShares); burnShares(_vault, _amountOfShares); } /// @notice permissionless rebalance for unhealthy vaults /// @param _vault vault address /// @dev rebalance all available amount of ether until the vault is healthy function forceRebalance(address _vault) external { if (_vault == address(0)) revert ZeroArgument("_vault"); uint256 maxAmountToRebalance = rebalanceShortfall(_vault); if (maxAmountToRebalance == 0) revert AlreadyHealthy(_vault); uint256 amountToRebalance = Math256.min(maxAmountToRebalance, _vault.balance); // TODO: add some gas compensation here IStakingVault(_vault).rebalance(amountToRebalance); } /// @notice rebalances the vault by writing off the amount of ether equal /// to `msg.value` from the vault's liability stETH /// @dev msg.sender should be vault's contract function rebalance() external payable whenResumed { if (msg.value == 0) revert ZeroArgument("msg.value"); VaultSocket storage socket = _connectedSocket(msg.sender); uint256 sharesToBurn = LIDO.getSharesByPooledEth(msg.value); uint256 liabilityShares = socket.liabilityShares; if (liabilityShares < sharesToBurn) revert InsufficientSharesToBurn(msg.sender, liabilityShares); socket.liabilityShares = uint96(liabilityShares - sharesToBurn); LIDO.rebalanceExternalEtherToInternal{value: msg.value}(); emit VaultRebalanced(msg.sender, sharesToBurn); } /// @notice Forces validator exit from the beacon chain when vault is unhealthy /// @param _vault The address of the vault to exit validators from /// @param _pubkeys The public keys of the validators to exit /// @param _refundRecipient The address that will receive the refund for transaction costs /// @dev When the vault becomes unhealthy, anyone can force its validators to exit the beacon chain /// This returns the vault's deposited ETH back to vault's balance and allows to rebalance the vault function forceValidatorExit(address _vault, bytes calldata _pubkeys, address _refundRecipient) external payable { if (msg.value == 0) revert ZeroArgument("msg.value"); if (_vault == address(0)) revert ZeroArgument("_vault"); if (_pubkeys.length == 0) revert ZeroArgument("_pubkeys"); if (_refundRecipient == address(0)) revert ZeroArgument("_refundRecipient"); if (_pubkeys.length % PUBLIC_KEY_LENGTH != 0) revert InvalidPubkeysLength(); if (isVaultHealthyAsOfLatestReport(_vault)) revert AlreadyHealthy(_vault); uint256 numValidators = _pubkeys.length / PUBLIC_KEY_LENGTH; uint64[] memory amounts = new uint64[](numValidators); IStakingVault(_vault).triggerValidatorWithdrawal{value: msg.value}(_pubkeys, amounts, _refundRecipient); emit ForcedValidatorExitTriggered(_vault, _pubkeys, _refundRecipient); } function _disconnect(address _vault) internal { VaultSocket storage socket = _connectedSocket(_vault); uint256 liabilityShares = socket.liabilityShares; if (liabilityShares > 0) { revert NoLiabilitySharesShouldBeLeft(_vault, liabilityShares); } socket.pendingDisconnect = true; emit VaultDisconnected(_vault); } /// @notice Permissionless update of the vault data /// @param _vault the address of the vault /// @param _totalValue the total value of the vault /// @param _inOutDelta the inOutDelta of the vault /// @param _feeSharesCharged the feeSharesCharged of the vault /// @param _liabilityShares the liabilityShares of the vault /// @param _proof the proof of the reported data function updateVaultData( address _vault, uint256 _totalValue, int256 _inOutDelta, uint256 _feeSharesCharged, uint256 _liabilityShares, bytes32[] calldata _proof ) external { VaultHubStorage storage $ = _getVaultHubStorage(); uint256 vaultIndex = $.vaultIndex[_vault]; if (vaultIndex == 0) revert NotConnectedToHub(_vault); bytes32 root = $.vaultsDataTreeRoot; bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(_vault, _totalValue, _inOutDelta, _feeSharesCharged, _liabilityShares)))); if (!MerkleProof.verify(_proof, root, leaf)) revert InvalidProof(); VaultSocket storage socket = $.sockets[vaultIndex]; // NB: charged fees can only cumulatively increase with time if (_feeSharesCharged < socket.feeSharesCharged) { revert InvalidFees(_vault, _feeSharesCharged, socket.feeSharesCharged); } socket.liabilityShares += uint96(_feeSharesCharged - socket.feeSharesCharged); socket.feeSharesCharged = uint96(_feeSharesCharged); uint256 newLiabilityShares = Math256.max(socket.liabilityShares, _liabilityShares); // locked ether can only be increased asynchronously once the oracle settled the new floor value // as of reference slot to prevent slashing upsides in between the report gathering and delivering uint256 lockedEther = Math256.max( LIDO.getPooledEthBySharesRoundUp(newLiabilityShares) * TOTAL_BASIS_POINTS / (TOTAL_BASIS_POINTS - socket.reserveRatioBP), socket.pendingDisconnect ? 0 : CONNECT_DEPOSIT ); IStakingVault(socket.vault).report($.vaultsDataTimestamp, _totalValue, _inOutDelta, lockedEther); uint256 length = $.sockets.length; if (socket.pendingDisconnect) { // remove disconnected vault from the list address vaultAddress = socket.vault; VaultSocket memory lastSocket = $.sockets[length - 1]; $.sockets[vaultIndex] = lastSocket; $.vaultIndex[lastSocket.vault] = vaultIndex; $.sockets.pop(); delete $.vaultIndex[vaultAddress]; } } function mintVaultsTreasuryFeeShares(uint256 _amountOfShares) external { if (msg.sender != LIDO_LOCATOR.accounting()) revert NotAuthorized("mintVaultsTreasuryFeeShares", msg.sender); LIDO.mintExternalShares(LIDO_LOCATOR.treasury(), _amountOfShares); } function _vaultAuth(address _vault, string memory _operation) internal view { if (msg.sender != OwnableUpgradeable(_vault).owner()) revert NotAuthorized(_operation, msg.sender); } function _connectedSocket(address _vault) internal view returns (VaultSocket storage) { VaultHubStorage storage $ = _getVaultHubStorage(); uint256 index = $.vaultIndex[_vault]; if (index == 0 || $.sockets[index].pendingDisconnect) revert NotConnectedToHub(_vault); return $.sockets[index]; } function _getVaultHubStorage() private pure returns (VaultHubStorage storage $) { assembly { $.slot := VAULT_HUB_STORAGE_LOCATION } } /// @dev check if the share limit is within the upper bound set by RELATIVE_SHARE_LIMIT_BP function _checkShareLimitUpperBound(address _vault, uint256 _shareLimit) internal view { uint256 relativeMaxShareLimitPerVault = (LIDO.getTotalShares() * RELATIVE_SHARE_LIMIT_BP) / TOTAL_BASIS_POINTS; if (_shareLimit > relativeMaxShareLimitPerVault) { revert ShareLimitTooHigh(_vault, _shareLimit, relativeMaxShareLimitPerVault); } } event VaultConnectionSet( address indexed vault, uint256 shareLimit, uint256 reserveRatioBP, uint256 forcedRebalanceThresholdBP, uint256 treasuryFeeBP ); event VaultsReportDataUpdated(uint64 indexed timestamp, bytes32 root, string cid); event ShareLimitUpdated(address indexed vault, uint256 newShareLimit); event VaultDisconnected(address indexed vault); event MintedSharesOnVault(address indexed vault, uint256 amountOfShares); event BurnedSharesOnVault(address indexed vault, uint256 amountOfShares); event VaultRebalanced(address indexed vault, uint256 sharesBurned); event VaultProxyCodehashAdded(bytes32 indexed codehash); event ForcedValidatorExitTriggered(address indexed vault, bytes pubkeys, address refundRecipient); error AlreadyHealthy(address vault); error VaultMintingCapacityExceeded(address vault, uint256 totalValue, uint256 liabilityShares, uint256 newRebalanceThresholdBP); error InsufficientSharesToBurn(address vault, uint256 amount); error ShareLimitExceeded(address vault, uint256 shareLimit); error AlreadyConnected(address vault, uint256 index); error NotConnectedToHub(address vault); error NotAuthorized(string operation, address addr); error ZeroArgument(string argument); error ShareLimitTooHigh(address vault, uint256 shareLimit, uint256 maxShareLimit); error ReserveRatioTooHigh(address vault, uint256 reserveRatioBP, uint256 maxReserveRatioBP); error ForcedRebalanceThresholdTooHigh(address vault, uint256 forcedRebalanceThresholdBP, uint256 maxForcedRebalanceThresholdBP); error TreasuryFeeTooHigh(address vault, uint256 treasuryFeeBP, uint256 maxTreasuryFeeBP); error InsufficientTotalValueToMint(address vault, uint256 totalValue); error AlreadyExists(bytes32 codehash); error NoLiabilitySharesShouldBeLeft(address vault, uint256 liabilityShares); error VaultProxyNotAllowed(address beacon); error InvalidPubkeysLength(); error RelativeShareLimitBPTooHigh(uint256 relativeShareLimitBP, uint256 totalBasisPoints); error VaultDepositorNotAllowed(address depositor); error InvalidProof(); error InvalidFees(address vault, uint256 newFees, uint256 oldFees); error VaultInsufficientLocked(address vault, uint256 currentLocked, uint256 expectedLocked); error VaultOssified(address vault); error VaultInsufficientBalance(address vault, uint256 currentBalance, uint256 expectedBalance); error VaultReportStaled(address vault); error VaultDeauthorized(address vault); }
// SPDX-FileCopyrightText: 2023 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md // solhint-disable-next-line lido/fixed-compiler-version pragma solidity >=0.4.24 <0.9.0; interface ILidoLocator { function accountingOracle() external view returns(address); function depositSecurityModule() external view returns(address); function elRewardsVault() external view returns(address); function lido() external view returns(address); function oracleReportSanityChecker() external view returns(address); function burner() external view returns(address); function stakingRouter() external view returns(address); function treasury() external view returns(address); function validatorsExitBusOracle() external view returns(address); function withdrawalQueue() external view returns(address); function withdrawalVault() external view returns(address); function postTokenRebaseReceiver() external view returns(address); function oracleDaemonConfig() external view returns(address); function accounting() external view returns (address); function predepositGuarantee() external view returns (address); function wstETH() external view returns (address); function vaultHub() external view returns (address); function operatorGrid() external view returns (address); /// @notice Returns core Lido protocol component addresses in a single call /// @dev This function provides a gas-efficient way to fetch multiple component addresses in a single call function coreComponents() external view returns( address elRewardsVault, address oracleReportSanityChecker, address stakingRouter, address treasury, address withdrawalQueue, address withdrawalVault ); /// @notice Returns addresses of components involved in processing oracle reports in the Lido contract /// @dev This function provides a gas-efficient way to fetch multiple component addresses in a single call function oracleReportComponents() external view returns( address accountingOracle, address oracleReportSanityChecker, address burner, address withdrawalQueue, address postTokenRebaseReceiver, address stakingRouter, address vaultHub ); }
// SPDX-FileCopyrightText: 2023 Lido <[email protected]> // SPDX-License-Identifier: MIT // Copied from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0457042d93d9dfd760dbaa06a4d2f1216fdbe297/contracts/utils/math/Math.sol // See contracts/COMPILERS.md // solhint-disable-next-line pragma solidity >=0.4.24 <0.9.0; library Math256 { /// @dev Returns the largest of two numbers. function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /// @dev Returns the smallest of two numbers. function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /// @dev Returns the largest of two numbers. function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /// @dev Returns the smallest of two numbers. function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /// @dev Returns the ceiling of the division of two numbers. /// /// This differs from standard division with `/` in that it rounds up instead /// of rounding down. function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /// @dev Returns absolute difference of two numbers. function absDiff(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a - b : b - a; } }
// SPDX-FileCopyrightText: 2023 Lido <[email protected]>, Aragon // SPDX-License-Identifier: MIT // solhint-disable-next-line lido/fixed-compiler-version pragma solidity ^0.8.9; library UnstructuredStorage { function getStorageBool(bytes32 position) internal view returns (bool data) { assembly { data := sload(position) } } function getStorageAddress(bytes32 position) internal view returns (address data) { assembly { data := sload(position) } } function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) { assembly { data := sload(position) } } function getStorageUint256(bytes32 position) internal view returns (uint256 data) { assembly { data := sload(position) } } function setStorageBool(bytes32 position, bool data) internal { assembly { sstore(position, data) } } function setStorageAddress(bytes32 position, address data) internal { assembly { sstore(position, data) } } function setStorageBytes32(bytes32 position, bytes32 data) internal { assembly { sstore(position, data) } } function setStorageUint256(bytes32 position, uint256 data) internal { assembly { sstore(position, data) } } }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // solhint-disable-next-line lido/fixed-compiler-version pragma solidity ^0.8.9; import {UnstructuredStorage} from "contracts/common/lib/UnstructuredStorage.sol"; /** * @title PausableUntil * @notice allows to pause the contract for a specific duration or indefinitely */ abstract contract PausableUntil { using UnstructuredStorage for bytes32; /// Contract resume/pause control storage slot bytes32 internal constant RESUME_SINCE_TIMESTAMP_POSITION = keccak256("lido.PausableUntil.resumeSinceTimestamp"); /// Special value for the infinite pause uint256 public constant PAUSE_INFINITELY = type(uint256).max; /// @notice Emitted when paused by the `pauseFor` or `pauseUntil` call event Paused(uint256 duration); /// @notice Emitted when resumed by the `resume` call event Resumed(); error ZeroPauseDuration(); error PausedExpected(); error ResumedExpected(); error PauseUntilMustBeInFuture(); /// @notice Reverts if paused modifier whenResumed() { _checkResumed(); _; } /// @notice Returns whether the contract is paused function isPaused() public view returns (bool) { return block.timestamp < RESUME_SINCE_TIMESTAMP_POSITION.getStorageUint256(); } /// @notice Returns one of: /// - PAUSE_INFINITELY if paused infinitely returns /// - the timestamp when the contract get resumed if paused for specific duration /// - some timestamp in past if not paused function getResumeSinceTimestamp() external view returns (uint256) { return RESUME_SINCE_TIMESTAMP_POSITION.getStorageUint256(); } function _checkPaused() internal view { if (!isPaused()) { revert PausedExpected(); } } function _checkResumed() internal view { if (isPaused()) { revert ResumedExpected(); } } function _resume() internal { _checkPaused(); RESUME_SINCE_TIMESTAMP_POSITION.setStorageUint256(block.timestamp); emit Resumed(); } function _pauseFor(uint256 _duration) internal { _checkResumed(); if (_duration == 0) revert ZeroPauseDuration(); uint256 resumeSince; if (_duration == PAUSE_INFINITELY) { resumeSince = PAUSE_INFINITELY; } else { resumeSince = block.timestamp + _duration; } _setPausedState(resumeSince); } function _pauseUntil(uint256 _pauseUntilInclusive) internal { _checkResumed(); if (_pauseUntilInclusive < block.timestamp) revert PauseUntilMustBeInFuture(); uint256 resumeSince; if (_pauseUntilInclusive != PAUSE_INFINITELY) { resumeSince = _pauseUntilInclusive + 1; } else { resumeSince = PAUSE_INFINITELY; } _setPausedState(resumeSince); } function _setPausedState(uint256 _resumeSince) internal { RESUME_SINCE_TIMESTAMP_POSITION.setStorageUint256(_resumeSince); if (_resumeSince == PAUSE_INFINITELY) { emit Paused(PAUSE_INFINITELY); } else { emit Paused(_resumeSince - block.timestamp); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol) pragma solidity ^0.8.20; import {IAccessControl} from "@openzeppelin/contracts-v5.2/access/IAccessControl.sol"; import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol"; import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol"; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ```solidity * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ```solidity * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} * to enforce additional security measures for this role. */ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable { struct RoleData { mapping(address account => bool) hasRole; bytes32 adminRole; } bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl struct AccessControlStorage { mapping(bytes32 role => RoleData) _roles; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800; function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) { assembly { $.slot := AccessControlStorageLocation } } /** * @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); _; } function __AccessControl_init() internal onlyInitializing { } function __AccessControl_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(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) { AccessControlStorage storage $ = _getAccessControlStorage(); 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) { AccessControlStorage storage $ = _getAccessControlStorage(); 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 { AccessControlStorage storage $ = _getAccessControlStorage(); 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) { AccessControlStorage storage $ = _getAccessControlStorage(); if (!hasRole(role, account)) { $._roles[role].hasRole[account] = true; emit RoleGranted(role, account, _msgSender()); return true; } else { return false; } } /** * @dev Attempts to revoke `role` from `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) { AccessControlStorage storage $ = _getAccessControlStorage(); 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.1.0) (access/extensions/AccessControlEnumerable.sol) pragma solidity ^0.8.20; import {IAccessControlEnumerable} from "@openzeppelin/contracts-v5.2/access/extensions/IAccessControlEnumerable.sol"; import {AccessControlUpgradeable} from "../AccessControlUpgradeable.sol"; import {EnumerableSet} from "@openzeppelin/contracts-v5.2/utils/structs/EnumerableSet.sol"; import {Initializable} from "../../proxy/utils/Initializable.sol"; /** * @dev Extension of {AccessControl} that allows enumerating the members of each role. */ abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessControlEnumerable, AccessControlUpgradeable { using EnumerableSet for EnumerableSet.AddressSet; /// @custom:storage-location erc7201:openzeppelin.storage.AccessControlEnumerable struct AccessControlEnumerableStorage { mapping(bytes32 role => EnumerableSet.AddressSet) _roleMembers; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControlEnumerable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant AccessControlEnumerableStorageLocation = 0xc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000; function _getAccessControlEnumerableStorage() private pure returns (AccessControlEnumerableStorage storage $) { assembly { $.slot := AccessControlEnumerableStorageLocation } } function __AccessControlEnumerable_init() internal onlyInitializing { } function __AccessControlEnumerable_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns one of the accounts that have `role`. `index` must be a * value between 0 and {getRoleMemberCount}, non-inclusive. * * Role bearers are not sorted in any particular way, and their ordering may * change at any point. * * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure * you perform all queries on the same block. See the following * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] * for more information. */ function getRoleMember(bytes32 role, uint256 index) public view virtual returns (address) { AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage(); return $._roleMembers[role].at(index); } /** * @dev Returns the number of accounts that have `role`. Can be used * together with {getRoleMember} to enumerate all bearers of a role. */ function getRoleMemberCount(bytes32 role) public view virtual returns (uint256) { AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage(); return $._roleMembers[role].length(); } /** * @dev Return all accounts that have `role` * * 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 getRoleMembers(bytes32 role) public view virtual returns (address[] memory) { AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage(); return $._roleMembers[role].values(); } /** * @dev Overload {AccessControl-_grantRole} to track enumerable memberships */ function _grantRole(bytes32 role, address account) internal virtual override returns (bool) { AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage(); bool granted = super._grantRole(role, account); if (granted) { $._roleMembers[role].add(account); } return granted; } /** * @dev Overload {AccessControl-_revokeRole} to track enumerable memberships */ function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) { AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage(); bool revoked = super._revokeRole(role, account); if (revoked) { $._roleMembers[role].remove(account); } return revoked; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol"; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is set to the address provided by the deployer. This can * later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { /// @custom:storage-location erc7201:openzeppelin.storage.Ownable struct OwnableStorage { address _owner; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300; function _getOwnableStorage() private pure returns (OwnableStorage storage $) { assembly { $.slot := OwnableStorageLocation } } /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ function __Ownable_init(address initialOwner) internal onlyInitializing { __Ownable_init_unchained(initialOwner); } function __Ownable_init_unchained(address initialOwner) internal onlyInitializing { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { OwnableStorage storage $ = _getOwnableStorage(); return $._owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { OwnableStorage storage $ = _getOwnableStorage(); address oldOwner = $._owner; $._owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.20; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Storage of the initializable contract. * * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions * when using with upgradeable contracts. * * @custom:storage-location erc7201:openzeppelin.storage.Initializable */ struct InitializableStorage { /** * @dev Indicates that the contract has been initialized. */ uint64 _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool _initializing; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; /** * @dev The contract is already initialized. */ error InvalidInitialization(); /** * @dev The contract is not initializing. */ error NotInitializing(); /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint64 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in * production. * * Emits an {Initialized} event. */ modifier initializer() { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); // Cache values to avoid duplicated sloads bool isTopLevelCall = !$._initializing; uint64 initialized = $._initialized; // Allowed calls: // - initialSetup: the contract is not in the initializing state and no previous version was // initialized // - construction: the contract is initialized at version 1 (no reininitialization) and the // current contract is just being deployed bool initialSetup = initialized == 0 && isTopLevelCall; bool construction = initialized == 1 && address(this).code.length == 0; if (!initialSetup && !construction) { revert InvalidInitialization(); } $._initialized = 1; if (isTopLevelCall) { $._initializing = true; } _; if (isTopLevelCall) { $._initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint64 version) { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing || $._initialized >= version) { revert InvalidInitialization(); } $._initialized = version; $._initializing = true; _; $._initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { _checkInitializing(); _; } /** * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. */ function _checkInitializing() internal view virtual { if (!_isInitializing()) { revert NotInitializing(); } } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing) { revert InvalidInitialization(); } if ($._initialized != type(uint64).max) { $._initialized = type(uint64).max; emit Initialized(type(uint64).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint64) { return _getInitializableStorage()._initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _getInitializableStorage()._initializing; } /** * @dev Returns a pointer to the storage namespace. */ // solhint-disable-next-line var-name-mixedcase function _getInitializableStorage() private pure returns (InitializableStorage storage $) { assembly { $.slot := INITIALIZABLE_STORAGE } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "@openzeppelin/contracts-v5.2/utils/introspection/IERC165.sol"; import {Initializable} from "../../proxy/utils/Initializable.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 ERC165Upgradeable is Initializable, IERC165 { function __ERC165_init() internal onlyInitializing { } function __ERC165_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
{ "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "cancun", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_stETH","type":"address"},{"internalType":"address","name":"_wstETH","type":"address"},{"internalType":"address","name":"_vaultHub","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ConfirmExpiryOutOfBounds","type":"error"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EthTransferFailed","type":"error"},{"inputs":[],"name":"FeeValueExceed100Percent","type":"error"},{"inputs":[],"name":"IncreasedOverLimit","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentAdjustment","type":"uint256"},{"internalType":"uint256","name":"currentAtPropositionAdjustment","type":"uint256"}],"name":"InvalidatedAdjustmentVote","type":"error"},{"inputs":[{"internalType":"uint256","name":"locked","type":"uint256"},{"internalType":"uint256","name":"mintableValue","type":"uint256"}],"name":"MintingCapacityExceeded","type":"error"},{"inputs":[],"name":"NoUnclaimedFee","type":"error"},{"inputs":[],"name":"NodeOperatorFeeUnclaimed","type":"error"},{"inputs":[],"name":"NonProxyCallsForbidden","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SameAdjustment","type":"error"},{"inputs":[],"name":"SenderNotMember","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"unreserved","type":"uint256"}],"name":"WithdrawalAmountExceedsUnreserved","type":"error"},{"inputs":[{"internalType":"string","name":"argument","type":"string"}],"name":"ZeroArgument","type":"error"},{"inputs":[],"name":"ZeroConfirmingRoles","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newAdjustment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldAdjustment","type":"uint256"}],"name":"AccruedRewardsAdjustmentSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldConfirmExpiry","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newConfirmExpiry","type":"uint256"}],"name":"ConfirmExpirySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ERC20Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721Recovered","type":"event"},{"anonymous":false,"inputs":[],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldNodeOperatorFeeBP","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newNodeOperatorFeeBP","type":"uint256"}],"name":"NodeOperatorFeeBPSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"member","type":"address"},{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"expiryTimestamp","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"RoleMemberConfirmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"stakingVault","type":"address"},{"indexed":true,"internalType":"bytes","name":"pubkey","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UnguaranteedDeposit","type":"event"},{"inputs":[],"name":"BURN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FUND_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIDO_VAULTHUB_AUTHORIZATION_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIDO_VAULTHUB_DEAUTHORIZATION_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LOCK_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MANUAL_ACCRUED_REWARDS_ADJUSTMENT_LIMIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_CONFIRM_EXPIRY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINT_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_CONFIRM_EXPIRY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NODE_OPERATOR_FEE_CLAIM_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NODE_OPERATOR_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NODE_OPERATOR_REWARDS_ADJUST_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OSSIFY_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSE_BEACON_CHAIN_DEPOSITS_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PDG_COMPENSATE_PREDEPOSIT_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PDG_PROVE_VALIDATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REBALANCE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RECOVER_ASSETS_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REQUEST_TIER_CHANGE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REQUEST_VALIDATOR_EXIT_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RESET_LOCKED_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RESUME_BEACON_CHAIN_DEPOSITS_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SET_DEPOSITOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STETH","outputs":[{"internalType":"contract ILido","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRIGGER_VALIDATOR_WITHDRAWAL_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNGUARANTEED_BEACON_CHAIN_DEPOSIT_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VAULT_HUB","outputs":[{"internalType":"contract VaultHub","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VOLUNTARY_DISCONNECT_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WITHDRAW_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WSTETH","outputs":[{"internalType":"contract IWstETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accruedRewardsAdjustment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authorizeLidoVaultHub","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountOfShares","type":"uint256"}],"name":"burnShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountOfStETH","type":"uint256"}],"name":"burnStETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountOfWstETH","type":"uint256"}],"name":"burnWstETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"claimNodeOperatorFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_pubkey","type":"bytes"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"compensateDisprovenPredepositFromPDG","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"confirmations","outputs":[{"internalType":"uint256","name":"expiryTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"confirmingRoles","outputs":[{"internalType":"bytes32[]","name":"roles","type":"bytes32[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"deauthorizeLidoVaultHub","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"forcedRebalanceThresholdBP","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fund","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getConfirmExpiry","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMembers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"role","type":"bytes32"}],"internalType":"struct Permissions.RoleAssignment[]","name":"_assignments","type":"tuple[]"}],"name":"grantRoles","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adjustmentIncrease","type":"uint256"}],"name":"increaseAccruedRewardsAdjustment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_defaultAdmin","type":"address"},{"internalType":"address","name":"_nodeOperatorManager","type":"address"},{"internalType":"uint256","name":"_nodeOperatorFeeBP","type":"uint256"},{"internalType":"uint256","name":"_confirmExpiry","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liabilityShares","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_amountOfShares","type":"uint256"}],"name":"mintShares","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_amountOfStETH","type":"uint256"}],"name":"mintStETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_amountOfWstETH","type":"uint256"}],"name":"mintWstETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"nodeOperatorFeeBP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nodeOperatorFeeClaimedReport","outputs":[{"internalType":"uint128","name":"totalValue","type":"uint128"},{"internalType":"int128","name":"inOutDelta","type":"int128"},{"internalType":"uint64","name":"timestamp","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nodeOperatorUnclaimedFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ossifyStakingVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pauseBeaconChainDeposits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"bytes","name":"pubkey","type":"bytes"},{"internalType":"uint256","name":"validatorIndex","type":"uint256"},{"internalType":"uint64","name":"childBlockTimestamp","type":"uint64"}],"internalType":"struct IPredepositGuarantee.ValidatorWitness[]","name":"_witnesses","type":"tuple[]"}],"name":"proveUnknownValidatorsToPDG","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_ether","type":"uint256"}],"name":"rebalanceVault","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"recoverERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_etherToFund","type":"uint256"}],"name":"remainingMintingCapacity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tierId","type":"uint256"}],"name":"requestTierChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_pubkeys","type":"bytes"}],"name":"requestValidatorExit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveRatioBP","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"resetLocked","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resumeBeaconChainDeposits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"role","type":"bytes32"}],"internalType":"struct Permissions.RoleAssignment[]","name":"_assignments","type":"tuple[]"}],"name":"revokeRoles","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newAdjustment","type":"uint256"},{"internalType":"uint256","name":"_currentAdjustment","type":"uint256"}],"name":"setAccruedRewardsAdjustment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newConfirmExpiry","type":"uint256"}],"name":"setConfirmExpiry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_depositor","type":"address"}],"name":"setDepositor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newNodeOperatorFeeBP","type":"uint256"}],"name":"setNodeOperatorFeeBP","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shareLimit","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingVault","outputs":[{"internalType":"contract IStakingVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalMintingCapacity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferStakingVaultOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasuryFeeBP","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_pubkeys","type":"bytes"},{"internalType":"uint64[]","name":"_amounts","type":"uint64[]"},{"internalType":"address","name":"_refundRecipient","type":"address"}],"name":"triggerValidatorWithdrawal","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"pubkey","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32","name":"depositDataRoot","type":"bytes32"}],"internalType":"struct StakingVaultDeposit[]","name":"_deposits","type":"tuple[]"}],"name":"unguaranteedDepositToBeaconChain","outputs":[{"internalType":"uint256","name":"totalAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unreserved","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultSocket","outputs":[{"components":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint96","name":"liabilityShares","type":"uint96"},{"internalType":"uint96","name":"shareLimit","type":"uint96"},{"internalType":"uint16","name":"reserveRatioBP","type":"uint16"},{"internalType":"uint16","name":"forcedRebalanceThresholdBP","type":"uint16"},{"internalType":"uint16","name":"treasuryFeeBP","type":"uint16"},{"internalType":"bool","name":"pendingDisconnect","type":"bool"},{"internalType":"uint96","name":"feeSharesCharged","type":"uint96"}],"internalType":"struct VaultHub.VaultSocket","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"voluntaryDisconnect","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_ether","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawableEther","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
61010060405262015180600355348015610017575f80fd5b50604051615d26380380615d2683398101604081905261003691610140565b80806001600160a01b038116610080576040516356e4289360e01b81526020600482015260096024820152682fbb30bab63a243ab160b91b60448201526064015b60405180910390fd5b306080526001600160a01b0390811660a052841690506100cc576040516356e4289360e01b81526020600482015260066024820152650bee6e88aa8960d31b6044820152606401610077565b6001600160a01b03821661010d576040516356e4289360e01b81526020600482015260076024820152660beeee6e88aa8960cb1b6044820152606401610077565b506001600160a01b0391821660c0521660e052610180565b80516001600160a01b038116811461013b575f80fd5b919050565b5f805f60608486031215610152575f80fd5b61015b84610125565b925061016960208501610125565b915061017760408501610125565b90509250925092565b60805160a05160c05160e051615abd6102695f395f8181610ff701528181611bd301528181611c44015281816130240152818161379001526137cf01525f8181611049015281816119ee01528181611b3f015281816120880152818161212001528181612274015281816123190152818161286a01528181613053015281816136d501528181613864015281816139160152818161477301526147fe01525f818161067701528181611fc70152818161289901528181613468015281816138e401528181613ade01528181613e940152818161451f015261482d01525f614c200152615abd5ff3fe608060405260043610610516575f3560e01c80636d47556611610299578063be807b2811610160578063e6c772d4116100c9578063f3fef3a311610083578063f3fef3a3146111bb578063f634dcbd146111da578063f6c76318146111f9578063faa1d4411461120f578063fd84b78514611242578063ff108ccb14611257575f80fd5b8063e6c772d4146110e4578063e9a9c850146110f8578063eb990c591461112b578063f06dce031461114a578063f0e9fcd11461117d578063f2c098b71461119c575f80fd5b8063d547741f1161011a578063d547741f14610fc7578063d9fb643a14610fe6578063dd46706414611019578063e00bfe5014611038578063e02023a11461106b578063e359c77f1461109e575f80fd5b8063be807b2814610f07578063c5ced5bb14610f3a578063c6728c6c14610f4d578063c70cb12014610f61578063ca15c87314610f94578063d4c3eea014610fb3575f80fd5b806397f51afe11610202578063a3246ad3116101bc578063a3246ad314610e45578063aa9f2d6a14610e71578063b35869ed14610e85578063b60d428814610eb8578063b930908f14610ec0578063bddf300c14610ef3575f80fd5b806397f51afe14610d8357806399ab9e0b14610da25780639aeb533314610dc1578063a0f3422a14610de0578063a217fddf14610dff578063a2ed81b014610e12575f80fd5b80638860be41116102535780638860be4114610cd65780638c2b499514610cea5780639010d07c14610d1d57806391d1485414610d3c57806392e0303614610d5b5780639563110514610d6f575f80fd5b80636d47556614610bf557806370ba285714610c285780637b108d9214610c3c57806382b7e7b914610c5d5780638322fff214610c90578063853c637d14610cb7575f80fd5b806335763d2b116103dd57806350823a491161034657806361404235116103005780636140423514610ac0578063626c5ebc14610ade578063699d79bd14610b115780636aa2930b14610b445780636ab1583614610bae5780636b1a00d514610be1575f80fd5b806350823a49146109f457806350d99dd414610a15578063528c198a14610a285780635680e14514610a3b5780635bc7136314610a6e5780635eaaf1d514610aa1575f80fd5b80633e619a6c116103975780633e619a6c1461093d5780633e8fe40e146109505780633efd85971461096f5780634156bbf41461098e57806343c71057146109ad5780634bc36840146109e0575f80fd5b806335763d2b1461084d57806336568abe1461086c5780633a04d4f31461088b5780633a284985146108ab5780633be75aa3146108de5780633c56eb471461090a575f80fd5b80631e73cad31161047f5780632c0772b4116104395780632c0772b4146107825780632f286b1b146107b55780632f2ff15d146107e857806332cd7ebf1461080757806332ffa14f1461082657806333ac88b41461083a575f80fd5b80631e73cad3146106c6578063221ed94d146106da578063248a9ca3146106f957806324e7964a146107275780632806f5e01461073b5780632bb115ce1461074f575f80fd5b80630da541b8116104d05780630da541b8146105f0578063107415521461060f5780631171bda91461062e578063158ef93e1461064d578063196b9e06146106665780631a296f40146106b1575f80fd5b806301ffc9a71461052a5780630857593b1461055e5780630a4a3ed2146105805780630b91990d1461059f5780630bff8881146105b35780630d7fcd1a146105c9575f80fd5b366105265761052434611276565b005b5f80fd5b348015610535575f80fd5b50610549610544366004614e6c565b6112fb565b60405190151581526020015b60405180910390f35b348015610569575f80fd5b50610572611325565b604051908152602001610555565b34801561058b575f80fd5b5061057261059a366004614eda565b61134d565b3480156105aa575f80fd5b50600354610572565b3480156105be575f80fd5b5061057262278d0081565b3480156105d4575f80fd5b506105dd611614565b60405161ffff9091168152602001610555565b3480156105fb575f80fd5b5061052461060a366004614f18565b611626565b34801561061a575f80fd5b50610524610629366004614f6c565b611632565b348015610639575f80fd5b50610524610648366004614fb2565b611640565b348015610658575f80fd5b506004546105499060ff1681565b348015610671575f80fd5b506106997f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610555565b3480156106bc575f80fd5b5061057260085481565b3480156106d1575f80fd5b50610524611816565b3480156106e5575f80fd5b506105726106f4366004614f18565b611820565b348015610704575f80fd5b50610572610713366004614f18565b5f9081526020819052604090206001015490565b348015610732575f80fd5b50610699611868565b348015610746575f80fd5b506105dd611871565b34801561075a575f80fd5b506105727fec7606002753a83f3033806d5596d2cf315f8f3836c68f2fabbbc3ed9b6f1ac481565b34801561078d575f80fd5b506105727fa90c7030a27f389f9fc8ed21a0556f40c88130cc14a80db936bed68261819b2c81565b3480156107c0575f80fd5b506105727faf5678186f680e15e8b3e8a8679bdd61c0eb1aaded90254c3e1b5b6b3fed62af81565b3480156107f3575f80fd5b50610524610802366004614ff0565b611883565b348015610812575f80fd5b50610524610821366004614f18565b6118ad565b348015610831575f80fd5b506105726118b6565b61052461084836600461501e565b6118c0565b348015610858575f80fd5b5061052461086736600461509b565b6118d4565b348015610877575f80fd5b50610524610886366004614ff0565b611972565b348015610896575f80fd5b506105725f80516020615a4883398151915281565b3480156108b6575f80fd5b506105727fa38b301640bddfd3e6a9d2a11d13551d53ef81526347ff09d798738fcc5a49d481565b3480156108e9575f80fd5b506108f26119a5565b6040516001600160601b039091168152602001610555565b348015610915575f80fd5b506105727fd0d885b3f66154d15e918afc9fcd5eb37aea7fbf993c32b18a84a0be97d1327681565b61052461094b366004615109565b6119b7565b34801561095b575f80fd5b5061052461096a366004614f18565b611c6a565b34801561097a575f80fd5b50610524610989366004614f18565b611cda565b348015610999575f80fd5b506105246109a8366004615133565b611f68565b3480156109b8575f80fd5b506105727f59d005e32db662b94335d6bedfeb453fd2202b9f0cc7a6ed498d9098171744b081565b3480156109eb575f80fd5b506108f2611f73565b3480156109ff575f80fd5b50610a08611f85565b6040516105559190615185565b610524610a23366004615109565b612063565b610524610a36366004615109565b6122e2565b348015610a46575f80fd5b506105727f3f82ecf462ddac43fc17ba11472c35f18b7760b4f5a5fc50b9625f9b5a22cf6281565b348015610a79575f80fd5b506105727f933b7d5c112a4d05b489cea0b2ced98acb27d3d0fc9827c92cdacb2d6c5559c281565b348015610aac575f80fd5b50610524610abb366004614f18565b612453565b348015610acb575f80fd5b506105726a084595161401484a00000081565b348015610ae9575f80fd5b506105727fb850402129bccae797798069a8cf3147a0cb7c3193f70558a75f7df0b8651c3081565b348015610b1c575f80fd5b506105727f32d0d6546e21c13ff633616141dc9daad87d248d1d37c56bf493d06d627ecb7b81565b348015610b4f575f80fd5b50600654600754610b7d916001600160801b03811691600160801b909104600f0b906001600160401b031683565b604080516001600160801b039094168452600f9290920b60208401526001600160401b031690820152606001610555565b348015610bb9575f80fd5b506105727fea19d3b23bd90fdd52445ad672f2b6fb1fef7230d49c6a827c1cd288d02994d581565b348015610bec575f80fd5b506105726126d2565b348015610c00575f80fd5b506105727f9586321ac05f110e4b4a0a42aba899709345af0ca78910e8832ddfd71fed2bf481565b348015610c33575f80fd5b506105246127da565b348015610c47575f80fd5b50610c506127e2565b6040516105559190615224565b348015610c68575f80fd5b506105727f24633f096579ea9942f9f4de94a59d0fd90e22436d62e831fc27ea6f0cbba5a081565b348015610c9b575f80fd5b5061069973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b348015610cc2575f80fd5b50610524610cd1366004614f18565b612853565b348015610ce1575f80fd5b506105dd61290d565b348015610cf5575f80fd5b506105727f956303b84cebe7f6d4c573121d1d147af67b9f1048630d8dba50a3e6f41a738c81565b348015610d28575f80fd5b50610699610d37366004615267565b61291f565b348015610d47575f80fd5b50610549610d56366004614ff0565b61293d565b348015610d66575f80fd5b50610524612965565b348015610d7a575f80fd5b5061052461296d565b348015610d8e575f80fd5b50610524610d9d366004614eda565b612975565b348015610dad575f80fd5b50610524610dbc366004615287565b61297f565b348015610dcc575f80fd5b50610524610ddb366004615267565b612988565b348015610deb575f80fd5b50610524610dfa366004615287565b612c76565b348015610e0a575f80fd5b506105725f81565b348015610e1d575f80fd5b506105727f12721d7ec843176aec149b06f8f88034866537c4b8a2fc38014f2daaba6f156381565b348015610e50575f80fd5b50610e64610e5f366004614f18565b612e00565b60405161055591906152a2565b348015610e7c575f80fd5b50610524612e19565b348015610e90575f80fd5b506105727fe0b9915a7819e810f29b50730662441fec3443eb363b7e7c90c77fada416f27681565b610524612e21565b348015610ecb575f80fd5b506105727f689f0a569be0c9b6cd2c11c81cb0add722272abdae6b649fdb1e05f1d9bb8a2f81565b348015610efe575f80fd5b50610524612e2a565b348015610f12575f80fd5b506105727f17960a6b137243c888669d93712b617414dd6d1ab55d5eca488ccb1d0894cd5c81565b610524610f48366004614f18565b612e32565b348015610f58575f80fd5b50610572612e4a565b348015610f6c575f80fd5b506105727fea6487df651bb740150364c496e1c7403dd62063c96e44906cc98c6a919a9d8881565b348015610f9f575f80fd5b50610572610fae366004614f18565b612f4e565b348015610fbe575f80fd5b50610572612f64565b348015610fd2575f80fd5b50610524610fe1366004614ff0565b612fcc565b348015610ff1575f80fd5b506106997f000000000000000000000000000000000000000000000000000000000000000081565b348015611024575f80fd5b50610524611033366004614f18565b612ff0565b348015611043575f80fd5b506106997f000000000000000000000000000000000000000000000000000000000000000081565b348015611076575f80fd5b506105727f355caf1c2580ed8185acb5ea3573b71f85186b41bdf69e3eb8f1fcd122a562df81565b3480156110a9575f80fd5b506105726110b8366004615326565b81516020818401810180516002825292820194820194909420919093529091525f908152604090205481565b3480156110ef575f80fd5b50610524612ff9565b348015611103575f80fd5b506105727fe996ac9b332538bb1fa3cd6743aa47011623cdb94bd964a494ee9d371e4a27d381565b348015611136575f80fd5b506105246111453660046153b9565b613001565b348015611155575f80fd5b506105727f6a77c9119dc50d7dc957a0b6bec3e9899530f9a3df66bb8addb7f073f678f2f281565b348015611188575f80fd5b506105246111973660046153fc565b6130bd565b3480156111a7575f80fd5b506105246111b6366004615287565b6131f0565b3480156111c6575f80fd5b506105246111d5366004615109565b6131f9565b3480156111e5575f80fd5b506105246111f436600461509b565b613239565b348015611204575f80fd5b506105726201518081565b34801561121a575f80fd5b506105727f49fd147dcd655645beca3a7658e351ec884fb6dfffceb007c33e3cb733af2adb81565b34801561124d575f80fd5b5061057260055481565b348015611262575f80fd5b50610524611271366004614f18565b6132d2565b7f933b7d5c112a4d05b489cea0b2ced98acb27d3d0fc9827c92cdacb2d6c5559c26112a0816132db565b6112a86132e5565b6001600160a01b031663b60d4288836040518263ffffffff1660e01b81526004015f604051808303818588803b1580156112e0575f80fd5b505af11580156112f2573d5f803e3d5ffd5b50505050505050565b5f6001600160e01b03198216635a05180f60e01b148061131f575061131f826132fa565b92915050565b5f6113486113316132e5565b6001600160a01b031631611343612e4a565b61332e565b905090565b5f806113576132e5565b90505f816001600160a01b0316636b96736b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611396573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113ba9190615430565b90505f5b84811015611402578585828181106113d8576113d861544b565b90506020028101906113ea919061545f565b6113f8906040013585615491565b93506001016113be565b5061140b612e4a565b831115611442578261141b612e4a565b604051632cbc004760e01b8152600481019290925260248201526044015b60405180910390fd5b61144b83613343565b6114618360085461145c9190615491565b6133d7565b5f826001600160a01b0316634cd79e0a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561149e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114c291906154a4565b6040516020016114d491815260200190565b6040516020818303038152906040529050365f5b86811015611609578787828181106115025761150261544b565b9050602002810190611514919061545f565b91506001600160a01b0384166322895118604084013561153485806154bb565b8761154260208901896154bb565b89606001356040518863ffffffff1660e01b815260040161156896959493929190615525565b5f604051808303818588803b15801561157f575f80fd5b505af1158015611591573d5f803e3d5ffd5b506115a493508592508291506154bb9050565b6040516115b292919061558c565b6040518091039020856001600160a01b03167f76dc34440571fb7e9534cdb59b8d71754696637cad774d6fa401da9b674861cf84604001356040516115f991815260200190565b60405180910390a36001016114e8565b505050505092915050565b5f61161d611f85565b60a00151905090565b61162f8161343c565b50565b61163c828261352f565b5050565b7fa38b301640bddfd3e6a9d2a11d13551d53ef81526347ff09d798738fcc5a49d461166a816132db565b6001600160a01b0384166116aa576040516356e4289360e01b81526020600482015260066024820152652fba37b5b2b760d11b6044820152606401611439565b6001600160a01b0383166116d1576040516356e4289360e01b81526004016114399061559b565b815f0361170b576040516356e4289360e01b815260206004820152600760248201526617d85b5bdd5b9d60ca1b6044820152606401611439565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b038516016117b8575f836001600160a01b0316836040515f6040518083038185875af1925050503d805f8114611779576040519150601f19603f3d011682016040523d82523d5f602084013e61177e565b606091505b50509050806117b257604051636f54afdd60e11b81526001600160a01b038516600482015260248101849052604401611439565b506117c3565b6117c384848461358e565b836001600160a01b0316836001600160a01b03167faca8fb252cde442184e5f10e0f2e6e4029e8cd7717cae63559079610702436aa8460405161180891815260200190565b60405180910390a350505050565b61181e6135ed565b565b5f8061182b83613668565b90505f611836611f85565b602001516001600160601b031690508082101561185657505f9392505050565b61186081836155bf565b949350505050565b5f6113486132e5565b5f61187a611f85565b60800151905090565b5f8281526020819052604090206001015461189d816132db565b6118a78383613758565b50505050565b61162f8161378b565b5f6113485f613668565b6118cd858585858561398c565b5050505050565b5f819003611914576040516356e4289360e01b815260206004820152600c60248201526b5f61737369676e6d656e747360a01b6044820152606401611439565b5f5b8181101561196d576119658383838181106119335761193361544b565b9050604002016020013584848481811061194f5761194f61544b565b610fe19260206040909202019081019150615287565b600101611916565b505050565b6001600160a01b038116331461199b5760405163334bd91960e11b815260040160405180910390fd5b61196d8282613a28565b5f6119ae611f85565b60400151905090565b34156119c6576119c634611276565b805f6119d0611f85565b90505f816060015161ffff166127106119e991906155bf565b6127107f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316630103349c8686602001516001600160601b0316611a349190615491565b6040518263ffffffff1660e01b8152600401611a5291815260200190565b602060405180830381865afa158015611a6d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a9191906154a4565b611a9b91906155d2565b611aa591906155e9565b9050611aaf6132e5565b6001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa158015611aea573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b0e91906154a4565b811115611b1e57611b1e81613a53565b611b283085613ab2565b6040516240cd2760e21b8152600481018590525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690630103349c90602401602060405180830381865afa158015611b8c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bb091906154a4565b604051630ea598cb60e41b8152600481018290529091505f906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063ea598cb0906024016020604051808303815f875af1158015611c19573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c3d91906154a4565b90506112f27f0000000000000000000000000000000000000000000000000000000000000000888361358e565b7fe0b9915a7819e810f29b50730662441fec3443eb363b7e7c90c77fada416f276611c94816132db565b5f82600854611ca39190615491565b90506a084595161401484a000000811115611cd15760405163f579fde760e01b815260040160405180910390fd5b61196d816133d7565b611ce26127e2565b80515f03611d035760405163f814a95b60e01b815260040160405180910390fd5b80515f80826001600160401b03811115611d1f57611d1f6152e2565b604051908082528060200260200182016040528015611d48578160200160208202803683370190505b5090505f8060035442611d5b9190615491565b90505f5b85811015611e4f575f878281518110611d7a57611d7a61544b565b60200260200101519050611d8e813361293d565b15611e02576001935085611da181615608565b9650506001858381518110611db857611db861544b565b60200260200101901515908115158152505080336001600160a01b03165f80516020615a68833981519152855f36604051611df593929190615620565b60405180910390a3611e46565b4260025f36604051611e1592919061558c565b90815260200160405180910390205f8381526020019081526020015f205410611e465785611e4281615608565b9650505b50600101611d5f565b5081611e6e5760405163dd16cb2360e01b815260040160405180910390fd5b848403611ee0575f5b85811015611ed1575f878281518110611e9257611e9261544b565b6020026020010151905060025f36604051611eae92919061558c565b90815260408051602092819003830190205f938452909152812055600101611e77565b50611edb87613b33565b6112f2565b5f5b85811015611f5e57838181518110611efc57611efc61544b565b602002602001015115611f56575f878281518110611f1c57611f1c61544b565b602002602001015190508260025f36604051611f3992919061558c565b90815260408051602092819003830190205f948552909152909120555b600101611ee2565b5050505050505050565b6118a7838383613bac565b5f611f7c611f85565b60200151905090565b60408051610100810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101919091527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663efe98d85611ffc6132e5565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240161010060405180830381865afa15801561203f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113489190615674565b34156120725761207234611276565b604051631920845160e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690631920845190602401602060405180830381865afa1580156120d5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120f991906154a4565b5f612102611f85565b90505f816060015161ffff1661271061211b91906155bf565b6127107f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316630103349c8686602001516001600160601b03166121669190615491565b6040518263ffffffff1660e01b815260040161218491815260200190565b602060405180830381865afa15801561219f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121c391906154a4565b6121cd91906155d2565b6121d791906155e9565b90506121e16132e5565b6001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa15801561221c573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061224091906154a4565b8111156122505761225081613a53565b604051631920845160e01b8152600481018590526118cd9086906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631920845190602401602060405180830381865afa1580156122b9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122dd91906154a4565b613ab2565b34156122f1576122f134611276565b805f6122fb611f85565b90505f816060015161ffff1661271061231491906155bf565b6127107f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316630103349c8686602001516001600160601b031661235f9190615491565b6040518263ffffffff1660e01b815260040161237d91815260200190565b602060405180830381865afa158015612398573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123bc91906154a4565b6123c691906155d2565b6123d091906155e9565b90506123da6132e5565b6001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa158015612415573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061243991906154a4565b8111156124495761244981613a53565b6118cd8585613ab2565b61245b6127e2565b80515f0361247c5760405163f814a95b60e01b815260040160405180910390fd5b80515f80826001600160401b03811115612498576124986152e2565b6040519080825280602002602001820160405280156124c1578160200160208202803683370190505b5090505f80600354426124d49190615491565b90505f5b858110156125c8575f8782815181106124f3576124f361544b565b60200260200101519050612507813361293d565b1561257b57600193508561251a81615608565b96505060018583815181106125315761253161544b565b60200260200101901515908115158152505080336001600160a01b03165f80516020615a68833981519152855f3660405161256e93929190615620565b60405180910390a36125bf565b4260025f3660405161258e92919061558c565b90815260200160405180910390205f8381526020019081526020015f2054106125bf57856125bb81615608565b9650505b506001016124d8565b50816125e75760405163dd16cb2360e01b815260040160405180910390fd5b848403612654575f5b8581101561264a575f87828151811061260b5761260b61544b565b6020026020010151905060025f3660405161262792919061558c565b90815260408051602092819003830190205f9384529091528120556001016125f0565b50611edb87613cb6565b5f5b85811015611f5e578381815181106126705761267061544b565b6020026020010151156126ca575f8782815181106126905761269061544b565b602002602001015190508260025f366040516126ad92919061558c565b90815260408051602092819003830190205f948552909152909120555b600101612656565b5f806126dc6132e5565b6001600160a01b0316636af6eeed6040518163ffffffff1660e01b8152600401606060405180830381865afa158015612717573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061273b919061574f565b600854600680546020840151939450909260016001607f1b03909216915f91839161277091600160801b9004600f0b906157cf565b84548651612787916001600160801b0316906157cf565b61279191906157cf565b61279b91906157cf565b90505f81600f0b136127ad575f6127d1565b612710600554826001600160801b03166127c791906155d2565b6127d191906155e9565b94505050505090565b61181e613dff565b60408051600280825260608083018452926020830190803683370190505090505f801b815f815181106128175761281761544b565b6020026020010181815250505f80516020615a48833981519152816001815181106128445761284461544b565b60200260200101818152505090565b604051636d78045960e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636d780459906128c39033907f0000000000000000000000000000000000000000000000000000000000000000908690600401615805565b6020604051808303815f875af11580156128df573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061290391906154a4565b5061162f81613e68565b5f612916611f85565b60600151905090565b5f8281526001602052604081206129369083613ec9565b9392505050565b5f918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b61181e613ed4565b61181e613f3d565b61163c8282613fa6565b61162f816140d2565b6129906127e2565b80515f036129b15760405163f814a95b60e01b815260040160405180910390fd5b80515f80826001600160401b038111156129cd576129cd6152e2565b6040519080825280602002602001820160405280156129f6578160200160208202803683370190505b5090505f8060035442612a099190615491565b90505f5b85811015612afd575f878281518110612a2857612a2861544b565b60200260200101519050612a3c813361293d565b15612ab0576001935085612a4f81615608565b9650506001858381518110612a6657612a6661544b565b60200260200101901515908115158152505080336001600160a01b03165f80516020615a68833981519152855f36604051612aa393929190615620565b60405180910390a3612af4565b4260025f36604051612ac392919061558c565b90815260200160405180910390205f8381526020019081526020015f205410612af45785612af081615608565b9650505b50600101612a0d565b5081612b1c5760405163dd16cb2360e01b815260040160405180910390fd5b848403612bed575f5b85811015612b7f575f878281518110612b4057612b4061544b565b6020026020010151905060025f36604051612b5c92919061558c565b90815260408051602092819003830190205f938452909152812055600101612b25565b508660085414612bb35760085460405160016273f97f60e11b03198152600481019190915260248101889052604401611439565b6a084595161401484a000000881115612bdf5760405163f579fde760e01b815260040160405180910390fd5b612be8886133d7565b611f5e565b5f5b85811015612c6b57838181518110612c0957612c0961544b565b602002602001015115612c63575f878281518110612c2957612c2961544b565b602002602001015190508260025f36604051612c4692919061558c565b90815260408051602092819003830190205f948552909152909120555b600101612bef565b505050505050505050565b7f24633f096579ea9942f9f4de94a59d0fd90e22436d62e831fc27ea6f0cbba5a0612ca0816132db565b6001600160a01b038216612cc7576040516356e4289360e01b81526004016114399061559b565b5f612cd06126d2565b9050805f03612cf257604051633e2d4cff60e11b815260040160405180910390fd5b60085415612d0357612d035f6133d7565b612d0b6132e5565b6001600160a01b0316636af6eeed6040518163ffffffff1660e01b8152600401606060405180830381865afa158015612d46573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d6a919061574f565b805160208201516001600160801b03908116600160801b0291161760065560400151600780546001600160401b0390921667ffffffffffffffff19909216919091179055612db66132e5565b60405163f3fef3a360e01b81526001600160a01b03858116600483015260248201849052919091169063f3fef3a3906044015b5f604051808303815f87803b1580156112e0575f80fd5b5f81815260016020526040902060609061131f906143ad565b61181e6143b9565b61181e34611276565b61181e614422565b3415612e4157612e4134611276565b61162f8161448b565b5f80612e546132e5565b90505f612e5f6126d2565b826001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e9b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ebf91906154a4565b612ec99190615491565b90505f826001600160a01b031663d4c3eea06040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f08573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f2c91906154a4565b9050808211612f4457612f3f82826155bf565b612f46565b5f5b935050505090565b5f81815260016020526040812061131f906144ea565b5f612f6d6132e5565b6001600160a01b031663d4c3eea06040518163ffffffff1660e01b8152600401602060405180830381865afa158015612fa8573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061134891906154a4565b5f82815260208190526040902060010154612fe6816132db565b6118a78383613a28565b61162f81613a53565b61181e6144f3565b61300d84848484614592565b60405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301525f1960248301527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906044016020604051808303815f875af1158015613099573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118cd9190615829565b7fa38b301640bddfd3e6a9d2a11d13551d53ef81526347ff09d798738fcc5a49d46130e7816132db565b6001600160a01b038416613127576040516356e4289360e01b81526020600482015260066024820152652fba37b5b2b760d11b6044820152606401611439565b6001600160a01b03821661314e576040516356e4289360e01b81526004016114399061559b565b604051632142170760e11b81526001600160a01b038516906342842e0e9061317e90309086908890600401615805565b5f604051808303815f87803b158015613195575f80fd5b505af11580156131a7573d5f803e3d5ffd5b50505050836001600160a01b0316826001600160a01b03167f6a30e6784464f0d1f4158aa4cb65ae9239b0fa87c7f2c083ee6dde44ba97b5e68560405161180891815260200190565b61162f81614690565b5f613202612e4a565b90508082111561322f57604051632cbc004760e01b81526004810183905260248101829052604401611439565b61196d83836146f2565b5f819003613279576040516356e4289360e01b815260206004820152600c60248201526b5f61737369676e6d656e747360a01b6044820152606401611439565b5f5b8181101561196d576132ca8383838181106132985761329861544b565b905060400201602001358484848181106132b4576132b461544b565b6108029260206040909202019081019150615287565b60010161327b565b61162f8161475b565b61162f81336148a1565b5f806132f0306148da565b6020015192915050565b5f6001600160e01b03198216637965db0b60e01b148061131f57506301ffc9a760e01b6001600160e01b031983161461131f565b5f81831061333c5781612936565b5090919050565b7fea6487df651bb740150364c496e1c7403dd62063c96e44906cc98c6a919a9d8861336d816132db565b6133756132e5565b60405163f3fef3a360e01b8152306004820152602481018490526001600160a01b03919091169063f3fef3a3906044015b5f604051808303815f87803b1580156133bd575f80fd5b505af11580156133cf573d5f803e3d5ffd5b505050505050565b6008548082036133fa576040516318a04aa160e01b815260040160405180910390fd5b600882905560408051838152602081018390527f04f4f631361c0cb55bafac3cc7c5b167edc0a7284003c4d780a4ee6ed00f52b6910160405180910390a15050565b7fd0d885b3f66154d15e918afc9fcd5eb37aea7fbf993c32b18a84a0be97d13276613466816132db565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b9a038286040518163ffffffff1660e01b8152600401602060405180830381865afa1580156134c2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134e69190615430565b6001600160a01b03166341d5467c6134fc6132e5565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018590526044016133a6565b7f32d0d6546e21c13ff633616141dc9daad87d248d1d37c56bf493d06d627ecb7b613559816132db565b6135616132e5565b6001600160a01b0316631074155284846040518363ffffffff1660e01b8152600401612de9929190615842565b6040516001600160a01b0383811660248301526044820183905261196d91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050614946565b7f6a77c9119dc50d7dc957a0b6bec3e9899530f9a3df66bb8addb7f073f678f2f2613617816132db565b61361f6132e5565b6001600160a01b0316631e73cad36040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613656575f80fd5b505af11580156118cd573d5f803e3d5ffd5b5f80612710613675611f85565b606001516136899061ffff166127106155bf565b846136926149b2565b61369c9190615491565b6136a691906155d2565b6136b091906155e9565b604051631920845160e01b815260048101829052909150612936906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631920845190602401602060405180830381865afa15801561371a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061373e91906154a4565b613746611f85565b604001516001600160601b031661332e565b5f806137648484614a2c565b90508015612936575f8481526001602052604090206137839084614abb565b509392505050565b6137b77f0000000000000000000000000000000000000000000000000000000000000000333084614acf565b604051636f074d1f60e11b8152600481018290525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063de0e9a3e906024016020604051808303815f875af115801561381d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061384191906154a4565b604051631920845160e01b8152600481018290529091505f906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631920845190602401602060405180830381865afa1580156138a9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906138cd91906154a4565b604051638fcb4e5b60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018390529192507f000000000000000000000000000000000000000000000000000000000000000090911690638fcb4e5b906044016020604051808303815f875af115801561395e573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061398291906154a4565b5061196d81613e68565b7fea19d3b23bd90fdd52445ad672f2b6fb1fef7230d49c6a827c1cd288d02994d56139b6816132db565b6139be6132e5565b6001600160a01b03166333ac88b43488888888886040518763ffffffff1660e01b81526004016139f2959493929190615860565b5f604051808303818588803b158015613a09575f80fd5b505af1158015613a1b573d5f803e3d5ffd5b5050505050505050505050565b5f80613a348484614af7565b90508015612936575f8481526001602052604090206137839084614b60565b7faf5678186f680e15e8b3e8a8679bdd61c0eb1aaded90254c3e1b5b6b3fed62af613a7d816132db565b613a856132e5565b6001600160a01b031663dd467064836040518263ffffffff1660e01b81526004016133a691815260200190565b7fe996ac9b332538bb1fa3cd6743aa47011623cdb94bd964a494ee9d371e4a27d3613adc816132db565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bf8ba596613b136132e5565b85856040518463ffffffff1660e01b8152600401612de993929190615805565b62015180811080613b46575062278d0081115b15613b645760405163bbc3a3e360e01b815260040160405180910390fd5b6003805490829055604080518281526020810184905233917f437dc1a3e26abf0b8381463abca48d1e7d322e803c2b160d3ee75c04a492a18291015b60405180910390a25050565b5f7f17960a6b137243c888669d93712b617414dd6d1ab55d5eca488ccb1d0894cd5c613bd7816132db565b613bdf6132e5565b6001600160a01b031663c7c4ff466040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c1a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613c3e9190615430565b6001600160a01b0316634380df4f8686866040518463ffffffff1660e01b8152600401613c6d939291906158d9565b6020604051808303815f875af1158015613c89573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613cad91906154a4565b95945050505050565b612710811115613cd95760405163137b7b8960e31b815260040160405180910390fd5b5f613ce26126d2565b1115613d0157604051638e09a5b360e01b815260040160405180910390fd5b60055480158015613d1157505f82115b15613dc257613d1e6132e5565b6001600160a01b0316636af6eeed6040518163ffffffff1660e01b8152600401606060405180830381865afa158015613d59573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613d7d919061574f565b805160208201516001600160801b03908116600160801b0291161760065560400151600780546001600160401b0390921667ffffffffffffffff199092169190911790555b6005829055604080518281526020810184905233917ffbbf61b0478977353f50313e99775188b5d61eef9a35657660db41b38e5d1b619101613ba0565b7f956303b84cebe7f6d4c573121d1d147af67b9f1048630d8dba50a3e6f41a738c613e29816132db565b613e316132e5565b6001600160a01b03166370ba28576040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613656575f80fd5b7f689f0a569be0c9b6cd2c11c81cb0add722272abdae6b649fdb1e05f1d9bb8a2f613e92816132db565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ee7a7c046134fc6132e5565b5f6129368383614b74565b7f12721d7ec843176aec149b06f8f88034866537c4b8a2fc38014f2daaba6f1563613efe816132db565b613f066132e5565b6001600160a01b03166392e030366040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613656575f80fd5b7f59d005e32db662b94335d6bedfeb453fd2202b9f0cc7a6ed498d9098171744b0613f67816132db565b613f6f6132e5565b6001600160a01b031663956311056040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613656575f80fd5b7fb850402129bccae797798069a8cf3147a0cb7c3193f70558a75f7df0b8651c30613fd0816132db565b5f613fd96132e5565b90505f816001600160a01b031663c7c4ff466040518163ffffffff1660e01b8152600401602060405180830381865afa158015614018573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061403c9190615430565b90505f5b848110156133cf57816001600160a01b0316630fef9cdf8787848181106140695761406961544b565b905060200281019061407b919061545f565b856040518363ffffffff1660e01b8152600401614099929190615975565b5f604051808303815f87803b1580156140b0575f80fd5b505af11580156140c2573d5f803e3d5ffd5b5050600190920191506140409050565b6140da6127e2565b80515f036140fb5760405163f814a95b60e01b815260040160405180910390fd5b80515f80826001600160401b03811115614117576141176152e2565b604051908082528060200260200182016040528015614140578160200160208202803683370190505b5090505f80600354426141539190615491565b90505f5b85811015614247575f8782815181106141725761417261544b565b60200260200101519050614186813361293d565b156141fa57600193508561419981615608565b96505060018583815181106141b0576141b061544b565b60200260200101901515908115158152505080336001600160a01b03165f80516020615a68833981519152855f366040516141ed93929190615620565b60405180910390a361423e565b4260025f3660405161420d92919061558c565b90815260200160405180910390205f8381526020019081526020015f20541061423e578561423a81615608565b9650505b50600101614157565b50816142665760405163dd16cb2360e01b815260040160405180910390fd5b84840361432f575f5b858110156142c9575f87828151811061428a5761428a61544b565b6020026020010151905060025f366040516142a692919061558c565b90815260408051602092819003830190205f93845290915281205560010161426f565b506142d26132e5565b60405163f2fde38b60e01b81526001600160a01b038981166004830152919091169063f2fde38b906024015f604051808303815f87803b158015614314575f80fd5b505af1158015614326573d5f803e3d5ffd5b505050506112f2565b5f5b85811015611f5e5783818151811061434b5761434b61544b565b6020026020010151156143a5575f87828151811061436b5761436b61544b565b602002602001015190508260025f3660405161438892919061558c565b90815260408051602092819003830190205f948552909152909120555b600101614331565b60605f61293683614b9a565b7fec7606002753a83f3033806d5596d2cf315f8f3836c68f2fabbbc3ed9b6f1ac46143e3816132db565b6143eb6132e5565b6001600160a01b031663aa9f2d6a6040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613656575f80fd5b7fa90c7030a27f389f9fc8ed21a0556f40c88130cc14a80db936bed68261819b2c61444c816132db565b6144546132e5565b6001600160a01b031663bddf300c6040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613656575f80fd5b7f3f82ecf462ddac43fc17ba11472c35f18b7760b4f5a5fc50b9625f9b5a22cf626144b5816132db565b6144bd6132e5565b6001600160a01b031663f4993018836040518263ffffffff1660e01b81526004016133a691815260200190565b5f61131f825490565b7f9586321ac05f110e4b4a0a42aba899709345af0ca78910e8832ddfd71fed2bf461451d816132db565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635888454e6145546132e5565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024015f604051808303815f87803b158015613656575f80fd5b6001600160a01b0383166145e0576040516356e4289360e01b81526020600482015260146024820152732fb737b232a7b832b930ba37b926b0b730b3b2b960611b6044820152606401611439565b6145ea8482614bf3565b6145f382613cb6565b61460a5f80516020615a4883398151915284613758565b506146225f80516020615a4883398151915280614cf3565b6146597f24633f096579ea9942f9f4de94a59d0fd90e22436d62e831fc27ea6f0cbba5a05f80516020615a48833981519152614cf3565b6118a77fe0b9915a7819e810f29b50730662441fec3443eb363b7e7c90c77fada416f2765f80516020615a48833981519152614cf3565b7f49fd147dcd655645beca3a7658e351ec884fb6dfffceb007c33e3cb733af2adb6146ba816132db565b6146c26132e5565b60405163f2c098b760e01b81526001600160a01b038481166004830152919091169063f2c098b7906024016133a6565b7f355caf1c2580ed8185acb5ea3573b71f85186b41bdf69e3eb8f1fcd122a562df61471c816132db565b6147246132e5565b60405163f3fef3a360e01b81526001600160a01b03858116600483015260248201859052919091169063f3fef3a390604401612de9565b604051631920845160e01b8152600481018290525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690631920845190602401602060405180830381865afa1580156147c0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906147e491906154a4565b604051636d78045960e01b81529091506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636d780459906148579033907f0000000000000000000000000000000000000000000000000000000000000000908690600401615805565b6020604051808303815f875af1158015614873573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061489791906154a4565b5061163c81613e68565b6148ab828261293d565b61163c5760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401611439565b60605f6148f2602d6001600160a01b0385163b6155bf565b6001600160401b03811115614909576149096152e2565b6040519080825280601f01601f191660200182016040528015614933576020820181803683370190505b5090508051602d60208301853c92915050565b5f8060205f8451602086015f885af180614965576040513d5f823e3d81fd5b50505f513d9150811561497c578060011415614989565b6001600160a01b0384163b155b156118a757604051635274afe760e01b81526001600160a01b0385166004820152602401611439565b5f6149bb6126d2565b6149c36132e5565b6001600160a01b031663d4c3eea06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156149fe573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614a2291906154a4565b61134891906155bf565b5f614a37838361293d565b614ab4575f838152602081815260408083206001600160a01b03861684529091529020805460ff19166001179055614a6c3390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a450600161131f565b505f61131f565b5f612936836001600160a01b038416614d3d565b6118a784856001600160a01b03166323b872dd8686866040516024016135bb93929190615805565b5f614b02838361293d565b15614ab4575f838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a450600161131f565b5f612936836001600160a01b038416614d82565b5f825f018281548110614b8957614b8961544b565b905f5260205f200154905092915050565b6060815f01805480602002602001604051908101604052809291908181526020018280548015614be757602002820191905f5260205f20905b815481526020019060010190808311614bd3575b50505050509050919050565b60045460ff1615614c165760405162dc149f60e41b815260040160405180910390fd5b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163003614c5f5760405163f9cc8ff960e01b815260040160405180910390fd5b6004805460ff191660011790556001600160a01b038216614cb3576040516356e4289360e01b815260206004820152600d60248201526c2fb232b330bab63a20b236b4b760991b6044820152606401611439565b614cbd5f83613758565b50614cc781613b33565b6040517f5daa87a0e9463431830481fd4b6e3403442dfb9a12b9c07597e9f61d50b633c8905f90a15050565b5f82815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b5f818152600183016020526040812054614ab457508154600181810184555f84815260208082209093018490558454848252828601909352604090209190915561131f565b5f8181526001830160205260408120548015614e5c575f614da46001836155bf565b85549091505f90614db7906001906155bf565b9050808214614e16575f865f018281548110614dd557614dd561544b565b905f5260205f200154905080875f018481548110614df557614df561544b565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080614e2757614e27615a33565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f90556001935050505061131f565b5f91505061131f565b5092915050565b5f60208284031215614e7c575f80fd5b81356001600160e01b031981168114612936575f80fd5b5f8083601f840112614ea3575f80fd5b5081356001600160401b03811115614eb9575f80fd5b6020830191508360208260051b8501011115614ed3575f80fd5b9250929050565b5f8060208385031215614eeb575f80fd5b82356001600160401b03811115614f00575f80fd5b614f0c85828601614e93565b90969095509350505050565b5f60208284031215614f28575f80fd5b5035919050565b5f8083601f840112614f3f575f80fd5b5081356001600160401b03811115614f55575f80fd5b602083019150836020828501011115614ed3575f80fd5b5f8060208385031215614f7d575f80fd5b82356001600160401b03811115614f92575f80fd5b614f0c85828601614f2f565b6001600160a01b038116811461162f575f80fd5b5f805f60608486031215614fc4575f80fd5b8335614fcf81614f9e565b92506020840135614fdf81614f9e565b929592945050506040919091013590565b5f8060408385031215615001575f80fd5b82359150602083013561501381614f9e565b809150509250929050565b5f805f805f60608688031215615032575f80fd5b85356001600160401b0380821115615048575f80fd5b61505489838a01614f2f565b9097509550602088013591508082111561506c575f80fd5b5061507988828901614e93565b909450925050604086013561508d81614f9e565b809150509295509295909350565b5f80602083850312156150ac575f80fd5b82356001600160401b03808211156150c2575f80fd5b818501915085601f8301126150d5575f80fd5b8135818111156150e3575f80fd5b8660208260061b85010111156150f7575f80fd5b60209290920196919550909350505050565b5f806040838503121561511a575f80fd5b823561512581614f9e565b946020939093013593505050565b5f805f60408486031215615145575f80fd5b83356001600160401b0381111561515a575f80fd5b61516686828701614f2f565b909450925050602084013561517a81614f9e565b809150509250925092565b5f6101008201905060018060a01b03835116825260208301516001600160601b038082166020850152806040860151166040850152505061ffff606084015116606083015260808301516151df608084018261ffff169052565b5060a08301516151f560a084018261ffff169052565b5060c083015161520960c084018215159052565b5060e0830151614e6560e08401826001600160601b03169052565b602080825282518282018190525f9190848201906040850190845b8181101561525b5783518352928401929184019160010161523f565b50909695505050505050565b5f8060408385031215615278575f80fd5b50508035926020909101359150565b5f60208284031215615297575f80fd5b813561293681614f9e565b602080825282518282018190525f9190848201906040850190845b8181101561525b5783516001600160a01b0316835292840192918401916001016152bd565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171561531e5761531e6152e2565b604052919050565b5f8060408385031215615337575f80fd5b82356001600160401b038082111561534d575f80fd5b818501915085601f830112615360575f80fd5b8135602082821115615374576153746152e2565b615386601f8301601f191682016152f6565b9250818352878183860101111561539b575f80fd5b81818501828501375f91830181019190915290969401359450505050565b5f805f80608085870312156153cc575f80fd5b84356153d781614f9e565b935060208501356153e781614f9e565b93969395505050506040820135916060013590565b5f805f6060848603121561540e575f80fd5b833561541981614f9e565b925060208401359150604084013561517a81614f9e565b5f60208284031215615440575f80fd5b815161293681614f9e565b634e487b7160e01b5f52603260045260245ffd5b5f8235607e19833603018112615473575f80fd5b9190910192915050565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561131f5761131f61547d565b5f602082840312156154b4575f80fd5b5051919050565b5f808335601e198436030181126154d0575f80fd5b8301803591506001600160401b038211156154e9575f80fd5b602001915036819003821315614ed3575f80fd5b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f61553860808301888a6154fd565b602083820381850152875180835280828a018385015e5f838201830152601f01601f1916909101838103820160408501529061557782820187896154fd565b92505050826060830152979650505050505050565b818382375f9101908152919050565b6020808252600a908201526917dc9958da5c1a595b9d60b21b604082015260600190565b8181038181111561131f5761131f61547d565b808202811582820484141761131f5761131f61547d565b5f8261560357634e487b7160e01b5f52601260045260245ffd5b500490565b5f600182016156195761561961547d565b5060010190565b838152604060208201525f613cad6040830184866154fd565b80516001600160601b038116811461564f575f80fd5b919050565b805161ffff8116811461564f575f80fd5b8051801515811461564f575f80fd5b5f610100808385031215615686575f80fd5b604051908101906001600160401b03821181831017156156a8576156a86152e2565b81604052835191506156b982614f9e565b8181526156c860208501615639565b60208201526156d960408501615639565b60408201526156ea60608501615654565b60608201526156fb60808501615654565b608082015261570c60a08501615654565b60a082015261571d60c08501615665565b60c082015261572e60e08501615639565b60e0820152949350505050565b6001600160401b038116811461162f575f80fd5b5f6060828403121561575f575f80fd5b604051606081018181106001600160401b0382111715615781576157816152e2565b60405282516001600160801b038116811461579a575f80fd5b81526020830151600f81900b81146157b0575f80fd5b602082015260408301516157c38161573b565b60408201529392505050565b600f82810b9082900b036f7fffffffffffffffffffffffffffffff19811260016001607f1b038213171561131f5761131f61547d565b6001600160a01b039384168152919092166020820152604081019190915260600190565b5f60208284031215615839575f80fd5b61293682615665565b602081525f6118606020830184866154fd565b803561564f8161573b565b606081525f6158736060830187896154fd565b828103602084810191909152858252869181015f5b878110156158b657833561589b8161573b565b6001600160401b031682529282019290820190600101615888565b506001600160a01b03959095166040949094019390935250919695505050505050565b604081525f6158ec6040830185876154fd565b905060018060a01b0383166020830152949350505050565b8183525f6001600160fb1b0383111561591b575f80fd5b8260051b80836020870137939093016020019392505050565b5f808335601e19843603018112615949575f80fd5b83016020810192503590506001600160401b03811115615967575f80fd5b803603821315614ed3575f80fd5b604081525f8335601e1985360301811261598d575f80fd5b84016020810190356001600160401b038111156159a8575f80fd5b8060051b36038213156159b9575f80fd5b608060408501526159ce60c085018284615904565b9150506159de6020860186615934565b848303603f190160608601526159f58382846154fd565b9250505060408501356080840152615a0f60608601615855565b6001600160401b031660a08401526001600160a01b03841660208401529050612936565b634e487b7160e01b5f52603160045260245ffdfe59783a4ae82167eefad593739a5430c1d9e896a16c35f1e5285ddd0c0980885c438f659bbe6931765fa05a3ab0be40c2b232d9c41e4db269dd64082747ee9452a2646970667358221220b42cbf4395f779ce5a62d9cb7fbb55680bc7930c86d72c24e5f3486352d125a964736f6c6343000819003300000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e1000000000000000000000000dfd55388020a8cedadce0b177df5ef1e11553b43000000000000000000000000dfa0b34f28b1b6735d2df150a99048139302a80e
Deployed Bytecode
0x608060405260043610610516575f3560e01c80636d47556611610299578063be807b2811610160578063e6c772d4116100c9578063f3fef3a311610083578063f3fef3a3146111bb578063f634dcbd146111da578063f6c76318146111f9578063faa1d4411461120f578063fd84b78514611242578063ff108ccb14611257575f80fd5b8063e6c772d4146110e4578063e9a9c850146110f8578063eb990c591461112b578063f06dce031461114a578063f0e9fcd11461117d578063f2c098b71461119c575f80fd5b8063d547741f1161011a578063d547741f14610fc7578063d9fb643a14610fe6578063dd46706414611019578063e00bfe5014611038578063e02023a11461106b578063e359c77f1461109e575f80fd5b8063be807b2814610f07578063c5ced5bb14610f3a578063c6728c6c14610f4d578063c70cb12014610f61578063ca15c87314610f94578063d4c3eea014610fb3575f80fd5b806397f51afe11610202578063a3246ad3116101bc578063a3246ad314610e45578063aa9f2d6a14610e71578063b35869ed14610e85578063b60d428814610eb8578063b930908f14610ec0578063bddf300c14610ef3575f80fd5b806397f51afe14610d8357806399ab9e0b14610da25780639aeb533314610dc1578063a0f3422a14610de0578063a217fddf14610dff578063a2ed81b014610e12575f80fd5b80638860be41116102535780638860be4114610cd65780638c2b499514610cea5780639010d07c14610d1d57806391d1485414610d3c57806392e0303614610d5b5780639563110514610d6f575f80fd5b80636d47556614610bf557806370ba285714610c285780637b108d9214610c3c57806382b7e7b914610c5d5780638322fff214610c90578063853c637d14610cb7575f80fd5b806335763d2b116103dd57806350823a491161034657806361404235116103005780636140423514610ac0578063626c5ebc14610ade578063699d79bd14610b115780636aa2930b14610b445780636ab1583614610bae5780636b1a00d514610be1575f80fd5b806350823a49146109f457806350d99dd414610a15578063528c198a14610a285780635680e14514610a3b5780635bc7136314610a6e5780635eaaf1d514610aa1575f80fd5b80633e619a6c116103975780633e619a6c1461093d5780633e8fe40e146109505780633efd85971461096f5780634156bbf41461098e57806343c71057146109ad5780634bc36840146109e0575f80fd5b806335763d2b1461084d57806336568abe1461086c5780633a04d4f31461088b5780633a284985146108ab5780633be75aa3146108de5780633c56eb471461090a575f80fd5b80631e73cad31161047f5780632c0772b4116104395780632c0772b4146107825780632f286b1b146107b55780632f2ff15d146107e857806332cd7ebf1461080757806332ffa14f1461082657806333ac88b41461083a575f80fd5b80631e73cad3146106c6578063221ed94d146106da578063248a9ca3146106f957806324e7964a146107275780632806f5e01461073b5780632bb115ce1461074f575f80fd5b80630da541b8116104d05780630da541b8146105f0578063107415521461060f5780631171bda91461062e578063158ef93e1461064d578063196b9e06146106665780631a296f40146106b1575f80fd5b806301ffc9a71461052a5780630857593b1461055e5780630a4a3ed2146105805780630b91990d1461059f5780630bff8881146105b35780630d7fcd1a146105c9575f80fd5b366105265761052434611276565b005b5f80fd5b348015610535575f80fd5b50610549610544366004614e6c565b6112fb565b60405190151581526020015b60405180910390f35b348015610569575f80fd5b50610572611325565b604051908152602001610555565b34801561058b575f80fd5b5061057261059a366004614eda565b61134d565b3480156105aa575f80fd5b50600354610572565b3480156105be575f80fd5b5061057262278d0081565b3480156105d4575f80fd5b506105dd611614565b60405161ffff9091168152602001610555565b3480156105fb575f80fd5b5061052461060a366004614f18565b611626565b34801561061a575f80fd5b50610524610629366004614f6c565b611632565b348015610639575f80fd5b50610524610648366004614fb2565b611640565b348015610658575f80fd5b506004546105499060ff1681565b348015610671575f80fd5b506106997f000000000000000000000000dfa0b34f28b1b6735d2df150a99048139302a80e81565b6040516001600160a01b039091168152602001610555565b3480156106bc575f80fd5b5061057260085481565b3480156106d1575f80fd5b50610524611816565b3480156106e5575f80fd5b506105726106f4366004614f18565b611820565b348015610704575f80fd5b50610572610713366004614f18565b5f9081526020819052604090206001015490565b348015610732575f80fd5b50610699611868565b348015610746575f80fd5b506105dd611871565b34801561075a575f80fd5b506105727fec7606002753a83f3033806d5596d2cf315f8f3836c68f2fabbbc3ed9b6f1ac481565b34801561078d575f80fd5b506105727fa90c7030a27f389f9fc8ed21a0556f40c88130cc14a80db936bed68261819b2c81565b3480156107c0575f80fd5b506105727faf5678186f680e15e8b3e8a8679bdd61c0eb1aaded90254c3e1b5b6b3fed62af81565b3480156107f3575f80fd5b50610524610802366004614ff0565b611883565b348015610812575f80fd5b50610524610821366004614f18565b6118ad565b348015610831575f80fd5b506105726118b6565b61052461084836600461501e565b6118c0565b348015610858575f80fd5b5061052461086736600461509b565b6118d4565b348015610877575f80fd5b50610524610886366004614ff0565b611972565b348015610896575f80fd5b506105725f80516020615a4883398151915281565b3480156108b6575f80fd5b506105727fa38b301640bddfd3e6a9d2a11d13551d53ef81526347ff09d798738fcc5a49d481565b3480156108e9575f80fd5b506108f26119a5565b6040516001600160601b039091168152602001610555565b348015610915575f80fd5b506105727fd0d885b3f66154d15e918afc9fcd5eb37aea7fbf993c32b18a84a0be97d1327681565b61052461094b366004615109565b6119b7565b34801561095b575f80fd5b5061052461096a366004614f18565b611c6a565b34801561097a575f80fd5b50610524610989366004614f18565b611cda565b348015610999575f80fd5b506105246109a8366004615133565b611f68565b3480156109b8575f80fd5b506105727f59d005e32db662b94335d6bedfeb453fd2202b9f0cc7a6ed498d9098171744b081565b3480156109eb575f80fd5b506108f2611f73565b3480156109ff575f80fd5b50610a08611f85565b6040516105559190615185565b610524610a23366004615109565b612063565b610524610a36366004615109565b6122e2565b348015610a46575f80fd5b506105727f3f82ecf462ddac43fc17ba11472c35f18b7760b4f5a5fc50b9625f9b5a22cf6281565b348015610a79575f80fd5b506105727f933b7d5c112a4d05b489cea0b2ced98acb27d3d0fc9827c92cdacb2d6c5559c281565b348015610aac575f80fd5b50610524610abb366004614f18565b612453565b348015610acb575f80fd5b506105726a084595161401484a00000081565b348015610ae9575f80fd5b506105727fb850402129bccae797798069a8cf3147a0cb7c3193f70558a75f7df0b8651c3081565b348015610b1c575f80fd5b506105727f32d0d6546e21c13ff633616141dc9daad87d248d1d37c56bf493d06d627ecb7b81565b348015610b4f575f80fd5b50600654600754610b7d916001600160801b03811691600160801b909104600f0b906001600160401b031683565b604080516001600160801b039094168452600f9290920b60208401526001600160401b031690820152606001610555565b348015610bb9575f80fd5b506105727fea19d3b23bd90fdd52445ad672f2b6fb1fef7230d49c6a827c1cd288d02994d581565b348015610bec575f80fd5b506105726126d2565b348015610c00575f80fd5b506105727f9586321ac05f110e4b4a0a42aba899709345af0ca78910e8832ddfd71fed2bf481565b348015610c33575f80fd5b506105246127da565b348015610c47575f80fd5b50610c506127e2565b6040516105559190615224565b348015610c68575f80fd5b506105727f24633f096579ea9942f9f4de94a59d0fd90e22436d62e831fc27ea6f0cbba5a081565b348015610c9b575f80fd5b5061069973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b348015610cc2575f80fd5b50610524610cd1366004614f18565b612853565b348015610ce1575f80fd5b506105dd61290d565b348015610cf5575f80fd5b506105727f956303b84cebe7f6d4c573121d1d147af67b9f1048630d8dba50a3e6f41a738c81565b348015610d28575f80fd5b50610699610d37366004615267565b61291f565b348015610d47575f80fd5b50610549610d56366004614ff0565b61293d565b348015610d66575f80fd5b50610524612965565b348015610d7a575f80fd5b5061052461296d565b348015610d8e575f80fd5b50610524610d9d366004614eda565b612975565b348015610dad575f80fd5b50610524610dbc366004615287565b61297f565b348015610dcc575f80fd5b50610524610ddb366004615267565b612988565b348015610deb575f80fd5b50610524610dfa366004615287565b612c76565b348015610e0a575f80fd5b506105725f81565b348015610e1d575f80fd5b506105727f12721d7ec843176aec149b06f8f88034866537c4b8a2fc38014f2daaba6f156381565b348015610e50575f80fd5b50610e64610e5f366004614f18565b612e00565b60405161055591906152a2565b348015610e7c575f80fd5b50610524612e19565b348015610e90575f80fd5b506105727fe0b9915a7819e810f29b50730662441fec3443eb363b7e7c90c77fada416f27681565b610524612e21565b348015610ecb575f80fd5b506105727f689f0a569be0c9b6cd2c11c81cb0add722272abdae6b649fdb1e05f1d9bb8a2f81565b348015610efe575f80fd5b50610524612e2a565b348015610f12575f80fd5b506105727f17960a6b137243c888669d93712b617414dd6d1ab55d5eca488ccb1d0894cd5c81565b610524610f48366004614f18565b612e32565b348015610f58575f80fd5b50610572612e4a565b348015610f6c575f80fd5b506105727fea6487df651bb740150364c496e1c7403dd62063c96e44906cc98c6a919a9d8881565b348015610f9f575f80fd5b50610572610fae366004614f18565b612f4e565b348015610fbe575f80fd5b50610572612f64565b348015610fd2575f80fd5b50610524610fe1366004614ff0565b612fcc565b348015610ff1575f80fd5b506106997f000000000000000000000000dfd55388020a8cedadce0b177df5ef1e11553b4381565b348015611024575f80fd5b50610524611033366004614f18565b612ff0565b348015611043575f80fd5b506106997f00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e181565b348015611076575f80fd5b506105727f355caf1c2580ed8185acb5ea3573b71f85186b41bdf69e3eb8f1fcd122a562df81565b3480156110a9575f80fd5b506105726110b8366004615326565b81516020818401810180516002825292820194820194909420919093529091525f908152604090205481565b3480156110ef575f80fd5b50610524612ff9565b348015611103575f80fd5b506105727fe996ac9b332538bb1fa3cd6743aa47011623cdb94bd964a494ee9d371e4a27d381565b348015611136575f80fd5b506105246111453660046153b9565b613001565b348015611155575f80fd5b506105727f6a77c9119dc50d7dc957a0b6bec3e9899530f9a3df66bb8addb7f073f678f2f281565b348015611188575f80fd5b506105246111973660046153fc565b6130bd565b3480156111a7575f80fd5b506105246111b6366004615287565b6131f0565b3480156111c6575f80fd5b506105246111d5366004615109565b6131f9565b3480156111e5575f80fd5b506105246111f436600461509b565b613239565b348015611204575f80fd5b506105726201518081565b34801561121a575f80fd5b506105727f49fd147dcd655645beca3a7658e351ec884fb6dfffceb007c33e3cb733af2adb81565b34801561124d575f80fd5b5061057260055481565b348015611262575f80fd5b50610524611271366004614f18565b6132d2565b7f933b7d5c112a4d05b489cea0b2ced98acb27d3d0fc9827c92cdacb2d6c5559c26112a0816132db565b6112a86132e5565b6001600160a01b031663b60d4288836040518263ffffffff1660e01b81526004015f604051808303818588803b1580156112e0575f80fd5b505af11580156112f2573d5f803e3d5ffd5b50505050505050565b5f6001600160e01b03198216635a05180f60e01b148061131f575061131f826132fa565b92915050565b5f6113486113316132e5565b6001600160a01b031631611343612e4a565b61332e565b905090565b5f806113576132e5565b90505f816001600160a01b0316636b96736b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611396573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113ba9190615430565b90505f5b84811015611402578585828181106113d8576113d861544b565b90506020028101906113ea919061545f565b6113f8906040013585615491565b93506001016113be565b5061140b612e4a565b831115611442578261141b612e4a565b604051632cbc004760e01b8152600481019290925260248201526044015b60405180910390fd5b61144b83613343565b6114618360085461145c9190615491565b6133d7565b5f826001600160a01b0316634cd79e0a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561149e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114c291906154a4565b6040516020016114d491815260200190565b6040516020818303038152906040529050365f5b86811015611609578787828181106115025761150261544b565b9050602002810190611514919061545f565b91506001600160a01b0384166322895118604084013561153485806154bb565b8761154260208901896154bb565b89606001356040518863ffffffff1660e01b815260040161156896959493929190615525565b5f604051808303818588803b15801561157f575f80fd5b505af1158015611591573d5f803e3d5ffd5b506115a493508592508291506154bb9050565b6040516115b292919061558c565b6040518091039020856001600160a01b03167f76dc34440571fb7e9534cdb59b8d71754696637cad774d6fa401da9b674861cf84604001356040516115f991815260200190565b60405180910390a36001016114e8565b505050505092915050565b5f61161d611f85565b60a00151905090565b61162f8161343c565b50565b61163c828261352f565b5050565b7fa38b301640bddfd3e6a9d2a11d13551d53ef81526347ff09d798738fcc5a49d461166a816132db565b6001600160a01b0384166116aa576040516356e4289360e01b81526020600482015260066024820152652fba37b5b2b760d11b6044820152606401611439565b6001600160a01b0383166116d1576040516356e4289360e01b81526004016114399061559b565b815f0361170b576040516356e4289360e01b815260206004820152600760248201526617d85b5bdd5b9d60ca1b6044820152606401611439565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b038516016117b8575f836001600160a01b0316836040515f6040518083038185875af1925050503d805f8114611779576040519150601f19603f3d011682016040523d82523d5f602084013e61177e565b606091505b50509050806117b257604051636f54afdd60e11b81526001600160a01b038516600482015260248101849052604401611439565b506117c3565b6117c384848461358e565b836001600160a01b0316836001600160a01b03167faca8fb252cde442184e5f10e0f2e6e4029e8cd7717cae63559079610702436aa8460405161180891815260200190565b60405180910390a350505050565b61181e6135ed565b565b5f8061182b83613668565b90505f611836611f85565b602001516001600160601b031690508082101561185657505f9392505050565b61186081836155bf565b949350505050565b5f6113486132e5565b5f61187a611f85565b60800151905090565b5f8281526020819052604090206001015461189d816132db565b6118a78383613758565b50505050565b61162f8161378b565b5f6113485f613668565b6118cd858585858561398c565b5050505050565b5f819003611914576040516356e4289360e01b815260206004820152600c60248201526b5f61737369676e6d656e747360a01b6044820152606401611439565b5f5b8181101561196d576119658383838181106119335761193361544b565b9050604002016020013584848481811061194f5761194f61544b565b610fe19260206040909202019081019150615287565b600101611916565b505050565b6001600160a01b038116331461199b5760405163334bd91960e11b815260040160405180910390fd5b61196d8282613a28565b5f6119ae611f85565b60400151905090565b34156119c6576119c634611276565b805f6119d0611f85565b90505f816060015161ffff166127106119e991906155bf565b6127107f00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e16001600160a01b0316630103349c8686602001516001600160601b0316611a349190615491565b6040518263ffffffff1660e01b8152600401611a5291815260200190565b602060405180830381865afa158015611a6d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a9191906154a4565b611a9b91906155d2565b611aa591906155e9565b9050611aaf6132e5565b6001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa158015611aea573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b0e91906154a4565b811115611b1e57611b1e81613a53565b611b283085613ab2565b6040516240cd2760e21b8152600481018590525f907f00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e16001600160a01b031690630103349c90602401602060405180830381865afa158015611b8c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bb091906154a4565b604051630ea598cb60e41b8152600481018290529091505f906001600160a01b037f000000000000000000000000dfd55388020a8cedadce0b177df5ef1e11553b43169063ea598cb0906024016020604051808303815f875af1158015611c19573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c3d91906154a4565b90506112f27f000000000000000000000000dfd55388020a8cedadce0b177df5ef1e11553b43888361358e565b7fe0b9915a7819e810f29b50730662441fec3443eb363b7e7c90c77fada416f276611c94816132db565b5f82600854611ca39190615491565b90506a084595161401484a000000811115611cd15760405163f579fde760e01b815260040160405180910390fd5b61196d816133d7565b611ce26127e2565b80515f03611d035760405163f814a95b60e01b815260040160405180910390fd5b80515f80826001600160401b03811115611d1f57611d1f6152e2565b604051908082528060200260200182016040528015611d48578160200160208202803683370190505b5090505f8060035442611d5b9190615491565b90505f5b85811015611e4f575f878281518110611d7a57611d7a61544b565b60200260200101519050611d8e813361293d565b15611e02576001935085611da181615608565b9650506001858381518110611db857611db861544b565b60200260200101901515908115158152505080336001600160a01b03165f80516020615a68833981519152855f36604051611df593929190615620565b60405180910390a3611e46565b4260025f36604051611e1592919061558c565b90815260200160405180910390205f8381526020019081526020015f205410611e465785611e4281615608565b9650505b50600101611d5f565b5081611e6e5760405163dd16cb2360e01b815260040160405180910390fd5b848403611ee0575f5b85811015611ed1575f878281518110611e9257611e9261544b565b6020026020010151905060025f36604051611eae92919061558c565b90815260408051602092819003830190205f938452909152812055600101611e77565b50611edb87613b33565b6112f2565b5f5b85811015611f5e57838181518110611efc57611efc61544b565b602002602001015115611f56575f878281518110611f1c57611f1c61544b565b602002602001015190508260025f36604051611f3992919061558c565b90815260408051602092819003830190205f948552909152909120555b600101611ee2565b5050505050505050565b6118a7838383613bac565b5f611f7c611f85565b60200151905090565b60408051610100810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101919091527f000000000000000000000000dfa0b34f28b1b6735d2df150a99048139302a80e6001600160a01b031663efe98d85611ffc6132e5565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240161010060405180830381865afa15801561203f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113489190615674565b34156120725761207234611276565b604051631920845160e01b8152600481018290527f00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e16001600160a01b031690631920845190602401602060405180830381865afa1580156120d5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120f991906154a4565b5f612102611f85565b90505f816060015161ffff1661271061211b91906155bf565b6127107f00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e16001600160a01b0316630103349c8686602001516001600160601b03166121669190615491565b6040518263ffffffff1660e01b815260040161218491815260200190565b602060405180830381865afa15801561219f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121c391906154a4565b6121cd91906155d2565b6121d791906155e9565b90506121e16132e5565b6001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa15801561221c573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061224091906154a4565b8111156122505761225081613a53565b604051631920845160e01b8152600481018590526118cd9086906001600160a01b037f00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e11690631920845190602401602060405180830381865afa1580156122b9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122dd91906154a4565b613ab2565b34156122f1576122f134611276565b805f6122fb611f85565b90505f816060015161ffff1661271061231491906155bf565b6127107f00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e16001600160a01b0316630103349c8686602001516001600160601b031661235f9190615491565b6040518263ffffffff1660e01b815260040161237d91815260200190565b602060405180830381865afa158015612398573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123bc91906154a4565b6123c691906155d2565b6123d091906155e9565b90506123da6132e5565b6001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa158015612415573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061243991906154a4565b8111156124495761244981613a53565b6118cd8585613ab2565b61245b6127e2565b80515f0361247c5760405163f814a95b60e01b815260040160405180910390fd5b80515f80826001600160401b03811115612498576124986152e2565b6040519080825280602002602001820160405280156124c1578160200160208202803683370190505b5090505f80600354426124d49190615491565b90505f5b858110156125c8575f8782815181106124f3576124f361544b565b60200260200101519050612507813361293d565b1561257b57600193508561251a81615608565b96505060018583815181106125315761253161544b565b60200260200101901515908115158152505080336001600160a01b03165f80516020615a68833981519152855f3660405161256e93929190615620565b60405180910390a36125bf565b4260025f3660405161258e92919061558c565b90815260200160405180910390205f8381526020019081526020015f2054106125bf57856125bb81615608565b9650505b506001016124d8565b50816125e75760405163dd16cb2360e01b815260040160405180910390fd5b848403612654575f5b8581101561264a575f87828151811061260b5761260b61544b565b6020026020010151905060025f3660405161262792919061558c565b90815260408051602092819003830190205f9384529091528120556001016125f0565b50611edb87613cb6565b5f5b85811015611f5e578381815181106126705761267061544b565b6020026020010151156126ca575f8782815181106126905761269061544b565b602002602001015190508260025f366040516126ad92919061558c565b90815260408051602092819003830190205f948552909152909120555b600101612656565b5f806126dc6132e5565b6001600160a01b0316636af6eeed6040518163ffffffff1660e01b8152600401606060405180830381865afa158015612717573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061273b919061574f565b600854600680546020840151939450909260016001607f1b03909216915f91839161277091600160801b9004600f0b906157cf565b84548651612787916001600160801b0316906157cf565b61279191906157cf565b61279b91906157cf565b90505f81600f0b136127ad575f6127d1565b612710600554826001600160801b03166127c791906155d2565b6127d191906155e9565b94505050505090565b61181e613dff565b60408051600280825260608083018452926020830190803683370190505090505f801b815f815181106128175761281761544b565b6020026020010181815250505f80516020615a48833981519152816001815181106128445761284461544b565b60200260200101818152505090565b604051636d78045960e01b81526001600160a01b037f00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e11690636d780459906128c39033907f000000000000000000000000dfa0b34f28b1b6735d2df150a99048139302a80e908690600401615805565b6020604051808303815f875af11580156128df573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061290391906154a4565b5061162f81613e68565b5f612916611f85565b60600151905090565b5f8281526001602052604081206129369083613ec9565b9392505050565b5f918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b61181e613ed4565b61181e613f3d565b61163c8282613fa6565b61162f816140d2565b6129906127e2565b80515f036129b15760405163f814a95b60e01b815260040160405180910390fd5b80515f80826001600160401b038111156129cd576129cd6152e2565b6040519080825280602002602001820160405280156129f6578160200160208202803683370190505b5090505f8060035442612a099190615491565b90505f5b85811015612afd575f878281518110612a2857612a2861544b565b60200260200101519050612a3c813361293d565b15612ab0576001935085612a4f81615608565b9650506001858381518110612a6657612a6661544b565b60200260200101901515908115158152505080336001600160a01b03165f80516020615a68833981519152855f36604051612aa393929190615620565b60405180910390a3612af4565b4260025f36604051612ac392919061558c565b90815260200160405180910390205f8381526020019081526020015f205410612af45785612af081615608565b9650505b50600101612a0d565b5081612b1c5760405163dd16cb2360e01b815260040160405180910390fd5b848403612bed575f5b85811015612b7f575f878281518110612b4057612b4061544b565b6020026020010151905060025f36604051612b5c92919061558c565b90815260408051602092819003830190205f938452909152812055600101612b25565b508660085414612bb35760085460405160016273f97f60e11b03198152600481019190915260248101889052604401611439565b6a084595161401484a000000881115612bdf5760405163f579fde760e01b815260040160405180910390fd5b612be8886133d7565b611f5e565b5f5b85811015612c6b57838181518110612c0957612c0961544b565b602002602001015115612c63575f878281518110612c2957612c2961544b565b602002602001015190508260025f36604051612c4692919061558c565b90815260408051602092819003830190205f948552909152909120555b600101612bef565b505050505050505050565b7f24633f096579ea9942f9f4de94a59d0fd90e22436d62e831fc27ea6f0cbba5a0612ca0816132db565b6001600160a01b038216612cc7576040516356e4289360e01b81526004016114399061559b565b5f612cd06126d2565b9050805f03612cf257604051633e2d4cff60e11b815260040160405180910390fd5b60085415612d0357612d035f6133d7565b612d0b6132e5565b6001600160a01b0316636af6eeed6040518163ffffffff1660e01b8152600401606060405180830381865afa158015612d46573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d6a919061574f565b805160208201516001600160801b03908116600160801b0291161760065560400151600780546001600160401b0390921667ffffffffffffffff19909216919091179055612db66132e5565b60405163f3fef3a360e01b81526001600160a01b03858116600483015260248201849052919091169063f3fef3a3906044015b5f604051808303815f87803b1580156112e0575f80fd5b5f81815260016020526040902060609061131f906143ad565b61181e6143b9565b61181e34611276565b61181e614422565b3415612e4157612e4134611276565b61162f8161448b565b5f80612e546132e5565b90505f612e5f6126d2565b826001600160a01b031663cf3090126040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e9b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ebf91906154a4565b612ec99190615491565b90505f826001600160a01b031663d4c3eea06040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f08573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f2c91906154a4565b9050808211612f4457612f3f82826155bf565b612f46565b5f5b935050505090565b5f81815260016020526040812061131f906144ea565b5f612f6d6132e5565b6001600160a01b031663d4c3eea06040518163ffffffff1660e01b8152600401602060405180830381865afa158015612fa8573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061134891906154a4565b5f82815260208190526040902060010154612fe6816132db565b6118a78383613a28565b61162f81613a53565b61181e6144f3565b61300d84848484614592565b60405163095ea7b360e01b81526001600160a01b037f000000000000000000000000dfd55388020a8cedadce0b177df5ef1e11553b43811660048301525f1960248301527f00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e1169063095ea7b3906044016020604051808303815f875af1158015613099573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118cd9190615829565b7fa38b301640bddfd3e6a9d2a11d13551d53ef81526347ff09d798738fcc5a49d46130e7816132db565b6001600160a01b038416613127576040516356e4289360e01b81526020600482015260066024820152652fba37b5b2b760d11b6044820152606401611439565b6001600160a01b03821661314e576040516356e4289360e01b81526004016114399061559b565b604051632142170760e11b81526001600160a01b038516906342842e0e9061317e90309086908890600401615805565b5f604051808303815f87803b158015613195575f80fd5b505af11580156131a7573d5f803e3d5ffd5b50505050836001600160a01b0316826001600160a01b03167f6a30e6784464f0d1f4158aa4cb65ae9239b0fa87c7f2c083ee6dde44ba97b5e68560405161180891815260200190565b61162f81614690565b5f613202612e4a565b90508082111561322f57604051632cbc004760e01b81526004810183905260248101829052604401611439565b61196d83836146f2565b5f819003613279576040516356e4289360e01b815260206004820152600c60248201526b5f61737369676e6d656e747360a01b6044820152606401611439565b5f5b8181101561196d576132ca8383838181106132985761329861544b565b905060400201602001358484848181106132b4576132b461544b565b6108029260206040909202019081019150615287565b60010161327b565b61162f8161475b565b61162f81336148a1565b5f806132f0306148da565b6020015192915050565b5f6001600160e01b03198216637965db0b60e01b148061131f57506301ffc9a760e01b6001600160e01b031983161461131f565b5f81831061333c5781612936565b5090919050565b7fea6487df651bb740150364c496e1c7403dd62063c96e44906cc98c6a919a9d8861336d816132db565b6133756132e5565b60405163f3fef3a360e01b8152306004820152602481018490526001600160a01b03919091169063f3fef3a3906044015b5f604051808303815f87803b1580156133bd575f80fd5b505af11580156133cf573d5f803e3d5ffd5b505050505050565b6008548082036133fa576040516318a04aa160e01b815260040160405180910390fd5b600882905560408051838152602081018390527f04f4f631361c0cb55bafac3cc7c5b167edc0a7284003c4d780a4ee6ed00f52b6910160405180910390a15050565b7fd0d885b3f66154d15e918afc9fcd5eb37aea7fbf993c32b18a84a0be97d13276613466816132db565b7f000000000000000000000000dfa0b34f28b1b6735d2df150a99048139302a80e6001600160a01b031663b9a038286040518163ffffffff1660e01b8152600401602060405180830381865afa1580156134c2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134e69190615430565b6001600160a01b03166341d5467c6134fc6132e5565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018590526044016133a6565b7f32d0d6546e21c13ff633616141dc9daad87d248d1d37c56bf493d06d627ecb7b613559816132db565b6135616132e5565b6001600160a01b0316631074155284846040518363ffffffff1660e01b8152600401612de9929190615842565b6040516001600160a01b0383811660248301526044820183905261196d91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050614946565b7f6a77c9119dc50d7dc957a0b6bec3e9899530f9a3df66bb8addb7f073f678f2f2613617816132db565b61361f6132e5565b6001600160a01b0316631e73cad36040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613656575f80fd5b505af11580156118cd573d5f803e3d5ffd5b5f80612710613675611f85565b606001516136899061ffff166127106155bf565b846136926149b2565b61369c9190615491565b6136a691906155d2565b6136b091906155e9565b604051631920845160e01b815260048101829052909150612936906001600160a01b037f00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e11690631920845190602401602060405180830381865afa15801561371a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061373e91906154a4565b613746611f85565b604001516001600160601b031661332e565b5f806137648484614a2c565b90508015612936575f8481526001602052604090206137839084614abb565b509392505050565b6137b77f000000000000000000000000dfd55388020a8cedadce0b177df5ef1e11553b43333084614acf565b604051636f074d1f60e11b8152600481018290525f907f000000000000000000000000dfd55388020a8cedadce0b177df5ef1e11553b436001600160a01b03169063de0e9a3e906024016020604051808303815f875af115801561381d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061384191906154a4565b604051631920845160e01b8152600481018290529091505f906001600160a01b037f00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e11690631920845190602401602060405180830381865afa1580156138a9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906138cd91906154a4565b604051638fcb4e5b60e01b81526001600160a01b037f000000000000000000000000dfa0b34f28b1b6735d2df150a99048139302a80e81166004830152602482018390529192507f00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e190911690638fcb4e5b906044016020604051808303815f875af115801561395e573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061398291906154a4565b5061196d81613e68565b7fea19d3b23bd90fdd52445ad672f2b6fb1fef7230d49c6a827c1cd288d02994d56139b6816132db565b6139be6132e5565b6001600160a01b03166333ac88b43488888888886040518763ffffffff1660e01b81526004016139f2959493929190615860565b5f604051808303818588803b158015613a09575f80fd5b505af1158015613a1b573d5f803e3d5ffd5b5050505050505050505050565b5f80613a348484614af7565b90508015612936575f8481526001602052604090206137839084614b60565b7faf5678186f680e15e8b3e8a8679bdd61c0eb1aaded90254c3e1b5b6b3fed62af613a7d816132db565b613a856132e5565b6001600160a01b031663dd467064836040518263ffffffff1660e01b81526004016133a691815260200190565b7fe996ac9b332538bb1fa3cd6743aa47011623cdb94bd964a494ee9d371e4a27d3613adc816132db565b7f000000000000000000000000dfa0b34f28b1b6735d2df150a99048139302a80e6001600160a01b031663bf8ba596613b136132e5565b85856040518463ffffffff1660e01b8152600401612de993929190615805565b62015180811080613b46575062278d0081115b15613b645760405163bbc3a3e360e01b815260040160405180910390fd5b6003805490829055604080518281526020810184905233917f437dc1a3e26abf0b8381463abca48d1e7d322e803c2b160d3ee75c04a492a18291015b60405180910390a25050565b5f7f17960a6b137243c888669d93712b617414dd6d1ab55d5eca488ccb1d0894cd5c613bd7816132db565b613bdf6132e5565b6001600160a01b031663c7c4ff466040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c1a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613c3e9190615430565b6001600160a01b0316634380df4f8686866040518463ffffffff1660e01b8152600401613c6d939291906158d9565b6020604051808303815f875af1158015613c89573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613cad91906154a4565b95945050505050565b612710811115613cd95760405163137b7b8960e31b815260040160405180910390fd5b5f613ce26126d2565b1115613d0157604051638e09a5b360e01b815260040160405180910390fd5b60055480158015613d1157505f82115b15613dc257613d1e6132e5565b6001600160a01b0316636af6eeed6040518163ffffffff1660e01b8152600401606060405180830381865afa158015613d59573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613d7d919061574f565b805160208201516001600160801b03908116600160801b0291161760065560400151600780546001600160401b0390921667ffffffffffffffff199092169190911790555b6005829055604080518281526020810184905233917ffbbf61b0478977353f50313e99775188b5d61eef9a35657660db41b38e5d1b619101613ba0565b7f956303b84cebe7f6d4c573121d1d147af67b9f1048630d8dba50a3e6f41a738c613e29816132db565b613e316132e5565b6001600160a01b03166370ba28576040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613656575f80fd5b7f689f0a569be0c9b6cd2c11c81cb0add722272abdae6b649fdb1e05f1d9bb8a2f613e92816132db565b7f000000000000000000000000dfa0b34f28b1b6735d2df150a99048139302a80e6001600160a01b031663ee7a7c046134fc6132e5565b5f6129368383614b74565b7f12721d7ec843176aec149b06f8f88034866537c4b8a2fc38014f2daaba6f1563613efe816132db565b613f066132e5565b6001600160a01b03166392e030366040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613656575f80fd5b7f59d005e32db662b94335d6bedfeb453fd2202b9f0cc7a6ed498d9098171744b0613f67816132db565b613f6f6132e5565b6001600160a01b031663956311056040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613656575f80fd5b7fb850402129bccae797798069a8cf3147a0cb7c3193f70558a75f7df0b8651c30613fd0816132db565b5f613fd96132e5565b90505f816001600160a01b031663c7c4ff466040518163ffffffff1660e01b8152600401602060405180830381865afa158015614018573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061403c9190615430565b90505f5b848110156133cf57816001600160a01b0316630fef9cdf8787848181106140695761406961544b565b905060200281019061407b919061545f565b856040518363ffffffff1660e01b8152600401614099929190615975565b5f604051808303815f87803b1580156140b0575f80fd5b505af11580156140c2573d5f803e3d5ffd5b5050600190920191506140409050565b6140da6127e2565b80515f036140fb5760405163f814a95b60e01b815260040160405180910390fd5b80515f80826001600160401b03811115614117576141176152e2565b604051908082528060200260200182016040528015614140578160200160208202803683370190505b5090505f80600354426141539190615491565b90505f5b85811015614247575f8782815181106141725761417261544b565b60200260200101519050614186813361293d565b156141fa57600193508561419981615608565b96505060018583815181106141b0576141b061544b565b60200260200101901515908115158152505080336001600160a01b03165f80516020615a68833981519152855f366040516141ed93929190615620565b60405180910390a361423e565b4260025f3660405161420d92919061558c565b90815260200160405180910390205f8381526020019081526020015f20541061423e578561423a81615608565b9650505b50600101614157565b50816142665760405163dd16cb2360e01b815260040160405180910390fd5b84840361432f575f5b858110156142c9575f87828151811061428a5761428a61544b565b6020026020010151905060025f366040516142a692919061558c565b90815260408051602092819003830190205f93845290915281205560010161426f565b506142d26132e5565b60405163f2fde38b60e01b81526001600160a01b038981166004830152919091169063f2fde38b906024015f604051808303815f87803b158015614314575f80fd5b505af1158015614326573d5f803e3d5ffd5b505050506112f2565b5f5b85811015611f5e5783818151811061434b5761434b61544b565b6020026020010151156143a5575f87828151811061436b5761436b61544b565b602002602001015190508260025f3660405161438892919061558c565b90815260408051602092819003830190205f948552909152909120555b600101614331565b60605f61293683614b9a565b7fec7606002753a83f3033806d5596d2cf315f8f3836c68f2fabbbc3ed9b6f1ac46143e3816132db565b6143eb6132e5565b6001600160a01b031663aa9f2d6a6040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613656575f80fd5b7fa90c7030a27f389f9fc8ed21a0556f40c88130cc14a80db936bed68261819b2c61444c816132db565b6144546132e5565b6001600160a01b031663bddf300c6040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613656575f80fd5b7f3f82ecf462ddac43fc17ba11472c35f18b7760b4f5a5fc50b9625f9b5a22cf626144b5816132db565b6144bd6132e5565b6001600160a01b031663f4993018836040518263ffffffff1660e01b81526004016133a691815260200190565b5f61131f825490565b7f9586321ac05f110e4b4a0a42aba899709345af0ca78910e8832ddfd71fed2bf461451d816132db565b7f000000000000000000000000dfa0b34f28b1b6735d2df150a99048139302a80e6001600160a01b0316635888454e6145546132e5565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024015f604051808303815f87803b158015613656575f80fd5b6001600160a01b0383166145e0576040516356e4289360e01b81526020600482015260146024820152732fb737b232a7b832b930ba37b926b0b730b3b2b960611b6044820152606401611439565b6145ea8482614bf3565b6145f382613cb6565b61460a5f80516020615a4883398151915284613758565b506146225f80516020615a4883398151915280614cf3565b6146597f24633f096579ea9942f9f4de94a59d0fd90e22436d62e831fc27ea6f0cbba5a05f80516020615a48833981519152614cf3565b6118a77fe0b9915a7819e810f29b50730662441fec3443eb363b7e7c90c77fada416f2765f80516020615a48833981519152614cf3565b7f49fd147dcd655645beca3a7658e351ec884fb6dfffceb007c33e3cb733af2adb6146ba816132db565b6146c26132e5565b60405163f2c098b760e01b81526001600160a01b038481166004830152919091169063f2c098b7906024016133a6565b7f355caf1c2580ed8185acb5ea3573b71f85186b41bdf69e3eb8f1fcd122a562df61471c816132db565b6147246132e5565b60405163f3fef3a360e01b81526001600160a01b03858116600483015260248201859052919091169063f3fef3a390604401612de9565b604051631920845160e01b8152600481018290525f907f00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e16001600160a01b031690631920845190602401602060405180830381865afa1580156147c0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906147e491906154a4565b604051636d78045960e01b81529091506001600160a01b037f00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e11690636d780459906148579033907f000000000000000000000000dfa0b34f28b1b6735d2df150a99048139302a80e908690600401615805565b6020604051808303815f875af1158015614873573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061489791906154a4565b5061163c81613e68565b6148ab828261293d565b61163c5760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401611439565b60605f6148f2602d6001600160a01b0385163b6155bf565b6001600160401b03811115614909576149096152e2565b6040519080825280601f01601f191660200182016040528015614933576020820181803683370190505b5090508051602d60208301853c92915050565b5f8060205f8451602086015f885af180614965576040513d5f823e3d81fd5b50505f513d9150811561497c578060011415614989565b6001600160a01b0384163b155b156118a757604051635274afe760e01b81526001600160a01b0385166004820152602401611439565b5f6149bb6126d2565b6149c36132e5565b6001600160a01b031663d4c3eea06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156149fe573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614a2291906154a4565b61134891906155bf565b5f614a37838361293d565b614ab4575f838152602081815260408083206001600160a01b03861684529091529020805460ff19166001179055614a6c3390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a450600161131f565b505f61131f565b5f612936836001600160a01b038416614d3d565b6118a784856001600160a01b03166323b872dd8686866040516024016135bb93929190615805565b5f614b02838361293d565b15614ab4575f838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a450600161131f565b5f612936836001600160a01b038416614d82565b5f825f018281548110614b8957614b8961544b565b905f5260205f200154905092915050565b6060815f01805480602002602001604051908101604052809291908181526020018280548015614be757602002820191905f5260205f20905b815481526020019060010190808311614bd3575b50505050509050919050565b60045460ff1615614c165760405162dc149f60e41b815260040160405180910390fd5b6001600160a01b037f0000000000000000000000005667f7477325f85c1b5e324387545c5045a57e2b163003614c5f5760405163f9cc8ff960e01b815260040160405180910390fd5b6004805460ff191660011790556001600160a01b038216614cb3576040516356e4289360e01b815260206004820152600d60248201526c2fb232b330bab63a20b236b4b760991b6044820152606401611439565b614cbd5f83613758565b50614cc781613b33565b6040517f5daa87a0e9463431830481fd4b6e3403442dfb9a12b9c07597e9f61d50b633c8905f90a15050565b5f82815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b5f818152600183016020526040812054614ab457508154600181810184555f84815260208082209093018490558454848252828601909352604090209190915561131f565b5f8181526001830160205260408120548015614e5c575f614da46001836155bf565b85549091505f90614db7906001906155bf565b9050808214614e16575f865f018281548110614dd557614dd561544b565b905f5260205f200154905080875f018481548110614df557614df561544b565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080614e2757614e27615a33565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f90556001935050505061131f565b5f91505061131f565b5092915050565b5f60208284031215614e7c575f80fd5b81356001600160e01b031981168114612936575f80fd5b5f8083601f840112614ea3575f80fd5b5081356001600160401b03811115614eb9575f80fd5b6020830191508360208260051b8501011115614ed3575f80fd5b9250929050565b5f8060208385031215614eeb575f80fd5b82356001600160401b03811115614f00575f80fd5b614f0c85828601614e93565b90969095509350505050565b5f60208284031215614f28575f80fd5b5035919050565b5f8083601f840112614f3f575f80fd5b5081356001600160401b03811115614f55575f80fd5b602083019150836020828501011115614ed3575f80fd5b5f8060208385031215614f7d575f80fd5b82356001600160401b03811115614f92575f80fd5b614f0c85828601614f2f565b6001600160a01b038116811461162f575f80fd5b5f805f60608486031215614fc4575f80fd5b8335614fcf81614f9e565b92506020840135614fdf81614f9e565b929592945050506040919091013590565b5f8060408385031215615001575f80fd5b82359150602083013561501381614f9e565b809150509250929050565b5f805f805f60608688031215615032575f80fd5b85356001600160401b0380821115615048575f80fd5b61505489838a01614f2f565b9097509550602088013591508082111561506c575f80fd5b5061507988828901614e93565b909450925050604086013561508d81614f9e565b809150509295509295909350565b5f80602083850312156150ac575f80fd5b82356001600160401b03808211156150c2575f80fd5b818501915085601f8301126150d5575f80fd5b8135818111156150e3575f80fd5b8660208260061b85010111156150f7575f80fd5b60209290920196919550909350505050565b5f806040838503121561511a575f80fd5b823561512581614f9e565b946020939093013593505050565b5f805f60408486031215615145575f80fd5b83356001600160401b0381111561515a575f80fd5b61516686828701614f2f565b909450925050602084013561517a81614f9e565b809150509250925092565b5f6101008201905060018060a01b03835116825260208301516001600160601b038082166020850152806040860151166040850152505061ffff606084015116606083015260808301516151df608084018261ffff169052565b5060a08301516151f560a084018261ffff169052565b5060c083015161520960c084018215159052565b5060e0830151614e6560e08401826001600160601b03169052565b602080825282518282018190525f9190848201906040850190845b8181101561525b5783518352928401929184019160010161523f565b50909695505050505050565b5f8060408385031215615278575f80fd5b50508035926020909101359150565b5f60208284031215615297575f80fd5b813561293681614f9e565b602080825282518282018190525f9190848201906040850190845b8181101561525b5783516001600160a01b0316835292840192918401916001016152bd565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171561531e5761531e6152e2565b604052919050565b5f8060408385031215615337575f80fd5b82356001600160401b038082111561534d575f80fd5b818501915085601f830112615360575f80fd5b8135602082821115615374576153746152e2565b615386601f8301601f191682016152f6565b9250818352878183860101111561539b575f80fd5b81818501828501375f91830181019190915290969401359450505050565b5f805f80608085870312156153cc575f80fd5b84356153d781614f9e565b935060208501356153e781614f9e565b93969395505050506040820135916060013590565b5f805f6060848603121561540e575f80fd5b833561541981614f9e565b925060208401359150604084013561517a81614f9e565b5f60208284031215615440575f80fd5b815161293681614f9e565b634e487b7160e01b5f52603260045260245ffd5b5f8235607e19833603018112615473575f80fd5b9190910192915050565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561131f5761131f61547d565b5f602082840312156154b4575f80fd5b5051919050565b5f808335601e198436030181126154d0575f80fd5b8301803591506001600160401b038211156154e9575f80fd5b602001915036819003821315614ed3575f80fd5b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f61553860808301888a6154fd565b602083820381850152875180835280828a018385015e5f838201830152601f01601f1916909101838103820160408501529061557782820187896154fd565b92505050826060830152979650505050505050565b818382375f9101908152919050565b6020808252600a908201526917dc9958da5c1a595b9d60b21b604082015260600190565b8181038181111561131f5761131f61547d565b808202811582820484141761131f5761131f61547d565b5f8261560357634e487b7160e01b5f52601260045260245ffd5b500490565b5f600182016156195761561961547d565b5060010190565b838152604060208201525f613cad6040830184866154fd565b80516001600160601b038116811461564f575f80fd5b919050565b805161ffff8116811461564f575f80fd5b8051801515811461564f575f80fd5b5f610100808385031215615686575f80fd5b604051908101906001600160401b03821181831017156156a8576156a86152e2565b81604052835191506156b982614f9e565b8181526156c860208501615639565b60208201526156d960408501615639565b60408201526156ea60608501615654565b60608201526156fb60808501615654565b608082015261570c60a08501615654565b60a082015261571d60c08501615665565b60c082015261572e60e08501615639565b60e0820152949350505050565b6001600160401b038116811461162f575f80fd5b5f6060828403121561575f575f80fd5b604051606081018181106001600160401b0382111715615781576157816152e2565b60405282516001600160801b038116811461579a575f80fd5b81526020830151600f81900b81146157b0575f80fd5b602082015260408301516157c38161573b565b60408201529392505050565b600f82810b9082900b036f7fffffffffffffffffffffffffffffff19811260016001607f1b038213171561131f5761131f61547d565b6001600160a01b039384168152919092166020820152604081019190915260600190565b5f60208284031215615839575f80fd5b61293682615665565b602081525f6118606020830184866154fd565b803561564f8161573b565b606081525f6158736060830187896154fd565b828103602084810191909152858252869181015f5b878110156158b657833561589b8161573b565b6001600160401b031682529282019290820190600101615888565b506001600160a01b03959095166040949094019390935250919695505050505050565b604081525f6158ec6040830185876154fd565b905060018060a01b0383166020830152949350505050565b8183525f6001600160fb1b0383111561591b575f80fd5b8260051b80836020870137939093016020019392505050565b5f808335601e19843603018112615949575f80fd5b83016020810192503590506001600160401b03811115615967575f80fd5b803603821315614ed3575f80fd5b604081525f8335601e1985360301811261598d575f80fd5b84016020810190356001600160401b038111156159a8575f80fd5b8060051b36038213156159b9575f80fd5b608060408501526159ce60c085018284615904565b9150506159de6020860186615934565b848303603f190160608601526159f58382846154fd565b9250505060408501356080840152615a0f60608601615855565b6001600160401b031660a08401526001600160a01b03841660208401529050612936565b634e487b7160e01b5f52603160045260245ffdfe59783a4ae82167eefad593739a5430c1d9e896a16c35f1e5285ddd0c0980885c438f659bbe6931765fa05a3ab0be40c2b232d9c41e4db269dd64082747ee9452a2646970667358221220b42cbf4395f779ce5a62d9cb7fbb55680bc7930c86d72c24e5f3486352d125a964736f6c63430008190033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e1000000000000000000000000dfd55388020a8cedadce0b177df5ef1e11553b43000000000000000000000000dfa0b34f28b1b6735d2df150a99048139302a80e
-----Decoded View---------------
Arg [0] : _stETH (address): 0x04d160820C0f2E2C693D9Eb26078189D10A1a3e1
Arg [1] : _wstETH (address): 0xDFD55388020a8CEDADCe0B177DF5EF1E11553b43
Arg [2] : _vaultHub (address): 0xDfA0B34F28b1b6735d2df150a99048139302a80E
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 00000000000000000000000004d160820c0f2e2c693d9eb26078189d10a1a3e1
Arg [1] : 000000000000000000000000dfd55388020a8cedadce0b177df5ef1e11553b43
Arg [2] : 000000000000000000000000dfa0b34f28b1b6735d2df150a99048139302a80e
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.