Code for 0xcc77…64dd
Since block 21354329
Verified contract
- {{
- "language": "Solidity",
- "sources": {
- "@openzeppelin/contracts/interfaces/draft-IERC1822.sol": {
- "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified\n * proxy whose upgrades are fully controlled by the current implementation.\n */\ninterface IERC1822Proxiable {\n /**\n * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation\n * address.\n *\n * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks\n * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this\n * function revert if invoked through a proxy.\n */\n function proxiableUUID() external view returns (bytes32);\n}\n"
- },
- "@openzeppelin/contracts/proxy/beacon/IBeacon.sol": {
- "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev This is the interface that {BeaconProxy} expects of its beacon.\n */\ninterface IBeacon {\n /**\n * @dev Must return an address that can be used as a delegate call target.\n *\n * {BeaconProxy} will check that this address is a contract.\n */\n function implementation() external view returns (address);\n}\n"
- },
- "@openzeppelin/contracts/proxy/Clones.sol": {
- "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/Clones.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for\n * deploying minimal proxy contracts, also known as \"clones\".\n *\n * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies\n * > a minimal bytecode implementation that delegates all calls to a known, fixed address.\n *\n * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`\n * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the\n * deterministic method.\n *\n * _Available since v3.4._\n */\nlibrary Clones {\n /**\n * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.\n *\n * This function uses the create opcode, which should never revert.\n */\n function clone(address implementation) internal returns (address instance) {\n assembly {\n let ptr := mload(0x40)\n mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)\n mstore(add(ptr, 0x14), shl(0x60, implementation))\n mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)\n instance := create(0, ptr, 0x37)\n }\n require(instance != address(0), \"ERC1167: create failed\");\n }\n\n /**\n * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.\n *\n * This function uses the create2 opcode and a `salt` to deterministically deploy\n * the clone. Using the same `implementation` and `salt` multiple time will revert, since\n * the clones cannot be deployed twice at the same address.\n */\n function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {\n assembly {\n let ptr := mload(0x40)\n mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)\n mstore(add(ptr, 0x14), shl(0x60, implementation))\n mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)\n instance := create2(0, ptr, 0x37, salt)\n }\n require(instance != address(0), \"ERC1167: create2 failed\");\n }\n\n /**\n * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.\n */\n function predictDeterministicAddress(\n address implementation,\n bytes32 salt,\n address deployer\n ) internal pure returns (address predicted) {\n assembly {\n let ptr := mload(0x40)\n mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)\n mstore(add(ptr, 0x14), shl(0x60, implementation))\n mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)\n mstore(add(ptr, 0x38), shl(0x60, deployer))\n mstore(add(ptr, 0x4c), salt)\n mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))\n predicted := keccak256(add(ptr, 0x37), 0x55)\n }\n }\n\n /**\n * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.\n */\n function predictDeterministicAddress(address implementation, bytes32 salt)\n internal\n view\n returns (address predicted)\n {\n return predictDeterministicAddress(implementation, salt, address(this));\n }\n}\n"
- },
- "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol": {
- "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../Proxy.sol\";\nimport \"./ERC1967Upgrade.sol\";\n\n/**\n * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an\n * implementation address that can be changed. This address is stored in storage in the location specified by\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the\n * implementation behind the proxy.\n */\ncontract ERC1967Proxy is Proxy, ERC1967Upgrade {\n /**\n * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.\n *\n * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded\n * function call, and allows initializating the storage of the proxy like a Solidity constructor.\n */\n constructor(address _logic, bytes memory _data) payable {\n assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256(\"eip1967.proxy.implementation\")) - 1));\n _upgradeToAndCall(_logic, _data, false);\n }\n\n /**\n * @dev Returns the current implementation address.\n */\n function _implementation() internal view virtual override returns (address impl) {\n return ERC1967Upgrade._getImplementation();\n }\n}\n"
- },
- "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol": {
- "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../beacon/IBeacon.sol\";\nimport \"../../interfaces/draft-IERC1822.sol\";\nimport \"../../utils/Address.sol\";\nimport \"../../utils/StorageSlot.sol\";\n\n/**\n * @dev This abstract contract provides getters and event emitting update functions for\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.\n *\n * _Available since v4.1._\n *\n * @custom:oz-upgrades-unsafe-allow delegatecall\n */\nabstract contract ERC1967Upgrade {\n // This is the keccak-256 hash of \"eip1967.proxy.rollback\" subtracted by 1\n bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Emitted when the implementation is upgraded.\n */\n event Upgraded(address indexed implementation);\n\n /**\n * @dev Returns the current implementation address.\n */\n function _getImplementation() internal view returns (address) {\n return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n }\n\n /**\n * @dev Stores a new address in the EIP1967 implementation slot.\n */\n function _setImplementation(address newImplementation) private {\n require(Address.isContract(newImplementation), \"ERC1967: new implementation is not a contract\");\n StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n }\n\n /**\n * @dev Perform implementation upgrade\n *\n * Emits an {Upgraded} event.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Perform implementation upgrade with additional setup call.\n *\n * Emits an {Upgraded} event.\n */\n function _upgradeToAndCall(\n address newImplementation,\n bytes memory data,\n bool forceCall\n ) internal {\n _upgradeTo(newImplementation);\n if (data.length > 0 || forceCall) {\n Address.functionDelegateCall(newImplementation, data);\n }\n }\n\n /**\n * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.\n *\n * Emits an {Upgraded} event.\n */\n function _upgradeToAndCallUUPS(\n address newImplementation,\n bytes memory data,\n bool forceCall\n ) internal {\n // Upgrades from old implementations will perform a rollback test. This test requires the new\n // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing\n // this special case will break upgrade paths from old UUPS implementation to new ones.\n if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {\n _setImplementation(newImplementation);\n } else {\n try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {\n require(slot == _IMPLEMENTATION_SLOT, \"ERC1967Upgrade: unsupported proxiableUUID\");\n } catch {\n revert(\"ERC1967Upgrade: new implementation is not UUPS\");\n }\n _upgradeToAndCall(newImplementation, data, forceCall);\n }\n }\n\n /**\n * @dev Storage slot with the admin of the contract.\n * This is the keccak-256 hash of \"eip1967.proxy.admin\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n /**\n * @dev Emitted when the admin account has changed.\n */\n event AdminChanged(address previousAdmin, address newAdmin);\n\n /**\n * @dev Returns the current admin.\n */\n function _getAdmin() internal view returns (address) {\n return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;\n }\n\n /**\n * @dev Stores a new address in the EIP1967 admin slot.\n */\n function _setAdmin(address newAdmin) private {\n require(newAdmin != address(0), \"ERC1967: new admin is the zero address\");\n StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;\n }\n\n /**\n * @dev Changes the admin of the proxy.\n *\n * Emits an {AdminChanged} event.\n */\n function _changeAdmin(address newAdmin) internal {\n emit AdminChanged(_getAdmin(), newAdmin);\n _setAdmin(newAdmin);\n }\n\n /**\n * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.\n * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.\n */\n bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;\n\n /**\n * @dev Emitted when the beacon is upgraded.\n */\n event BeaconUpgraded(address indexed beacon);\n\n /**\n * @dev Returns the current beacon.\n */\n function _getBeacon() internal view returns (address) {\n return StorageSlot.getAddressSlot(_BEACON_SLOT).value;\n }\n\n /**\n * @dev Stores a new beacon in the EIP1967 beacon slot.\n */\n function _setBeacon(address newBeacon) private {\n require(Address.isContract(newBeacon), \"ERC1967: new beacon is not a contract\");\n require(\n Address.isContract(IBeacon(newBeacon).implementation()),\n \"ERC1967: beacon implementation is not a contract\"\n );\n StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;\n }\n\n /**\n * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does\n * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).\n *\n * Emits a {BeaconUpgraded} event.\n */\n function _upgradeBeaconToAndCall(\n address newBeacon,\n bytes memory data,\n bool forceCall\n ) internal {\n _setBeacon(newBeacon);\n emit BeaconUpgraded(newBeacon);\n if (data.length > 0 || forceCall) {\n Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);\n }\n }\n}\n"
- },
- "@openzeppelin/contracts/proxy/Proxy.sol": {
- "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (proxy/Proxy.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM\n * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to\n * be specified by overriding the virtual {_implementation} function.\n *\n * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a\n * different contract through the {_delegate} function.\n *\n * The success and return data of the delegated call will be returned back to the caller of the proxy.\n */\nabstract contract Proxy {\n /**\n * @dev Delegates the current call to `implementation`.\n *\n * This function does not return to its internal call site, it will return directly to the external caller.\n */\n function _delegate(address implementation) internal virtual {\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function\n * and {_fallback} should delegate.\n */\n function _implementation() internal view virtual returns (address);\n\n /**\n * @dev Delegates the current call to the address returned by `_implementation()`.\n *\n * This function does not return to its internall call site, it will return directly to the external caller.\n */\n function _fallback() internal virtual {\n _beforeFallback();\n _delegate(_implementation());\n }\n\n /**\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other\n * function in the contract matches the call data.\n */\n fallback() external payable virtual {\n _fallback();\n }\n\n /**\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data\n * is empty.\n */\n receive() external payable virtual {\n _fallback();\n }\n\n /**\n * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`\n * call, or as part of the Solidity `fallback` or `receive` functions.\n *\n * If overriden should call `super._beforeFallback()`.\n */\n function _beforeFallback() internal virtual {}\n}\n"
- },
- "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol": {
- "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../ERC1967/ERC1967Proxy.sol\";\n\n/**\n * @dev This contract implements a proxy that is upgradeable by an admin.\n *\n * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector\n * clashing], which can potentially be used in an attack, this contract uses the\n * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two\n * things that go hand in hand:\n *\n * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if\n * that call matches one of the admin functions exposed by the proxy itself.\n * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the\n * implementation. If the admin tries to call a function on the implementation it will fail with an error that says\n * \"admin cannot fallback to proxy target\".\n *\n * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing\n * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due\n * to sudden errors when trying to call a function from the proxy implementation.\n *\n * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,\n * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.\n */\ncontract TransparentUpgradeableProxy is ERC1967Proxy {\n /**\n * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and\n * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.\n */\n constructor(\n address _logic,\n address admin_,\n bytes memory _data\n ) payable ERC1967Proxy(_logic, _data) {\n assert(_ADMIN_SLOT == bytes32(uint256(keccak256(\"eip1967.proxy.admin\")) - 1));\n _changeAdmin(admin_);\n }\n\n /**\n * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.\n */\n modifier ifAdmin() {\n if (msg.sender == _getAdmin()) {\n _;\n } else {\n _fallback();\n }\n }\n\n /**\n * @dev Returns the current admin.\n *\n * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.\n *\n * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the\n * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.\n * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`\n */\n function admin() external ifAdmin returns (address admin_) {\n admin_ = _getAdmin();\n }\n\n /**\n * @dev Returns the current implementation.\n *\n * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.\n *\n * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the\n * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.\n * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`\n */\n function implementation() external ifAdmin returns (address implementation_) {\n implementation_ = _implementation();\n }\n\n /**\n * @dev Changes the admin of the proxy.\n *\n * Emits an {AdminChanged} event.\n *\n * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.\n */\n function changeAdmin(address newAdmin) external virtual ifAdmin {\n _changeAdmin(newAdmin);\n }\n\n /**\n * @dev Upgrade the implementation of the proxy.\n *\n * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.\n */\n function upgradeTo(address newImplementation) external ifAdmin {\n _upgradeToAndCall(newImplementation, bytes(\"\"), false);\n }\n\n /**\n * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified\n * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the\n * proxied contract.\n *\n * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {\n _upgradeToAndCall(newImplementation, data, true);\n }\n\n /**\n * @dev Returns the current admin.\n */\n function _admin() internal view virtual returns (address) {\n return _getAdmin();\n }\n\n /**\n * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.\n */\n function _beforeFallback() internal virtual override {\n require(msg.sender != _getAdmin(), \"TransparentUpgradeableProxy: admin cannot fallback to proxy target\");\n super._beforeFallback();\n }\n}\n"
- },
- "@openzeppelin/contracts/utils/Address.sol": {
- "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n"
- },
- "@openzeppelin/contracts/utils/StorageSlot.sol": {
- "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for reading and writing primitive types to specific storage slots.\n *\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\n * This library helps with reading and writing to such slots without the need for inline assembly.\n *\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\n *\n * Example usage to set ERC1967 implementation slot:\n * ```\n * contract ERC1967 {\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n *\n * function _getImplementation() internal view returns (address) {\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n * }\n *\n * function _setImplementation(address newImplementation) internal {\n * require(Address.isContract(newImplementation), \"ERC1967: new implementation is not a contract\");\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n * }\n * }\n * ```\n *\n * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._\n */\nlibrary StorageSlot {\n struct AddressSlot {\n address value;\n }\n\n struct BooleanSlot {\n bool value;\n }\n\n struct Bytes32Slot {\n bytes32 value;\n }\n\n struct Uint256Slot {\n uint256 value;\n }\n\n /**\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\n */\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\n */\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\n */\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\n */\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\n assembly {\n r.slot := slot\n }\n }\n}\n"
- },
- "src/contracts/AuthorizedFeeRecipient.sol": {
- "content": "//SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.8.10;\n\nimport \"./interfaces/IFeeDispatcher.sol\";\nimport \"./libs/DispatchersStorageLib.sol\";\nimport \"./interfaces/IFeeRecipient.sol\";\n\ncontract AuthorizedFeeRecipient is IFeeRecipient {\n /// @notice Constructor replay prevention\n bool internal initialized;\n /// @notice Address where funds are sent to be dispatched\n IFeeDispatcher internal dispatcher;\n /// @notice Public Key root assigned to this receiver\n bytes32 internal publicKeyRoot;\n /// @notice Address of the staking contract\n address internal stakingContract;\n\n error AlreadyInitialized();\n error Unauthorized();\n\n /// @notice Initializes the receiver\n /// @param _dispatcher Address that will handle the fee dispatching\n /// @param _publicKeyRoot Public Key root assigned to this receiver\n function init(address _dispatcher, bytes32 _publicKeyRoot) external {\n if (initialized) {\n revert AlreadyInitialized();\n }\n initialized = true;\n dispatcher = IFeeDispatcher(_dispatcher);\n publicKeyRoot = _publicKeyRoot;\n stakingContract = msg.sender; // The staking contract always calls init\n }\n\n /// @notice Empty calldata fallback\n receive() external payable {}\n\n /// @notice Non-empty calldata fallback\n fallback() external payable {}\n\n /// @notice Triggers a withdrawal by sending its funds + its public key root to the dispatcher\n /// @dev Can be called only be called through the staking contract\n function withdraw() external {\n if (msg.sender != stakingContract) {\n revert Unauthorized();\n }\n dispatcher.dispatch{value: address(this).balance}(publicKeyRoot);\n }\n\n /// @notice Retrieve the assigned public key root\n function getPublicKeyRoot() external view returns (bytes32) {\n return publicKeyRoot;\n }\n\n /// @notice retrieve the assigned withdrawer\n function getWithdrawer() external view returns (address) {\n return dispatcher.getWithdrawer(publicKeyRoot);\n }\n}\n"
- },
- "src/contracts/ConsensusLayerFeeDispatcher.sol": {
- "content": "//SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.8.10;\n\nimport \"./libs/DispatchersStorageLib.sol\";\nimport \"./interfaces/IStakingContractFeeDetails.sol\";\nimport \"./interfaces/IFeeDispatcher.sol\";\n\n/// @title Consensus Layer Fee Recipient\n/// @author Kiln\n/// @notice This contract can be used to receive fees from a validator and split them with a node operator\ncontract ConsensusLayerFeeDispatcher is IFeeDispatcher {\n using DispatchersStorageLib for bytes32;\n\n event Withdrawal(\n address indexed withdrawer,\n address indexed feeRecipient,\n bytes32 pubKeyRoot,\n uint256 rewards,\n uint256 nodeOperatorFee,\n uint256 treasuryFee\n );\n\n error TreasuryReceiveError(bytes errorData);\n error FeeRecipientReceiveError(bytes errorData);\n error WithdrawerReceiveError(bytes errorData);\n error ZeroBalanceWithdrawal();\n error AlreadyInitialized();\n error InvalidCall();\n\n bytes32 internal constant STAKING_CONTRACT_ADDRESS_SLOT =\n keccak256(\"ConsensusLayerFeeRecipient.stakingContractAddress\");\n uint256 internal constant BASIS_POINTS = 10_000;\n bytes32 internal constant VERSION_SLOT = keccak256(\"ConsensusLayerFeeRecipient.version\");\n\n /// @notice Ensures an initialisation call has been called only once per _version value\n /// @param _version The current initialisation value\n modifier init(uint256 _version) {\n if (_version != VERSION_SLOT.getUint256() + 1) {\n revert AlreadyInitialized();\n }\n\n VERSION_SLOT.setUint256(_version);\n\n _;\n }\n\n /// @notice Constructor method allowing us to prevent calls to initCLFR by setting the appropriate version\n constructor(uint256 _version) {\n VERSION_SLOT.setUint256(_version);\n }\n\n /// @notice Initialize the contract by storing the staking contract\n /// @param _stakingContract Address of the Staking Contract\n function initCLD(address _stakingContract) external init(1) {\n STAKING_CONTRACT_ADDRESS_SLOT.setAddress(_stakingContract);\n }\n\n /// @notice Performs a withdrawal on this contract's balance\n function dispatch(bytes32 _publicKeyRoot) external payable {\n IStakingContractFeeDetails stakingContract = IStakingContractFeeDetails(\n STAKING_CONTRACT_ADDRESS_SLOT.getAddress()\n );\n\n uint256 balance = address(this).balance; // this has taken into account msg.value\n if (balance == 0) {\n revert ZeroBalanceWithdrawal();\n }\n\n bool exitRequested = stakingContract.getExitRequestedFromRoot(_publicKeyRoot);\n bool withdrawn = stakingContract.getWithdrawnFromPublicKeyRoot(_publicKeyRoot);\n\n uint256 nonExemptBalance = balance;\n\n if (exitRequested && balance >= 31 ether && !withdrawn) {\n // If the skimmed rewards were withdrawn and the validator then underperformed\n // an healthy exit can be slightly lower than 32 ETH\n // We exempt the balance up to 32 ETH, happens only once.\n // !withdrawn prevents this logic being reused to not pay the fee on rewards\n uint256 exemption = nonExemptBalance > 32 ether ? 32 ether : nonExemptBalance;\n nonExemptBalance -= exemption;\n stakingContract.toggleWithdrawnFromPublicKeyRoot(_publicKeyRoot);\n }\n // In case of slashing the exit is not requested we don't exempt anything\n // This is in case of slashing, the staker will be rebated manually\n // A slashed validator may have accumulated enough skimmed rewards to still have a balance > 32 ETH\n // All of this will be taken into account and the staker will be compensated for the commission taken\n // on its principal and the loss according to the SLA described in the Terms&Conditions\n\n uint256 globalFee = (nonExemptBalance * stakingContract.getGlobalFee()) / BASIS_POINTS;\n uint256 operatorFee = (globalFee * stakingContract.getOperatorFee()) / BASIS_POINTS;\n address operator = stakingContract.getOperatorFeeRecipient(_publicKeyRoot);\n address treasury = stakingContract.getTreasury();\n address withdrawer = stakingContract.getWithdrawerFromPublicKeyRoot(_publicKeyRoot);\n\n (bool status, bytes memory data) = withdrawer.call{value: balance - globalFee}(\"\");\n if (status == false) {\n revert WithdrawerReceiveError(data);\n }\n if (globalFee > 0) {\n (status, data) = treasury.call{value: globalFee - operatorFee}(\"\");\n if (status == false) {\n revert TreasuryReceiveError(data);\n }\n }\n if (operatorFee > 0) {\n (status, data) = operator.call{value: operatorFee}(\"\");\n if (status == false) {\n revert FeeRecipientReceiveError(data);\n }\n }\n emit Withdrawal(\n withdrawer,\n operator,\n _publicKeyRoot,\n balance - globalFee,\n operatorFee,\n globalFee - operatorFee\n );\n }\n\n /// @notice Retrieve the staking contract address\n function getStakingContract() external view returns (address) {\n return STAKING_CONTRACT_ADDRESS_SLOT.getAddress();\n }\n\n /// @notice Retrieve the assigned withdrawer for the given public key root\n /// @param _publicKeyRoot Public key root to get the owner\n function getWithdrawer(bytes32 _publicKeyRoot) external view returns (address) {\n IStakingContractFeeDetails stakingContract = IStakingContractFeeDetails(\n STAKING_CONTRACT_ADDRESS_SLOT.getAddress()\n );\n return stakingContract.getWithdrawerFromPublicKeyRoot(_publicKeyRoot);\n }\n\n receive() external payable {\n revert InvalidCall();\n }\n\n fallback() external payable {\n revert InvalidCall();\n }\n}\n"
- },
- "src/contracts/ExecutionLayerFeeDispatcher.sol": {
- "content": "//SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.8.10;\n\nimport \"./libs/DispatchersStorageLib.sol\";\nimport \"./interfaces/IStakingContractFeeDetails.sol\";\nimport \"./interfaces/IFeeDispatcher.sol\";\n\n/// @title Execution Layer Fee Recipient\n/// @author Kiln\n/// @notice This contract can be used to receive fees from a validator and split them with a node operator\ncontract ExecutionLayerFeeDispatcher is IFeeDispatcher {\n using DispatchersStorageLib for bytes32;\n\n event Withdrawal(\n address indexed withdrawer,\n address indexed feeRecipient,\n bytes32 pubKeyRoot,\n uint256 rewards,\n uint256 nodeOperatorFee,\n uint256 treasuryFee\n );\n\n error TreasuryReceiveError(bytes errorData);\n error FeeRecipientReceiveError(bytes errorData);\n error WithdrawerReceiveError(bytes errorData);\n error ZeroBalanceWithdrawal();\n error AlreadyInitialized();\n error InvalidCall();\n\n bytes32 internal constant STAKING_CONTRACT_ADDRESS_SLOT =\n keccak256(\"ExecutionLayerFeeRecipient.stakingContractAddress\");\n uint256 internal constant BASIS_POINTS = 10_000;\n bytes32 internal constant VERSION_SLOT = keccak256(\"ExecutionLayerFeeRecipient.version\");\n\n /// @notice Ensures an initialisation call has been called only once per _version value\n /// @param _version The current initialisation value\n modifier init(uint256 _version) {\n if (_version != VERSION_SLOT.getUint256() + 1) {\n revert AlreadyInitialized();\n }\n\n VERSION_SLOT.setUint256(_version);\n\n _;\n }\n\n /// @notice Constructor method allowing us to prevent calls to initCLFR by setting the appropriate version\n constructor(uint256 _version) {\n VERSION_SLOT.setUint256(_version);\n }\n\n /// @notice Initialize the contract by storing the staking contract and the public key in storage\n /// @param _stakingContract Address of the Staking Contract\n function initELD(address _stakingContract) external init(1) {\n STAKING_CONTRACT_ADDRESS_SLOT.setAddress(_stakingContract);\n }\n\n /// @notice Performs a withdrawal on this contract's balance\n function dispatch(bytes32 _publicKeyRoot) external payable {\n uint256 balance = address(this).balance;\n if (balance == 0) {\n revert ZeroBalanceWithdrawal();\n }\n IStakingContractFeeDetails stakingContract = IStakingContractFeeDetails(\n STAKING_CONTRACT_ADDRESS_SLOT.getAddress()\n );\n address withdrawer = stakingContract.getWithdrawerFromPublicKeyRoot(_publicKeyRoot);\n address operator = stakingContract.getOperatorFeeRecipient(_publicKeyRoot);\n address treasury = stakingContract.getTreasury();\n uint256 globalFee = (balance * stakingContract.getGlobalFee()) / BASIS_POINTS;\n uint256 operatorFee = (globalFee * stakingContract.getOperatorFee()) / BASIS_POINTS;\n\n (bool status, bytes memory data) = withdrawer.call{value: balance - globalFee}(\"\");\n if (status == false) {\n revert WithdrawerReceiveError(data);\n }\n if (globalFee > 0) {\n (status, data) = treasury.call{value: globalFee - operatorFee}(\"\");\n if (status == false) {\n revert TreasuryReceiveError(data);\n }\n }\n if (operatorFee > 0) {\n (status, data) = operator.call{value: operatorFee}(\"\");\n if (status == false) {\n revert FeeRecipientReceiveError(data);\n }\n }\n emit Withdrawal(\n withdrawer,\n operator,\n _publicKeyRoot,\n balance - globalFee,\n operatorFee,\n globalFee - operatorFee\n );\n }\n\n /// @notice Retrieve the staking contract address\n function getStakingContract() external view returns (address) {\n return STAKING_CONTRACT_ADDRESS_SLOT.getAddress();\n }\n\n /// @notice Retrieve the assigned withdrawer for the given public key root\n /// @param _publicKeyRoot Public key root to get the owner\n function getWithdrawer(bytes32 _publicKeyRoot) external view returns (address) {\n IStakingContractFeeDetails stakingContract = IStakingContractFeeDetails(\n STAKING_CONTRACT_ADDRESS_SLOT.getAddress()\n );\n return stakingContract.getWithdrawerFromPublicKeyRoot(_publicKeyRoot);\n }\n\n receive() external payable {\n revert InvalidCall();\n }\n\n fallback() external payable {\n revert InvalidCall();\n }\n}\n"
- },
- "src/contracts/FeeRecipient.sol": {
- "content": "//SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.8.10;\n\nimport \"./interfaces/IFeeDispatcher.sol\";\n\ncontract FeeRecipient {\n /// @notice Constructor replay prevention\n bool internal initialized;\n /// @notice Address where funds are sent to be dispatched\n IFeeDispatcher internal dispatcher;\n /// @notice Public Key root assigned to this receiver\n bytes32 internal publicKeyRoot;\n\n error AlreadyInitialized();\n\n /// @notice Initializes the receiver\n /// @param _dispatcher Address that will handle the fee dispatching\n /// @param _publicKeyRoot Public Key root assigned to this receiver\n function init(address _dispatcher, bytes32 _publicKeyRoot) external {\n if (initialized) {\n revert AlreadyInitialized();\n }\n initialized = true;\n dispatcher = IFeeDispatcher(_dispatcher);\n publicKeyRoot = _publicKeyRoot;\n }\n\n /// @notice Empty calldata fallback\n receive() external payable {}\n\n /// @notice Non-empty calldata fallback\n fallback() external payable {}\n\n /// @notice Triggers a withdrawal by sending its funds + its public key root to the dispatcher\n /// @dev Can be called by any wallet as recipients are not parameters\n function withdraw() external {\n dispatcher.dispatch{value: address(this).balance}(publicKeyRoot);\n }\n\n /// @notice Retrieve the assigned public key root\n function getPublicKeyRoot() external view returns (bytes32) {\n return publicKeyRoot;\n }\n\n /// @notice retrieve the assigned withdrawer\n function getWithdrawer() external view returns (address) {\n return dispatcher.getWithdrawer(publicKeyRoot);\n }\n}\n"
- },
- "src/contracts/interfaces/IDepositContract.sol": {
- "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.10;\n\ninterface IDepositContract {\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawalCredentials,\n bytes calldata signature,\n bytes32 depositDataRoot\n ) external payable;\n}\n"
- },
- "src/contracts/interfaces/IFeeDispatcher.sol": {
- "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.10;\n\ninterface IFeeDispatcher {\n function dispatch(bytes32 _publicKeyRoot) external payable;\n\n function getWithdrawer(bytes32 _publicKeyRoot) external view returns (address);\n}\n"
- },
- "src/contracts/interfaces/IFeeRecipient.sol": {
- "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.10;\n\ninterface IFeeRecipient {\n function init(address _dispatcher, bytes32 _publicKeyRoot) external;\n\n function withdraw() external;\n}\n"
- },
- "src/contracts/interfaces/IStakingContractFeeDetails.sol": {
- "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.10;\n\ninterface IStakingContractFeeDetails {\n function getWithdrawerFromPublicKeyRoot(bytes32 _publicKeyRoot) external view returns (address);\n\n function getTreasury() external view returns (address);\n\n function getOperatorFeeRecipient(bytes32 pubKeyRoot) external view returns (address);\n\n function getGlobalFee() external view returns (uint256);\n\n function getOperatorFee() external view returns (uint256);\n\n function getExitRequestedFromRoot(bytes32 _publicKeyRoot) external view returns (bool);\n\n function getWithdrawnFromPublicKeyRoot(bytes32 _publicKeyRoot) external view returns (bool);\n\n function toggleWithdrawnFromPublicKeyRoot(bytes32 _publicKeyRoot) external;\n}\n"
- },
- "src/contracts/libs/BytesLib.sol": {
- "content": "//SPDX-License-Identifier: MIT\npragma solidity >=0.8.10;\n\n/// Based on GNSPS/BytesLib.sol\nlibrary BytesLib {\n function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(\n 0x40,\n and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n )\n )\n }\n\n return tempBytes;\n }\n\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n ) internal pure returns (bytes memory) {\n require(_length + 31 >= _length, \"slice_overflow\");\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n}\n"
- },
- "src/contracts/libs/DispatchersStorageLib.sol": {
- "content": "//SPDX-License-Identifier: MIT\npragma solidity >=0.8.10;\n\nlibrary DispatchersStorageLib {\n function getUint256(bytes32 position) internal view returns (uint256 data) {\n assembly {\n data := sload(position)\n }\n }\n\n function setUint256(bytes32 position, uint256 data) internal {\n assembly {\n sstore(position, data)\n }\n }\n\n function getAddress(bytes32 position) internal view returns (address data) {\n assembly {\n data := sload(position)\n }\n }\n\n function setAddress(bytes32 position, address data) internal {\n assembly {\n sstore(position, data)\n }\n }\n}\n"
- },
- "src/contracts/libs/StakingContractStorageLib.sol": {
- "content": "//SPDX-License-Identifier: MIT\npragma solidity >=0.8.10;\n\nlibrary StakingContractStorageLib {\n function getUint256(bytes32 position) internal view returns (uint256 data) {\n assembly {\n data := sload(position)\n }\n }\n\n function setUint256(bytes32 position, uint256 data) internal {\n assembly {\n sstore(position, data)\n }\n }\n\n function getAddress(bytes32 position) internal view returns (address data) {\n assembly {\n data := sload(position)\n }\n }\n\n function setAddress(bytes32 position, address data) internal {\n assembly {\n sstore(position, data)\n }\n }\n\n function getBool(bytes32 position) internal view returns (bool data) {\n assembly {\n data := sload(position)\n }\n }\n\n function setBool(bytes32 position, bool data) internal {\n assembly {\n sstore(position, data)\n }\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant VERSION_SLOT = keccak256(\"StakingContract.version\");\n\n function getVersion() internal view returns (uint256) {\n return getUint256(VERSION_SLOT);\n }\n\n function setVersion(uint256 _newVersion) internal {\n setUint256(VERSION_SLOT, _newVersion);\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant ADMIN_SLOT = keccak256(\"StakingContract.admin\");\n bytes32 internal constant PENDING_ADMIN_SLOT = keccak256(\"StakingContract.pendingAdmin\");\n\n function getAdmin() internal view returns (address) {\n return getAddress(ADMIN_SLOT);\n }\n\n function setAdmin(address _newAdmin) internal {\n setAddress(ADMIN_SLOT, _newAdmin);\n }\n\n function getPendingAdmin() internal view returns (address) {\n return getAddress(PENDING_ADMIN_SLOT);\n }\n\n function setPendingAdmin(address _newPendingAdmin) internal {\n setAddress(PENDING_ADMIN_SLOT, _newPendingAdmin);\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant TREASURY_SLOT = keccak256(\"StakingContract.treasury\");\n\n function getTreasury() internal view returns (address) {\n return getAddress(TREASURY_SLOT);\n }\n\n function setTreasury(address _newTreasury) internal {\n setAddress(TREASURY_SLOT, _newTreasury);\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant DEPOSIT_CONTRACT_SLOT = keccak256(\"StakingContract.depositContract\");\n\n function getDepositContract() internal view returns (address) {\n return getAddress(DEPOSIT_CONTRACT_SLOT);\n }\n\n function setDepositContract(address _newDepositContract) internal {\n setAddress(DEPOSIT_CONTRACT_SLOT, _newDepositContract);\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant OPERATORS_SLOT = keccak256(\"StakingContract.operators\");\n\n struct OperatorInfo {\n address operator;\n address feeRecipient;\n uint256 limit;\n bytes[] publicKeys;\n bytes[] signatures;\n bool deactivated;\n }\n\n struct OperatorsSlot {\n OperatorInfo[] value;\n }\n\n function getOperators() internal pure returns (OperatorsSlot storage p) {\n bytes32 slot = OPERATORS_SLOT;\n assembly {\n p.slot := slot\n }\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n /// Validator funding information is stored in a packed fashion\n /// We fit 4 vfi per storage slot.\n /// Each vfi is stored in 64 bits, with the following layout:\n /// 32 bits for the number of available keys\n /// 32 bits for the number of funded keys\n\n uint256 internal constant FUNDED_OFFSET = 32;\n\n bytes32 internal constant VALIDATORS_FUNDING_INFO_SLOT = keccak256(\"StakingContract.validatorsFundingInfo\");\n\n struct ValidatorsFundingInfo {\n uint32 availableKeys;\n uint32 funded;\n }\n\n struct UintToUintMappingSlot {\n mapping(uint256 => uint256) value;\n }\n\n function getValidatorsFundingInfo(uint256 _index) internal view returns (ValidatorsFundingInfo memory vfi) {\n UintToUintMappingSlot storage p;\n bytes32 slot = VALIDATORS_FUNDING_INFO_SLOT;\n\n assembly {\n p.slot := slot\n }\n\n uint256 slotIndex = _index >> 2; // divide by 4\n uint256 innerIndex = (_index & 3) << 6; // modulo 4, multiply by 64\n uint256 value = p.value[slotIndex] >> innerIndex;\n vfi.availableKeys = uint32(value);\n vfi.funded = uint32(value >> FUNDED_OFFSET);\n }\n\n function setValidatorsFundingInfo(\n uint256 _index,\n uint32 _availableKeys,\n uint32 _funded\n ) internal {\n UintToUintMappingSlot storage p;\n bytes32 slot = VALIDATORS_FUNDING_INFO_SLOT;\n\n assembly {\n p.slot := slot\n }\n\n uint256 slotIndex = _index >> 2; // divide by 4\n uint256 innerIndex = (_index & 3) << 6; // modulo 4, multiply by 64\n p.value[slotIndex] =\n (p.value[slotIndex] & (~(uint256(0xFFFFFFFFFFFFFFFF) << innerIndex))) | // clear the bits we want to set\n ((uint256(_availableKeys) | (uint256(_funded) << FUNDED_OFFSET)) << innerIndex);\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant TOTAL_AVAILABLE_VALIDATORS_SLOT = keccak256(\"StakingContract.totalAvailableValidators\");\n\n function getTotalAvailableValidators() internal view returns (uint256) {\n return getUint256(TOTAL_AVAILABLE_VALIDATORS_SLOT);\n }\n\n function setTotalAvailableValidators(uint256 _newTotal) internal {\n setUint256(TOTAL_AVAILABLE_VALIDATORS_SLOT, _newTotal);\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant WITHDRAWERS_SLOT = keccak256(\"StakingContract.withdrawers\");\n\n struct WithdrawersSlot {\n mapping(bytes32 => address) value;\n }\n\n function getWithdrawers() internal pure returns (WithdrawersSlot storage p) {\n bytes32 slot = WITHDRAWERS_SLOT;\n assembly {\n p.slot := slot\n }\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n struct OperatorIndex {\n bool enabled;\n uint32 operatorIndex;\n }\n\n struct OperatorIndexPerValidatorSlot {\n mapping(bytes32 => OperatorIndex) value;\n }\n\n bytes32 internal constant OPERATOR_INDEX_PER_VALIDATOR_SLOT =\n keccak256(\"StakingContract.operatorIndexPerValidator\");\n\n function getOperatorIndexPerValidator() internal pure returns (OperatorIndexPerValidatorSlot storage p) {\n bytes32 slot = OPERATOR_INDEX_PER_VALIDATOR_SLOT;\n assembly {\n p.slot := slot\n }\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant GLOBAL_FEE_SLOT = keccak256(\"StakingContract.globalFee\");\n\n function getGlobalFee() internal view returns (uint256) {\n return getUint256(GLOBAL_FEE_SLOT);\n }\n\n function setGlobalFee(uint256 _newTreasuryFee) internal {\n setUint256(GLOBAL_FEE_SLOT, _newTreasuryFee);\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant OPERATOR_FEE_SLOT = keccak256(\"StakingContract.operatorFee\");\n\n function getOperatorFee() internal view returns (uint256) {\n return getUint256(OPERATOR_FEE_SLOT);\n }\n\n function setOperatorFee(uint256 _newOperatorFee) internal {\n setUint256(OPERATOR_FEE_SLOT, _newOperatorFee);\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant EL_DISPATCHER_SLOT = keccak256(\"StakingContract.executionLayerDispatcher\");\n\n function getELDispatcher() internal view returns (address) {\n return getAddress(EL_DISPATCHER_SLOT);\n }\n\n function setELDispatcher(address _newElDispatcher) internal {\n setAddress(EL_DISPATCHER_SLOT, _newElDispatcher);\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant CL_DISPATCHER_SLOT = keccak256(\"StakingContract.consensusLayerDispatcher\");\n\n function getCLDispatcher() internal view returns (address) {\n return getAddress(CL_DISPATCHER_SLOT);\n }\n\n function setCLDispatcher(address _newClDispatcher) internal {\n setAddress(CL_DISPATCHER_SLOT, _newClDispatcher);\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant FEE_RECIPIENT_IMPLEMENTATION_SLOT =\n keccak256(\"StakingContract.feeRecipientImplementation\");\n\n function getFeeRecipientImplementation() internal view returns (address) {\n return getAddress(FEE_RECIPIENT_IMPLEMENTATION_SLOT);\n }\n\n function setFeeRecipientImplementation(address _newFeeRecipientImplementation) internal {\n setAddress(FEE_RECIPIENT_IMPLEMENTATION_SLOT, _newFeeRecipientImplementation);\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant WITHDRAWER_CUSTOMIZATION_ENABLED_SLOT =\n keccak256(\"StakingContract.withdrawerCustomizationEnabled\");\n\n function getWithdrawerCustomizationEnabled() internal view returns (bool) {\n return getBool(WITHDRAWER_CUSTOMIZATION_ENABLED_SLOT);\n }\n\n function setWithdrawerCustomizationEnabled(bool _enabled) internal {\n setBool(WITHDRAWER_CUSTOMIZATION_ENABLED_SLOT, _enabled);\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant EXIT_REQUEST_MAPPING_SLOT =\n bytes32(uint256(keccak256(\"StakingContract.exitRequest\")) - 1);\n\n struct ExitRequestMap {\n mapping(bytes32 => bool) value;\n }\n\n function getExitRequestMap() internal pure returns (ExitRequestMap storage p) {\n bytes32 slot = EXIT_REQUEST_MAPPING_SLOT;\n assembly {\n p.slot := slot\n }\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant WITHDRAWN_MAPPING_SLOT = bytes32(uint256(keccak256(\"StakingContract.withdrawn\")) - 1);\n\n struct WithdrawnMap {\n mapping(bytes32 => bool) value;\n }\n\n function getWithdrawnMap() internal pure returns (WithdrawnMap storage p) {\n bytes32 slot = WITHDRAWN_MAPPING_SLOT;\n assembly {\n p.slot := slot\n }\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant GLOBAL_COMMISSION_LIMIT_SLOT =\n bytes32(uint256(keccak256(\"StakingContract.globalCommissionLimit\")) - 1);\n\n function getGlobalCommissionLimit() internal view returns (uint256) {\n return getUint256(GLOBAL_COMMISSION_LIMIT_SLOT);\n }\n\n function setGlobalCommissionLimit(uint256 value) internal {\n setUint256(GLOBAL_COMMISSION_LIMIT_SLOT, value);\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant OPERATOR_COMMISSION_LIMIT_SLOT =\n bytes32(uint256(keccak256(\"StakingContract.operatorCommissionLimit\")) - 1);\n\n function getOperatorCommissionLimit() internal view returns (uint256) {\n return getUint256(OPERATOR_COMMISSION_LIMIT_SLOT);\n }\n\n function setOperatorCommissionLimit(uint256 value) internal {\n setUint256(OPERATOR_COMMISSION_LIMIT_SLOT, value);\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant DEPOSIT_STOPPED_SLOT = bytes32(uint256(keccak256(\"StakingContract.depositStopped\")) - 1);\n\n function getDepositStopped() internal view returns (bool) {\n return getBool(DEPOSIT_STOPPED_SLOT);\n }\n\n function setDepositStopped(bool val) internal {\n setBool(DEPOSIT_STOPPED_SLOT, val);\n }\n\n /* ========================================\n ===========================================\n =========================================*/\n\n bytes32 internal constant LAST_VALIDATOR_EDIT_SLOT =\n bytes32(uint256(keccak256(\"StakingContract.lastValidatorsEdit\")) - 1);\n\n function getLastValidatorEdit() internal view returns (uint256) {\n return getUint256(LAST_VALIDATOR_EDIT_SLOT);\n }\n\n function setLastValidatorEdit(uint256 value) internal {\n setUint256(LAST_VALIDATOR_EDIT_SLOT, value);\n }\n}\n"
- },
- "src/contracts/StakingContract.sol": {
- "content": "//SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.8.10;\n\nimport \"./libs/BytesLib.sol\";\nimport \"./interfaces/IFeeRecipient.sol\";\nimport \"./interfaces/IDepositContract.sol\";\nimport \"./libs/StakingContractStorageLib.sol\";\nimport \"@openzeppelin/contracts/proxy/Clones.sol\";\n\n/// @title Ethereum Staking Contract\n/// @author Kiln\n/// @notice You can use this contract to store validator keys and have users fund them and trigger deposits.\ncontract StakingContract {\n using StakingContractStorageLib for bytes32;\n\n uint256 internal constant EXECUTION_LAYER_SALT_PREFIX = 0;\n uint256 internal constant CONSENSUS_LAYER_SALT_PREFIX = 1;\n uint256 public constant SIGNATURE_LENGTH = 96;\n uint256 public constant PUBLIC_KEY_LENGTH = 48;\n uint256 public constant DEPOSIT_SIZE = 32 ether;\n // this is the equivalent of Uint256Lib.toLittleEndian64(DEPOSIT_SIZE / 1000000000 wei);\n uint256 constant DEPOSIT_SIZE_AMOUNT_LITTLEENDIAN64 =\n 0x0040597307000000000000000000000000000000000000000000000000000000;\n uint256 internal constant BASIS_POINTS = 10_000;\n uint256 internal constant WITHDRAWAL_CREDENTIAL_PREFIX_01 =\n 0x0100000000000000000000000000000000000000000000000000000000000000;\n\n error Forbidden();\n error InvalidFee();\n error Deactivated();\n error NoOperators();\n error InvalidCall();\n error Unauthorized();\n error DepositFailure();\n error DepositsStopped();\n error InvalidArgument();\n error UnsortedIndexes();\n error InvalidPublicKeys();\n error InvalidSignatures();\n error InvalidWithdrawer();\n error InvalidZeroAddress();\n error AlreadyInitialized();\n error InvalidDepositValue();\n error NotEnoughValidators();\n error InvalidValidatorCount();\n error DuplicateValidatorKey(bytes);\n error FundedValidatorDeletionAttempt();\n error OperatorLimitTooHigh(uint256 limit, uint256 keyCount);\n error MaximumOperatorCountAlreadyReached();\n error LastEditAfterSnapshot();\n error PublicKeyNotInContract();\n\n struct ValidatorAllocationCache {\n bool used;\n uint8 operatorIndex;\n uint32 funded;\n uint32 toDeposit;\n uint32 available;\n }\n\n event Deposit(address indexed caller, address indexed withdrawer, bytes publicKey, bytes signature);\n event ValidatorKeysAdded(uint256 indexed operatorIndex, bytes publicKeys, bytes signatures);\n event ValidatorKeyRemoved(uint256 indexed operatorIndex, bytes publicKey);\n event ChangedWithdrawer(bytes publicKey, address newWithdrawer);\n event ChangedOperatorLimit(uint256 operatorIndex, uint256 limit);\n event ChangedTreasury(address newTreasury);\n event ChangedGlobalFee(uint256 newGlobalFee);\n event ChangedOperatorFee(uint256 newOperatorFee);\n event ChangedAdmin(address newAdmin);\n event ChangedDepositsStopped(bool isStopped);\n event NewOperator(address operatorAddress, address feeRecipientAddress, uint256 index);\n event ChangedOperatorAddresses(uint256 operatorIndex, address operatorAddress, address feeRecipientAddress);\n event DeactivatedOperator(uint256 _operatorIndex);\n event ActivatedOperator(uint256 _operatorIndex);\n event SetWithdrawerCustomizationStatus(bool _status);\n event ExitRequest(address caller, bytes pubkey);\n event ValidatorsEdited(uint256 blockNumber);\n\n /// @notice Ensures an initialisation call has been called only once per _version value\n /// @param _version The current initialisation value\n modifier init(uint256 _version) {\n if (_version != StakingContractStorageLib.getVersion() + 1) {\n revert AlreadyInitialized();\n }\n\n StakingContractStorageLib.setVersion(_version);\n _;\n }\n\n /// @notice Ensures that the caller is the admin\n modifier onlyAdmin() {\n if (msg.sender != StakingContractStorageLib.getAdmin()) {\n revert Unauthorized();\n }\n\n _;\n }\n\n /// @notice Ensures that the caller is the admin or the operator\n modifier onlyActiveOperatorOrAdmin(uint256 _operatorIndex) {\n if (msg.sender == StakingContractStorageLib.getAdmin()) {\n _;\n } else {\n _onlyActiveOperator(_operatorIndex);\n _;\n }\n }\n\n /// @notice Ensures that the caller is the admin\n modifier onlyActiveOperator(uint256 _operatorIndex) {\n _onlyActiveOperator(_operatorIndex);\n _;\n }\n\n /// @notice Ensures that the caller is the operator fee recipient\n modifier onlyActiveOperatorFeeRecipient(uint256 _operatorIndex) {\n StakingContractStorageLib.OperatorInfo storage operatorInfo = StakingContractStorageLib.getOperators().value[\n _operatorIndex\n ];\n\n if (operatorInfo.deactivated) {\n revert Deactivated();\n }\n\n if (msg.sender != operatorInfo.feeRecipient) {\n revert Unauthorized();\n }\n\n _;\n }\n\n /// @notice Explicit deposit method using msg.sender\n /// @dev A multiple of 32 ETH should be sent\n function deposit() external payable {\n _deposit();\n }\n\n /// @notice Implicit deposit method\n /// @dev A multiple of 32 ETH should be sent\n /// @dev The withdrawer is set to the message sender address\n receive() external payable {\n _deposit();\n }\n\n /// @notice Fallback detection\n /// @dev Fails on any call that fallbacks\n fallback() external payable {\n revert InvalidCall();\n }\n\n function initialize_1(\n address _admin,\n address _treasury,\n address _depositContract,\n address _elDispatcher,\n address _clDispatcher,\n address _feeRecipientImplementation,\n uint256 _globalFee,\n uint256 _operatorFee,\n uint256 globalCommissionLimitBPS,\n uint256 operatorCommissionLimitBPS\n ) external init(1) {\n _checkAddress(_admin);\n StakingContractStorageLib.setAdmin(_admin);\n _checkAddress(_treasury);\n StakingContractStorageLib.setTreasury(_treasury);\n\n if (_globalFee > BASIS_POINTS) {\n revert InvalidFee();\n }\n StakingContractStorageLib.setGlobalFee(_globalFee);\n if (_operatorFee > BASIS_POINTS) {\n revert InvalidFee();\n }\n StakingContractStorageLib.setOperatorFee(_operatorFee);\n\n _checkAddress(_elDispatcher);\n StakingContractStorageLib.setELDispatcher(_elDispatcher);\n _checkAddress(_clDispatcher);\n StakingContractStorageLib.setCLDispatcher(_clDispatcher);\n _checkAddress(_depositContract);\n StakingContractStorageLib.setDepositContract(_depositContract);\n _checkAddress(_feeRecipientImplementation);\n StakingContractStorageLib.setFeeRecipientImplementation(_feeRecipientImplementation);\n initialize_2(globalCommissionLimitBPS, operatorCommissionLimitBPS);\n }\n\n function initialize_2(uint256 globalCommissionLimitBPS, uint256 operatorCommissionLimitBPS) public init(2) {\n if (globalCommissionLimitBPS > BASIS_POINTS) {\n revert InvalidFee();\n }\n StakingContractStorageLib.setGlobalCommissionLimit(globalCommissionLimitBPS);\n if (operatorCommissionLimitBPS > BASIS_POINTS) {\n revert InvalidFee();\n }\n StakingContractStorageLib.setOperatorCommissionLimit(operatorCommissionLimitBPS);\n }\n\n /// @notice Changes the behavior of the withdrawer customization logic\n /// @param _enabled True to allow users to customize the withdrawer\n function setWithdrawerCustomizationEnabled(bool _enabled) external onlyAdmin {\n StakingContractStorageLib.setWithdrawerCustomizationEnabled(_enabled);\n emit SetWithdrawerCustomizationStatus(_enabled);\n }\n\n /// @notice Retrieve system admin\n function getAdmin() external view returns (address) {\n return StakingContractStorageLib.getAdmin();\n }\n\n /// @notice Set new treasury\n /// @dev Only callable by admin\n /// @param _newTreasury New Treasury address\n function setTreasury(address _newTreasury) external onlyAdmin {\n emit ChangedTreasury(_newTreasury);\n StakingContractStorageLib.setTreasury(_newTreasury);\n }\n\n /// @notice Retrieve system treasury\n function getTreasury() external view returns (address) {\n return StakingContractStorageLib.getTreasury();\n }\n\n /// @notice Retrieve the global fee\n function getGlobalFee() external view returns (uint256) {\n return StakingContractStorageLib.getGlobalFee();\n }\n\n /// @notice Retrieve the operator fee\n function getOperatorFee() external view returns (uint256) {\n return StakingContractStorageLib.getOperatorFee();\n }\n\n /// @notice Compute the Execution Layer Fee recipient address for a given validator public key\n /// @param _publicKey Validator to get the recipient\n function getELFeeRecipient(bytes calldata _publicKey) external view returns (address) {\n return _getDeterministicReceiver(_publicKey, EXECUTION_LAYER_SALT_PREFIX);\n }\n\n /// @notice Compute the Consensus Layer Fee recipient address for a given validator public key\n /// @param _publicKey Validator to get the recipient\n function getCLFeeRecipient(bytes calldata _publicKey) external view returns (address) {\n return _getDeterministicReceiver(_publicKey, CONSENSUS_LAYER_SALT_PREFIX);\n }\n\n /// @notice Retrieve the Execution & Consensus Layer Fee operator recipient for a given public key\n function getOperatorFeeRecipient(bytes32 pubKeyRoot) external view returns (address) {\n if (StakingContractStorageLib.getOperatorIndexPerValidator().value[pubKeyRoot].enabled == false) {\n revert PublicKeyNotInContract();\n }\n return\n StakingContractStorageLib\n .getOperators()\n .value[StakingContractStorageLib.getOperatorIndexPerValidator().value[pubKeyRoot].operatorIndex]\n .feeRecipient;\n }\n\n /// @notice Retrieve withdrawer of public key\n /// @notice In case the validator is not enabled, it will return address(0)\n /// @param _publicKey Public Key to check\n function getWithdrawer(bytes calldata _publicKey) external view returns (address) {\n return _getWithdrawer(_getPubKeyRoot(_publicKey));\n }\n\n /// @notice Retrieve withdrawer of public key root\n /// @notice In case the validator is not enabled, it will return address(0)\n /// @param _publicKeyRoot Hash of the public key\n function getWithdrawerFromPublicKeyRoot(bytes32 _publicKeyRoot) external view returns (address) {\n return _getWithdrawer(_publicKeyRoot);\n }\n\n /// @notice Retrieve whether the validator exit has been requested\n /// @notice In case the validator is not enabled, it will return false\n /// @param _publicKeyRoot Public Key Root to check\n function getExitRequestedFromRoot(bytes32 _publicKeyRoot) external view returns (bool) {\n return _getExitRequest(_publicKeyRoot);\n }\n\n /// @notice Return true if the validator already went through the exit logic\n /// @notice In case the validator is not enabled, it will return false\n /// @param _publicKeyRoot Public Key Root of the validator\n function getWithdrawnFromPublicKeyRoot(bytes32 _publicKeyRoot) external view returns (bool) {\n return StakingContractStorageLib.getWithdrawnMap().value[_publicKeyRoot];\n }\n\n /// @notice Retrieve the enabled status of public key root, true if the key is in the contract\n /// @param _publicKeyRoot Hash of the public key\n function getEnabledFromPublicKeyRoot(bytes32 _publicKeyRoot) external view returns (bool) {\n return StakingContractStorageLib.getOperatorIndexPerValidator().value[_publicKeyRoot].enabled;\n }\n\n /// @notice Allows the CLDispatcher to signal a validator went through the exit logic\n /// @param _publicKeyRoot Public Key Root of the validator\n function toggleWithdrawnFromPublicKeyRoot(bytes32 _publicKeyRoot) external {\n if (msg.sender != StakingContractStorageLib.getCLDispatcher()) {\n revert Unauthorized();\n }\n StakingContractStorageLib.getWithdrawnMap().value[_publicKeyRoot] = true;\n }\n\n /// @notice Returns false if the users can deposit, true if deposits are stopped\n function getDepositsStopped() external view returns (bool) {\n return StakingContractStorageLib.getDepositStopped();\n }\n\n /// @notice Retrieve operator details\n /// @param _operatorIndex Operator index\n function getOperator(uint256 _operatorIndex)\n external\n view\n returns (\n address operatorAddress,\n address feeRecipientAddress,\n uint256 limit,\n uint256 keys,\n uint256 funded,\n uint256 available,\n bool deactivated\n )\n {\n StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();\n if (_operatorIndex < operators.value.length) {\n StakingContractStorageLib.ValidatorsFundingInfo memory _operatorInfo = StakingContractStorageLib\n .getValidatorsFundingInfo(_operatorIndex);\n StakingContractStorageLib.OperatorInfo storage _operator = operators.value[_operatorIndex];\n\n (operatorAddress, feeRecipientAddress, limit, keys, deactivated) = (\n _operator.operator,\n _operator.feeRecipient,\n _operator.limit,\n _operator.publicKeys.length,\n _operator.deactivated\n );\n (funded, available) = (_operatorInfo.funded, _operatorInfo.availableKeys);\n }\n }\n\n /// @notice Get details about a validator\n /// @param _operatorIndex Index of the operator running the validator\n /// @param _validatorIndex Index of the validator\n function getValidator(uint256 _operatorIndex, uint256 _validatorIndex)\n external\n view\n returns (\n bytes memory publicKey,\n bytes memory signature,\n address withdrawer,\n bool funded\n )\n {\n StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();\n publicKey = operators.value[_operatorIndex].publicKeys[_validatorIndex];\n signature = operators.value[_operatorIndex].signatures[_validatorIndex];\n withdrawer = _getWithdrawer(_getPubKeyRoot(publicKey));\n funded = _validatorIndex < StakingContractStorageLib.getValidatorsFundingInfo(_operatorIndex).funded;\n }\n\n /// @notice Get the total available keys that are ready to be used for deposits\n function getAvailableValidatorCount() external view returns (uint256) {\n return StakingContractStorageLib.getTotalAvailableValidators();\n }\n\n /// @notice Set new admin\n /// @dev Only callable by admin\n /// @param _newAdmin New Administrator address\n function transferOwnership(address _newAdmin) external onlyAdmin {\n StakingContractStorageLib.setPendingAdmin(_newAdmin);\n }\n\n /// @notice New admin must accept its role by calling this method\n /// @dev Only callable by new admin\n function acceptOwnership() external {\n address newAdmin = StakingContractStorageLib.getPendingAdmin();\n\n if (msg.sender != newAdmin) {\n revert Unauthorized();\n }\n StakingContractStorageLib.setAdmin(newAdmin);\n StakingContractStorageLib.setPendingAdmin(address(0));\n emit ChangedAdmin(newAdmin);\n }\n\n /// @notice Get the new admin's address previously set for an ownership transfer\n function getPendingAdmin() external view returns (address) {\n return StakingContractStorageLib.getPendingAdmin();\n }\n\n /// @notice Add new operator\n /// @dev Only callable by admin\n /// @param _operatorAddress Operator address allowed to add / remove validators\n /// @param _feeRecipientAddress Privileged operator address used to manage rewards and operator addresses\n function addOperator(address _operatorAddress, address _feeRecipientAddress) external onlyAdmin returns (uint256) {\n StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();\n StakingContractStorageLib.OperatorInfo memory newOperator;\n\n if (operators.value.length == 1) {\n revert MaximumOperatorCountAlreadyReached();\n }\n newOperator.operator = _operatorAddress;\n newOperator.feeRecipient = _feeRecipientAddress;\n operators.value.push(newOperator);\n uint256 operatorIndex = operators.value.length - 1;\n emit NewOperator(_operatorAddress, _feeRecipientAddress, operatorIndex);\n return operatorIndex;\n }\n\n /// @notice Set new operator addresses (operations and reward management)\n /// @dev Only callable by fee recipient address manager\n /// @param _operatorIndex Index of the operator to update\n /// @param _operatorAddress New operator address for operations management\n /// @param _feeRecipientAddress New operator address for reward management\n function setOperatorAddresses(\n uint256 _operatorIndex,\n address _operatorAddress,\n address _feeRecipientAddress\n ) external onlyActiveOperatorFeeRecipient(_operatorIndex) {\n _checkAddress(_operatorAddress);\n _checkAddress(_feeRecipientAddress);\n StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();\n\n operators.value[_operatorIndex].operator = _operatorAddress;\n operators.value[_operatorIndex].feeRecipient = _feeRecipientAddress;\n emit ChangedOperatorAddresses(_operatorIndex, _operatorAddress, _feeRecipientAddress);\n }\n\n /// @notice Set withdrawer for public key\n /// @dev Only callable by current public key withdrawer\n /// @param _publicKey Public key to change withdrawer\n /// @param _newWithdrawer New withdrawer address\n function setWithdrawer(bytes calldata _publicKey, address _newWithdrawer) external {\n if (!StakingContractStorageLib.getWithdrawerCustomizationEnabled()) {\n revert Forbidden();\n }\n _checkAddress(_newWithdrawer);\n bytes32 pubkeyRoot = _getPubKeyRoot(_publicKey);\n StakingContractStorageLib.WithdrawersSlot storage withdrawers = StakingContractStorageLib.getWithdrawers();\n\n if (withdrawers.value[pubkeyRoot] != msg.sender) {\n revert Unauthorized();\n }\n\n emit ChangedWithdrawer(_publicKey, _newWithdrawer);\n\n withdrawers.value[pubkeyRoot] = _newWithdrawer;\n }\n\n /// @notice Set operator staking limits\n /// @dev Only callable by admin\n /// @dev Limit should not exceed the validator key count of the operator\n /// @dev Keys should be registered before limit is increased\n /// @dev Allows all keys to be verified by the system admin before limit is increased\n /// @param _operatorIndex Operator Index\n /// @param _limit New staking limit\n /// @param _snapshot Block number at which verification was done\n function setOperatorLimit(\n uint256 _operatorIndex,\n uint256 _limit,\n uint256 _snapshot\n ) external onlyAdmin {\n StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();\n if (operators.value[_operatorIndex].deactivated) {\n revert Deactivated();\n }\n uint256 publicKeyCount = operators.value[_operatorIndex].publicKeys.length;\n if (publicKeyCount < _limit) {\n revert OperatorLimitTooHigh(_limit, publicKeyCount);\n }\n if (\n operators.value[_operatorIndex].limit < _limit &&\n StakingContractStorageLib.getLastValidatorEdit() > _snapshot\n ) {\n revert LastEditAfterSnapshot();\n }\n operators.value[_operatorIndex].limit = _limit;\n _updateAvailableValidatorCount(_operatorIndex);\n emit ChangedOperatorLimit(_operatorIndex, _limit);\n }\n\n /// @notice Deactivates an operator and changes the fee recipient address and the staking limit\n /// @param _operatorIndex Operator Index\n /// @param _temporaryFeeRecipient Temporary address to receive funds decided by the system admin\n function deactivateOperator(uint256 _operatorIndex, address _temporaryFeeRecipient) external onlyAdmin {\n StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();\n operators.value[_operatorIndex].limit = 0;\n emit ChangedOperatorLimit(_operatorIndex, 0);\n operators.value[_operatorIndex].deactivated = true;\n emit DeactivatedOperator(_operatorIndex);\n operators.value[_operatorIndex].feeRecipient = _temporaryFeeRecipient;\n emit ChangedOperatorAddresses(_operatorIndex, operators.value[_operatorIndex].operator, _temporaryFeeRecipient);\n _updateAvailableValidatorCount(_operatorIndex);\n }\n\n /// @notice Activates an operator, without changing its 0 staking limit\n /// @param _operatorIndex Operator Index\n /// @param _newFeeRecipient Sets the fee recipient address\n function activateOperator(uint256 _operatorIndex, address _newFeeRecipient) external onlyAdmin {\n StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();\n operators.value[_operatorIndex].deactivated = false;\n emit ActivatedOperator(_operatorIndex);\n operators.value[_operatorIndex].feeRecipient = _newFeeRecipient;\n emit ChangedOperatorAddresses(_operatorIndex, operators.value[_operatorIndex].operator, _newFeeRecipient);\n }\n\n /// @notice Change the Operator fee\n /// @param _operatorFee Fee in Basis Point\n function setOperatorFee(uint256 _operatorFee) external onlyAdmin {\n if (_operatorFee > StakingContractStorageLib.getOperatorCommissionLimit()) {\n revert InvalidFee();\n }\n StakingContractStorageLib.setOperatorFee(_operatorFee);\n emit ChangedOperatorFee(_operatorFee);\n }\n\n /// @notice Change the Global fee\n /// @param _globalFee Fee in Basis Point\n function setGlobalFee(uint256 _globalFee) external onlyAdmin {\n if (_globalFee > StakingContractStorageLib.getGlobalCommissionLimit()) {\n revert InvalidFee();\n }\n StakingContractStorageLib.setGlobalFee(_globalFee);\n emit ChangedGlobalFee(_globalFee);\n }\n\n /// @notice Add new validator public keys and signatures\n /// @dev Only callable by operator\n /// @param _operatorIndex Operator Index\n /// @param _keyCount Number of keys added\n /// @param _publicKeys Concatenated _keyCount public keys\n /// @param _signatures Concatenated _keyCount signatures\n function addValidators(\n uint256 _operatorIndex,\n uint256 _keyCount,\n bytes calldata _publicKeys,\n bytes calldata _signatures\n ) external onlyActiveOperator(_operatorIndex) {\n if (_keyCount == 0) {\n revert InvalidArgument();\n }\n\n if (_publicKeys.length % PUBLIC_KEY_LENGTH != 0 || _publicKeys.length / PUBLIC_KEY_LENGTH != _keyCount) {\n revert InvalidPublicKeys();\n }\n\n if (_signatures.length % SIGNATURE_LENGTH != 0 || _signatures.length / SIGNATURE_LENGTH != _keyCount) {\n revert InvalidSignatures();\n }\n\n StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();\n StakingContractStorageLib.OperatorIndexPerValidatorSlot\n storage operatorIndexPerValidator = StakingContractStorageLib.getOperatorIndexPerValidator();\n\n for (uint256 i; i < _keyCount; ) {\n bytes memory publicKey = BytesLib.slice(_publicKeys, i * PUBLIC_KEY_LENGTH, PUBLIC_KEY_LENGTH);\n bytes memory signature = BytesLib.slice(_signatures, i * SIGNATURE_LENGTH, SIGNATURE_LENGTH);\n\n operators.value[_operatorIndex].publicKeys.push(publicKey);\n operators.value[_operatorIndex].signatures.push(signature);\n\n bytes32 pubKeyRoot = _getPubKeyRoot(publicKey);\n\n if (operatorIndexPerValidator.value[pubKeyRoot].enabled) {\n revert DuplicateValidatorKey(publicKey);\n }\n\n operatorIndexPerValidator.value[pubKeyRoot] = StakingContractStorageLib.OperatorIndex({\n enabled: true,\n operatorIndex: uint32(_operatorIndex)\n });\n\n unchecked {\n ++i;\n }\n }\n\n emit ValidatorKeysAdded(_operatorIndex, _publicKeys, _signatures);\n _updateLastValidatorsEdit();\n _updateAvailableValidatorCount(_operatorIndex);\n }\n\n /// @notice Remove unfunded validators\n /// @dev Only callable by operator\n /// @dev Indexes should be provided in decreasing order\n /// @dev The limit will be set to the lowest removed operator index to ensure all changes above the\n /// lowest removed validator key are verified by the system administrator\n /// @param _operatorIndex Operator Index\n /// @param _indexes List of indexes to delete, in decreasing order\n function removeValidators(uint256 _operatorIndex, uint256[] calldata _indexes)\n external\n onlyActiveOperatorOrAdmin(_operatorIndex)\n {\n if (_indexes.length == 0) {\n revert InvalidArgument();\n }\n\n StakingContractStorageLib.ValidatorsFundingInfo memory operatorInfo = StakingContractStorageLib\n .getValidatorsFundingInfo(_operatorIndex);\n StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();\n StakingContractStorageLib.OperatorIndexPerValidatorSlot\n storage operatorIndexPerValidator = StakingContractStorageLib.getOperatorIndexPerValidator();\n\n if (_indexes[_indexes.length - 1] < operatorInfo.funded) {\n revert FundedValidatorDeletionAttempt();\n }\n for (uint256 i; i < _indexes.length; ) {\n if (i > 0 && _indexes[i] >= _indexes[i - 1]) {\n revert UnsortedIndexes();\n }\n\n bytes32 pubKeyRoot = _getPubKeyRoot(operators.value[_operatorIndex].publicKeys[_indexes[i]]);\n operatorIndexPerValidator.value[pubKeyRoot].enabled = false;\n operatorIndexPerValidator.value[pubKeyRoot].operatorIndex = 0;\n\n emit ValidatorKeyRemoved(_operatorIndex, operators.value[_operatorIndex].publicKeys[_indexes[i]]);\n if (_indexes[i] == operators.value[_operatorIndex].publicKeys.length - 1) {\n operators.value[_operatorIndex].publicKeys.pop();\n operators.value[_operatorIndex].signatures.pop();\n } else {\n operators.value[_operatorIndex].publicKeys[_indexes[i]] = operators.value[_operatorIndex].publicKeys[\n operators.value[_operatorIndex].publicKeys.length - 1\n ];\n operators.value[_operatorIndex].publicKeys.pop();\n operators.value[_operatorIndex].signatures[_indexes[i]] = operators.value[_operatorIndex].signatures[\n operators.value[_operatorIndex].signatures.length - 1\n ];\n operators.value[_operatorIndex].signatures.pop();\n }\n\n unchecked {\n ++i;\n }\n }\n\n if (_indexes[_indexes.length - 1] < operators.value[_operatorIndex].limit) {\n operators.value[_operatorIndex].limit = _indexes[_indexes.length - 1];\n emit ChangedOperatorLimit(_operatorIndex, _indexes[_indexes.length - 1]);\n }\n\n _updateLastValidatorsEdit();\n _updateAvailableValidatorCount(_operatorIndex);\n }\n\n /// @notice Withdraw the Execution Layer Fee for given validators public keys\n /// @dev Funds are sent to the withdrawer account\n /// @dev This method is public on purpose\n /// @param _publicKeys Validators to withdraw Execution Layer Fees from\n function batchWithdrawELFee(bytes calldata _publicKeys) external {\n if (_publicKeys.length % PUBLIC_KEY_LENGTH != 0) {\n revert InvalidPublicKeys();\n }\n for (uint256 i = 0; i < _publicKeys.length; ) {\n bytes memory publicKey = BytesLib.slice(_publicKeys, i, PUBLIC_KEY_LENGTH);\n _onlyWithdrawerOrAdmin(publicKey);\n _deployAndWithdraw(publicKey, EXECUTION_LAYER_SALT_PREFIX, StakingContractStorageLib.getELDispatcher());\n unchecked {\n i += PUBLIC_KEY_LENGTH;\n }\n }\n }\n\n /// @notice Withdraw the Consensus Layer Fee for given validators public keys\n /// @dev Funds are sent to the withdrawer account\n /// @dev This method is public on purpose\n /// @param _publicKeys Validators to withdraw Consensus Layer Fees from\n function batchWithdrawCLFee(bytes calldata _publicKeys) external {\n if (_publicKeys.length % PUBLIC_KEY_LENGTH != 0) {\n revert InvalidPublicKeys();\n }\n for (uint256 i = 0; i < _publicKeys.length; ) {\n bytes memory publicKey = BytesLib.slice(_publicKeys, i, PUBLIC_KEY_LENGTH);\n _onlyWithdrawerOrAdmin(publicKey);\n _deployAndWithdraw(publicKey, CONSENSUS_LAYER_SALT_PREFIX, StakingContractStorageLib.getCLDispatcher());\n unchecked {\n i += PUBLIC_KEY_LENGTH;\n }\n }\n }\n\n /// @notice Withdraw both Consensus and Execution Layer Fees for given validators public keys\n /// @dev Funds are sent to the withdrawer account\n /// @param _publicKeys Validators to withdraw fees from\n function batchWithdraw(bytes calldata _publicKeys) external {\n if (_publicKeys.length % PUBLIC_KEY_LENGTH != 0) {\n revert InvalidPublicKeys();\n }\n for (uint256 i = 0; i < _publicKeys.length; ) {\n bytes memory publicKey = BytesLib.slice(_publicKeys, i, PUBLIC_KEY_LENGTH);\n _onlyWithdrawerOrAdmin(publicKey);\n _deployAndWithdraw(publicKey, EXECUTION_LAYER_SALT_PREFIX, StakingContractStorageLib.getELDispatcher());\n _deployAndWithdraw(publicKey, CONSENSUS_LAYER_SALT_PREFIX, StakingContractStorageLib.getCLDispatcher());\n unchecked {\n i += PUBLIC_KEY_LENGTH;\n }\n }\n }\n\n /// @notice Withdraw the Execution Layer Fee for a given validator public key\n /// @dev Funds are sent to the withdrawer account\n /// @param _publicKey Validator to withdraw Execution Layer Fees from\n function withdrawELFee(bytes calldata _publicKey) external {\n _onlyWithdrawerOrAdmin(_publicKey);\n _deployAndWithdraw(_publicKey, EXECUTION_LAYER_SALT_PREFIX, StakingContractStorageLib.getELDispatcher());\n }\n\n /// @notice Withdraw the Consensus Layer Fee for a given validator public key\n /// @dev Funds are sent to the withdrawer account\n /// @param _publicKey Validator to withdraw Consensus Layer Fees from\n function withdrawCLFee(bytes calldata _publicKey) external {\n _onlyWithdrawerOrAdmin(_publicKey);\n _deployAndWithdraw(_publicKey, CONSENSUS_LAYER_SALT_PREFIX, StakingContractStorageLib.getCLDispatcher());\n }\n\n /// @notice Withdraw both Consensus and Execution Layer Fee for a given validator public key\n /// @dev Reverts if any is null\n /// @param _publicKey Validator to withdraw Execution and Consensus Layer Fees from\n function withdraw(bytes calldata _publicKey) external {\n _onlyWithdrawerOrAdmin(_publicKey);\n _deployAndWithdraw(_publicKey, EXECUTION_LAYER_SALT_PREFIX, StakingContractStorageLib.getELDispatcher());\n _deployAndWithdraw(_publicKey, CONSENSUS_LAYER_SALT_PREFIX, StakingContractStorageLib.getCLDispatcher());\n }\n\n function requestValidatorsExit(bytes calldata _publicKeys) external {\n if (_publicKeys.length % PUBLIC_KEY_LENGTH != 0) {\n revert InvalidPublicKeys();\n }\n for (uint256 i = 0; i < _publicKeys.length; ) {\n bytes memory publicKey = BytesLib.slice(_publicKeys, i, PUBLIC_KEY_LENGTH);\n bytes32 pubKeyRoot = _getPubKeyRoot(publicKey);\n address withdrawer = _getWithdrawer(pubKeyRoot);\n if (msg.sender != withdrawer) {\n revert Unauthorized();\n }\n _setExitRequest(pubKeyRoot, true);\n emit ExitRequest(withdrawer, publicKey);\n unchecked {\n i += PUBLIC_KEY_LENGTH;\n }\n }\n }\n\n /// @notice Utility to stop or allow deposits\n function setDepositsStopped(bool val) external onlyAdmin {\n emit ChangedDepositsStopped(val);\n StakingContractStorageLib.setDepositStopped(val);\n }\n\n /// ██ ███ ██ ████████ ███████ ██████ ███ ██ █████ ██\n /// ██ ████ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██\n /// ██ ██ ██ ██ ██ █████ ██████ ██ ██ ██ ███████ ██\n /// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██\n /// ██ ██ ████ ██ ███████ ██ ██ ██ ████ ██ ██ ███████\n\n function _onlyWithdrawerOrAdmin(bytes memory _publicKey) internal view {\n if (\n msg.sender != _getWithdrawer(_getPubKeyRoot(_publicKey)) &&\n StakingContractStorageLib.getAdmin() != msg.sender\n ) {\n revert InvalidWithdrawer();\n }\n }\n\n function _onlyActiveOperator(uint256 _operatorIndex) internal view {\n StakingContractStorageLib.OperatorInfo storage operatorInfo = StakingContractStorageLib.getOperators().value[\n _operatorIndex\n ];\n\n if (operatorInfo.deactivated) {\n revert Deactivated();\n }\n\n if (msg.sender != operatorInfo.operator) {\n revert Unauthorized();\n }\n }\n\n function _getPubKeyRoot(bytes memory _publicKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(_publicKey, bytes16(0)));\n }\n\n function _getWithdrawer(bytes32 _publicKeyRoot) internal view returns (address) {\n return StakingContractStorageLib.getWithdrawers().value[_publicKeyRoot];\n }\n\n function _getExitRequest(bytes32 _publicKeyRoot) internal view returns (bool) {\n return StakingContractStorageLib.getExitRequestMap().value[_publicKeyRoot];\n }\n\n function _setExitRequest(bytes32 _publicKeyRoot, bool _value) internal {\n StakingContractStorageLib.getExitRequestMap().value[_publicKeyRoot] = _value;\n }\n\n function _updateAvailableValidatorCount(uint256 _operatorIndex) internal {\n StakingContractStorageLib.ValidatorsFundingInfo memory validatorFundingInfo = StakingContractStorageLib\n .getValidatorsFundingInfo(_operatorIndex);\n StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();\n\n uint32 oldAvailableCount = validatorFundingInfo.availableKeys;\n uint32 newAvailableCount = 0;\n uint256 cap = operators.value[_operatorIndex].limit;\n\n if (cap <= validatorFundingInfo.funded) {\n StakingContractStorageLib.setValidatorsFundingInfo(_operatorIndex, 0, validatorFundingInfo.funded);\n } else {\n newAvailableCount = uint32(cap - validatorFundingInfo.funded);\n StakingContractStorageLib.setValidatorsFundingInfo(\n _operatorIndex,\n newAvailableCount,\n validatorFundingInfo.funded\n );\n }\n\n if (oldAvailableCount != newAvailableCount) {\n StakingContractStorageLib.setTotalAvailableValidators(\n (StakingContractStorageLib.getTotalAvailableValidators() - oldAvailableCount) + newAvailableCount\n );\n }\n }\n\n function _updateLastValidatorsEdit() internal {\n StakingContractStorageLib.setLastValidatorEdit(block.number);\n emit ValidatorsEdited(block.number);\n }\n\n function _addressToWithdrawalCredentials(address _recipient) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_recipient)) + WITHDRAWAL_CREDENTIAL_PREFIX_01);\n }\n\n function _depositValidatorsOfOperator(uint256 _operatorIndex, uint256 _validatorCount) internal {\n StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();\n StakingContractStorageLib.OperatorInfo storage operator = operators.value[_operatorIndex];\n StakingContractStorageLib.ValidatorsFundingInfo memory vfi = StakingContractStorageLib.getValidatorsFundingInfo(\n _operatorIndex\n );\n\n for (uint256 i = vfi.funded; i < vfi.funded + _validatorCount; ) {\n bytes memory publicKey = operator.publicKeys[i];\n bytes memory signature = operator.signatures[i];\n address consensusLayerRecipient = _getDeterministicReceiver(publicKey, CONSENSUS_LAYER_SALT_PREFIX);\n bytes32 withdrawalCredentials = _addressToWithdrawalCredentials(consensusLayerRecipient);\n bytes32 pubkeyRoot = _getPubKeyRoot(publicKey);\n _depositValidator(publicKey, pubkeyRoot, signature, withdrawalCredentials);\n StakingContractStorageLib.getWithdrawers().value[pubkeyRoot] = msg.sender;\n emit Deposit(msg.sender, msg.sender, publicKey, signature);\n unchecked {\n ++i;\n }\n }\n\n StakingContractStorageLib.setValidatorsFundingInfo(\n _operatorIndex,\n uint32(vfi.availableKeys - _validatorCount),\n uint32(vfi.funded + _validatorCount)\n );\n }\n\n /// @notice Internal utility to deposit a public key, its signature and 32 ETH to the consensus layer\n /// @param _publicKey The Public Key to deposit\n /// @param _signature The Signature to deposit\n /// @param _withdrawalCredentials The Withdrawal Credentials to deposit\n function _depositValidator(\n bytes memory _publicKey,\n bytes32 _pubkeyRoot,\n bytes memory _signature,\n bytes32 _withdrawalCredentials\n ) internal {\n bytes32 signatureRoot = sha256(\n abi.encodePacked(\n sha256(BytesLib.slice(_signature, 0, 64)),\n sha256(abi.encodePacked(BytesLib.slice(_signature, 64, SIGNATURE_LENGTH - 64), bytes32(0)))\n )\n );\n\n bytes32 depositDataRoot = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(_pubkeyRoot, _withdrawalCredentials)),\n sha256(abi.encodePacked(DEPOSIT_SIZE_AMOUNT_LITTLEENDIAN64, signatureRoot))\n )\n );\n\n uint256 targetBalance = address(this).balance - DEPOSIT_SIZE;\n\n IDepositContract(StakingContractStorageLib.getDepositContract()).deposit{value: DEPOSIT_SIZE}(\n _publicKey,\n abi.encodePacked(_withdrawalCredentials),\n _signature,\n depositDataRoot\n );\n\n if (address(this).balance != targetBalance) {\n revert DepositFailure();\n }\n }\n\n function _depositOnOneOperator(uint256 _depositCount, uint256 _totalAvailableValidators) internal {\n StakingContractStorageLib.setTotalAvailableValidators(_totalAvailableValidators - _depositCount);\n _depositValidatorsOfOperator(0, _depositCount);\n }\n\n function _deposit() internal {\n if (StakingContractStorageLib.getDepositStopped()) {\n revert DepositsStopped();\n }\n if (msg.value == 0 || msg.value % DEPOSIT_SIZE != 0) {\n revert InvalidDepositValue();\n }\n uint256 totalAvailableValidators = StakingContractStorageLib.getTotalAvailableValidators();\n uint256 depositCount = msg.value / DEPOSIT_SIZE;\n if (depositCount > totalAvailableValidators) {\n revert NotEnoughValidators();\n }\n StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();\n if (operators.value.length == 0) {\n revert NoOperators();\n }\n _depositOnOneOperator(depositCount, totalAvailableValidators);\n }\n\n function _min(uint256 _a, uint256 _b) internal pure returns (uint256) {\n if (_a < _b) {\n return _a;\n }\n return _b;\n }\n\n /// @notice Internal utility to compute the receiver deterministic address\n /// @param _publicKey Public Key assigned to the receiver\n /// @param _prefix Prefix used to generate multiple receivers per public key\n function _getDeterministicReceiver(bytes memory _publicKey, uint256 _prefix) internal view returns (address) {\n bytes32 publicKeyRoot = _getPubKeyRoot(_publicKey);\n bytes32 salt = sha256(abi.encodePacked(_prefix, publicKeyRoot));\n address implementation = StakingContractStorageLib.getFeeRecipientImplementation();\n return Clones.predictDeterministicAddress(implementation, salt);\n }\n\n /// @notice Internal utility to deploy and withdraw the fees from a receiver\n /// @param _publicKey Public Key assigned to the receiver\n /// @param _prefix Prefix used to generate multiple receivers per public key\n /// @param _dispatcher Address of the dispatcher contract\n function _deployAndWithdraw(\n bytes memory _publicKey,\n uint256 _prefix,\n address _dispatcher\n ) internal {\n bytes32 publicKeyRoot = _getPubKeyRoot(_publicKey);\n bytes32 feeRecipientSalt = sha256(abi.encodePacked(_prefix, publicKeyRoot));\n address implementation = StakingContractStorageLib.getFeeRecipientImplementation();\n address feeRecipientAddress = Clones.predictDeterministicAddress(implementation, feeRecipientSalt);\n if (feeRecipientAddress.code.length == 0) {\n Clones.cloneDeterministic(implementation, feeRecipientSalt);\n IFeeRecipient(feeRecipientAddress).init(_dispatcher, publicKeyRoot);\n }\n IFeeRecipient(feeRecipientAddress).withdraw();\n }\n\n function _checkAddress(address _address) internal pure {\n if (_address == address(0)) {\n revert InvalidZeroAddress();\n }\n }\n}\n"
- },
- "src/contracts/TUPProxy.sol": {
- "content": "//SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.8.10;\n\nimport \"@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol\";\n\n/// @title TUPProxy (Transparent Upgradeable Pausable Proxy)\n/// @author SkillZ\n/// @notice This contract extends the Transparent Upgradeable proxy and adds a system wide pause feature.\n/// When the system is paused, the fallback will fail no matter what calls are made.\ncontract TUPProxy is TransparentUpgradeableProxy {\n bytes32 private constant _PAUSE_SLOT = bytes32(uint256(keccak256(\"eip1967.proxy.pause\")) - 1);\n\n error CallWhenPaused();\n\n constructor(\n address _logic,\n address admin_,\n bytes memory _data\n ) payable TransparentUpgradeableProxy(_logic, admin_, _data) {}\n\n /// @dev Retrieves Paused state\n /// @return Paused state\n function isPaused() external ifAdmin returns (bool) {\n return StorageSlot.getBooleanSlot(_PAUSE_SLOT).value;\n }\n\n /// @dev Pauses system\n function pause() external ifAdmin {\n StorageSlot.getBooleanSlot(_PAUSE_SLOT).value = true;\n }\n\n /// @dev Unpauses system\n function unpause() external ifAdmin {\n StorageSlot.getBooleanSlot(_PAUSE_SLOT).value = false;\n }\n\n /// @dev Overrides the fallback method to check if system is not paused before\n /// @dev Address Zero is allowed to perform calls even if system is paused. This allows\n /// view functions to be called when the system is paused as rpc providers can easily\n /// set the sender address to zero.\n function _beforeFallback() internal override {\n if (StorageSlot.getBooleanSlot(_PAUSE_SLOT).value == false || msg.sender == address(0)) {\n super._beforeFallback();\n } else {\n revert CallWhenPaused();\n }\n }\n}\n"
- }
- },
- "settings": {
- "optimizer": {
- "enabled": true,
- "runs": 10000
- },
- "outputSelection": {
- "*": {
- "*": [
- "evm.bytecode",
- "evm.deployedBytecode",
- "abi"
- ]
- }
- },
- "metadata": {
- "useLiteralContent": true
- }
- }
- }}
Contract sourced from Etherscan. Solidity version v0.8.13+commit.abaa5c0e
.
Panoramix decompilation
# Palkeoramix decompiler. def _fallback(?) payable: # default function delegate 0x223ca2fe219df1aad87787a0de4b65f541f08542 with: funct call.data[return_data.size len 4] gas gas_remaining wei args call.data[return_data.size + 4 len calldata.size - 4] if not delegate.return_code: revert with ext_call.return_data[return_data.size len return_data.size] return ext_call.return_data[return_data.size len return_data.size]
Decompilation generated by Panoramix.
Raw bytecode
0x363d3d373d3d3d363d73223ca2fe219df1aad87787a0de4b65f541f085425af43d82803e903d91602b57fd5bf3