diff --git a/deploy/6_deploy_safe_staking_contract.ts b/deploy/6_deploy_safe_staking_contract.ts index f70ce6c..d01776d 100644 --- a/deploy/6_deploy_safe_staking_contract.ts +++ b/deploy/6_deploy_safe_staking_contract.ts @@ -8,7 +8,7 @@ const getMaxFeeBps = (network: string): number => { case "holesky_dev_safe": return 1000; case "mainnet_safe": - return 1000; //10% max user fee + return 2000; //20% max user fee default: return 1000; diff --git a/deployment.holesky_dev_safe.json b/deployment.holesky_dev_safe.json new file mode 100644 index 0000000..7c542a6 --- /dev/null +++ b/deployment.holesky_dev_safe.json @@ -0,0 +1,16 @@ +{ + "name": "holesky_dev_safe", + "chainId": "17000", + "contracts": { + "ConsensusLayerFeeDispatcher": "0xee4d637Aa7966b13174CD977A8387cD767B46aD4", + "ExecutionLayerFeeDispatcher": "0x429Bf8D9a1ff237D0F52bd39a968Aee07C1A8C89", + "FeeRecipient": "0x2f1aEd559Efb6107b6f713f6573CC66d6AAB3A35", + "StakingContract": "0x124E9Ef64689C56ed1577b9a112c117eEfdAD21B" + }, + "namedAccounts": { + "deployer": "0xdA53Ce2F763A3270638127CEA2826e32Cd3428e5", + "admin": "0xdA53Ce2F763A3270638127CEA2826e32Cd3428e5", + "depositContract": "0x4242424242424242424242424242424242424242", + "treasury": "0xdA53Ce2F763A3270638127CEA2826e32Cd3428e5" + } +} \ No newline at end of file diff --git a/deployment.mainnet_safe.json b/deployment.mainnet_safe.json new file mode 100644 index 0000000..1bba474 --- /dev/null +++ b/deployment.mainnet_safe.json @@ -0,0 +1,16 @@ +{ + "name": "mainnet_safe", + "chainId": "1", + "contracts": { + "ConsensusLayerFeeDispatcher": "0x2EA6BB1376531f6272e877eaF86608D18CB278d3", + "ExecutionLayerFeeDispatcher": "0xf26dB8ca5C95BA640F011653e17b2B1AEa17ABb6", + "FeeRecipient": "0x223Ca2Fe219df1AaD87787a0de4b65F541f08542", + "StakingContract": "0xC9db9bf0c9c8Fa30B09c45233E6A2D5eEfF9aBF3" + }, + "namedAccounts": { + "deployer": "0x6C74FDa18Ea90E1C61e800Dd9B4508Ac782e0Dc8", + "admin": "0x60CFAC5cD4aEed165023A81F57A0bc92D7CfEb6E", + "depositContract": "0x00000000219ab540356cBB839Cbe05303d7705Fa", + "treasury": "0xF9beDA1d78916CC89D4B3F6beF092Dc1D302112b" + } +} \ No newline at end of file diff --git a/deployments/holesky_dev_safe/.chainId b/deployments/holesky_dev_safe/.chainId new file mode 100644 index 0000000..029d1a3 --- /dev/null +++ b/deployments/holesky_dev_safe/.chainId @@ -0,0 +1 @@ +17000 \ No newline at end of file diff --git a/deployments/holesky_dev_safe/ConsensusLayerFeeDispatcher.json b/deployments/holesky_dev_safe/ConsensusLayerFeeDispatcher.json new file mode 100644 index 0000000..fd4fe0b --- /dev/null +++ b/deployments/holesky_dev_safe/ConsensusLayerFeeDispatcher.json @@ -0,0 +1,241 @@ +{ + "address": "0xee4d637Aa7966b13174CD977A8387cD767B46aD4", + "abi": [ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_version", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "errorData", + "type": "bytes" + } + ], + "name": "FeeRecipientReceiveError", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "errorData", + "type": "bytes" + } + ], + "name": "TreasuryReceiveError", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "errorData", + "type": "bytes" + } + ], + "name": "WithdrawerReceiveError", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroBalanceWithdrawal", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "withdrawer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "feeRecipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "pubKeyRoot", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "rewards", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nodeOperatorFee", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "treasuryFee", + "type": "uint256" + } + ], + "name": "Withdrawal", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "dispatch", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getStakingContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "getWithdrawer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_stakingContract", + "type": "address" + } + ], + "name": "initCLD", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0x62a37b62a9c24487f324a5d0a085b3084ce3dd2725e3159f722d9c278bc091ac", + "receipt": { + "to": null, + "from": "0xdA53Ce2F763A3270638127CEA2826e32Cd3428e5", + "contractAddress": "0xee4d637Aa7966b13174CD977A8387cD767B46aD4", + "transactionIndex": 15, + "gasUsed": "765166", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xd9051198bb979ccc1a0847ce7d08f0056a64fdbd94bfb153a3dccd82dae9e54e", + "transactionHash": "0x62a37b62a9c24487f324a5d0a085b3084ce3dd2725e3159f722d9c278bc091ac", + "logs": [], + "blockNumber": 2182291, + "cumulativeGasUsed": "2919362", + "status": 1, + "byzantium": true + }, + "args": [ + 0 + ], + "numDeployments": 1, + "solcInputHash": "e20c74109a1c61285ef1a2afbbbc70eb", + "metadata": "{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_version\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AlreadyInitialized\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorData\",\"type\":\"bytes\"}],\"name\":\"FeeRecipientReceiveError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorData\",\"type\":\"bytes\"}],\"name\":\"TreasuryReceiveError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorData\",\"type\":\"bytes\"}],\"name\":\"WithdrawerReceiveError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroBalanceWithdrawal\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"withdrawer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeRecipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"pubKeyRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rewards\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorFee\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"treasuryFee\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"dispatch\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStakingContract\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"getWithdrawer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_stakingContract\",\"type\":\"address\"}],\"name\":\"initCLD\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"author\":\"Kiln\",\"kind\":\"dev\",\"methods\":{\"getWithdrawer(bytes32)\":{\"params\":{\"_publicKeyRoot\":\"Public key root to get the owner\"}},\"initCLD(address)\":{\"params\":{\"_stakingContract\":\"Address of the Staking Contract\"}}},\"title\":\"Consensus Layer Fee Recipient\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"constructor\":{\"notice\":\"Constructor method allowing us to prevent calls to initCLFR by setting the appropriate version\"},\"dispatch(bytes32)\":{\"notice\":\"Performs a withdrawal on this contract's balance\"},\"getStakingContract()\":{\"notice\":\"Retrieve the staking contract address\"},\"getWithdrawer(bytes32)\":{\"notice\":\"Retrieve the assigned withdrawer for the given public key root\"},\"initCLD(address)\":{\"notice\":\"Initialize the contract by storing the staking contract\"}},\"notice\":\"This contract can be used to receive fees from a validator and split them with a node operator\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/contracts/ConsensusLayerFeeDispatcher.sol\":\"ConsensusLayerFeeDispatcher\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"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\",\"keccak256\":\"0x5852cbcf53e2454bb6b9c662fb32e2f35aeb3cdd5f0c341a9eb6c7d8ad0e6523\",\"license\":\"BUSL-1.1\"},\"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\",\"keccak256\":\"0x75efa5a697b32235188a62f730b7ab2a2fc5c422a93010aa0b18e93ea1bade45\",\"license\":\"MIT\"},\"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\",\"keccak256\":\"0xca5bfd023b6c479ab82320ebced9a560f9b8ffa61df27c02e83e7cb7fa23f569\",\"license\":\"MIT\"},\"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\",\"keccak256\":\"0x585a11c0ae6f9fda70d5d242ce02336bfd60fd75afed28495156307ba944f6cc\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x608060405234801561001057600080fd5b50604051610d7b380380610d7b83398101604081905261002f91610070565b610066817f6081a8ce97ad8a3951bac5b885ad6097d6b55cb45a6dd62b5069939f8c95ffef61006c60201b610aad1790919060201c565b50610089565b9055565b60006020828403121561008257600080fd5b5051919050565b610ce3806100986000396000f3fe6080604052600436106100435760003560e01c806327de9016146100ac5780635f0f28d9146100f55780638e68dce414610117578063ce1206501461012c5761007a565b3661007a576040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3480156100b857600080fd5b506100cc6100c7366004610ab1565b61013f565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b34801561010157600080fd5b50610115610110366004610aef565b610203565b005b34801561012357600080fd5b506100cc6102c6565b61011561013a366004610ab1565b6102f5565b60008061016a7ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a515490565b6040517fa74008010000000000000000000000000000000000000000000000000000000081526004810185905290915073ffffffffffffffffffffffffffffffffffffffff82169063a740080190602401602060405180830381865afa1580156101d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101fc9190610b0c565b9392505050565b600161022d7f6081a8ce97ad8a3951bac5b885ad6097d6b55cb45a6dd62b5069939f8c95ffef5490565b610238906001610b58565b8114610270576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6102997f6081a8ce97ad8a3951bac5b885ad6097d6b55cb45a6dd62b5069939f8c95ffef829055565b6102c27ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a51839055565b5050565b60006102f07ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a515490565b905090565b600061031f7ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a515490565b905047600081900361035d576040517fb6562b0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fc13d03500000000000000000000000000000000000000000000000000000000081526004810184905260009073ffffffffffffffffffffffffffffffffffffffff84169063c13d035090602401602060405180830381865afa1580156103cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ef9190610b70565b6040517fcac594df0000000000000000000000000000000000000000000000000000000081526004810186905290915060009073ffffffffffffffffffffffffffffffffffffffff85169063cac594df90602401602060405180830381865afa158015610460573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104849190610b70565b90508282801561049d57506801ae361fc1451c00008410155b80156104a7575081155b156105605760006801bc16d674ec80000082116104c457816104cf565b6801bc16d674ec8000005b90506104db8183610b92565b6040517fb86bcaf70000000000000000000000000000000000000000000000000000000081526004810189905290925073ffffffffffffffffffffffffffffffffffffffff87169063b86bcaf790602401600060405180830381600087803b15801561054657600080fd5b505af115801561055a573d6000803e3d6000fd5b50505050505b60006127108673ffffffffffffffffffffffffffffffffffffffff16631bcbfaba6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105d49190610ba9565b6105de9084610bc2565b6105e89190610bff565b905060006127108773ffffffffffffffffffffffffffffffffffffffff1663286966086040518163ffffffff1660e01b8152600401602060405180830381865afa15801561063a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061065e9190610ba9565b6106689084610bc2565b6106729190610bff565b6040517f9adf91ee000000000000000000000000000000000000000000000000000000008152600481018a905290915060009073ffffffffffffffffffffffffffffffffffffffff891690639adf91ee90602401602060405180830381865afa1580156106e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107079190610b0c565b905060008873ffffffffffffffffffffffffffffffffffffffff16633b19e84a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610756573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061077a9190610b0c565b6040517fa7400801000000000000000000000000000000000000000000000000000000008152600481018c905290915060009073ffffffffffffffffffffffffffffffffffffffff8b169063a740080190602401602060405180830381865afa1580156107eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061080f9190610b0c565b905060008073ffffffffffffffffffffffffffffffffffffffff8316610835888d610b92565b604051600081818185875af1925050503d8060008114610871576040519150601f19603f3d011682016040523d82523d6000602084013e610876565b606091505b5090925090508115156000036108c357806040517fce13343d0000000000000000000000000000000000000000000000000000000081526004016108ba9190610c3a565b60405180910390fd5b861561096f5773ffffffffffffffffffffffffffffffffffffffff84166108ea8789610b92565b604051600081818185875af1925050503d8060008114610926576040519150601f19603f3d011682016040523d82523d6000602084013e61092b565b606091505b50909250905081151560000361096f57806040517fbc98622d0000000000000000000000000000000000000000000000000000000081526004016108ba9190610c3a565b8515610a145760405173ffffffffffffffffffffffffffffffffffffffff8616908790600081818185875af1925050503d80600081146109cb576040519150601f19603f3d011682016040523d82523d6000602084013e6109d0565b606091505b509092509050811515600003610a1457806040517fe5ea83e70000000000000000000000000000000000000000000000000000000081526004016108ba9190610c3a565b8473ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f570610cb78811f3a7f90d272791d0a5e71648fb7368280519fc5866e5184db1d8f8a8f610a709190610b92565b8a610a7b818e610b92565b60408051948552602085019390935291830152606082015260800160405180910390a350505050505050505050505050565b9055565b600060208284031215610ac357600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610aec57600080fd5b50565b600060208284031215610b0157600080fd5b81356101fc81610aca565b600060208284031215610b1e57600080fd5b81516101fc81610aca565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115610b6b57610b6b610b29565b500190565b600060208284031215610b8257600080fd5b815180151581146101fc57600080fd5b600082821015610ba457610ba4610b29565b500390565b600060208284031215610bbb57600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615610bfa57610bfa610b29565b500290565b600082610c35577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600060208083528351808285015260005b81811015610c6757858101830151858201604001528201610c4b565b81811115610c79576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201604001939250505056fea26469706673582212202da726a0ebf275a4ea33e21c3cea1b07a08ff132835baf11a00a6a7218c4d46464736f6c634300080d0033", + "deployedBytecode": "0x6080604052600436106100435760003560e01c806327de9016146100ac5780635f0f28d9146100f55780638e68dce414610117578063ce1206501461012c5761007a565b3661007a576040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3480156100b857600080fd5b506100cc6100c7366004610ab1565b61013f565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b34801561010157600080fd5b50610115610110366004610aef565b610203565b005b34801561012357600080fd5b506100cc6102c6565b61011561013a366004610ab1565b6102f5565b60008061016a7ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a515490565b6040517fa74008010000000000000000000000000000000000000000000000000000000081526004810185905290915073ffffffffffffffffffffffffffffffffffffffff82169063a740080190602401602060405180830381865afa1580156101d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101fc9190610b0c565b9392505050565b600161022d7f6081a8ce97ad8a3951bac5b885ad6097d6b55cb45a6dd62b5069939f8c95ffef5490565b610238906001610b58565b8114610270576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6102997f6081a8ce97ad8a3951bac5b885ad6097d6b55cb45a6dd62b5069939f8c95ffef829055565b6102c27ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a51839055565b5050565b60006102f07ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a515490565b905090565b600061031f7ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a515490565b905047600081900361035d576040517fb6562b0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fc13d03500000000000000000000000000000000000000000000000000000000081526004810184905260009073ffffffffffffffffffffffffffffffffffffffff84169063c13d035090602401602060405180830381865afa1580156103cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ef9190610b70565b6040517fcac594df0000000000000000000000000000000000000000000000000000000081526004810186905290915060009073ffffffffffffffffffffffffffffffffffffffff85169063cac594df90602401602060405180830381865afa158015610460573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104849190610b70565b90508282801561049d57506801ae361fc1451c00008410155b80156104a7575081155b156105605760006801bc16d674ec80000082116104c457816104cf565b6801bc16d674ec8000005b90506104db8183610b92565b6040517fb86bcaf70000000000000000000000000000000000000000000000000000000081526004810189905290925073ffffffffffffffffffffffffffffffffffffffff87169063b86bcaf790602401600060405180830381600087803b15801561054657600080fd5b505af115801561055a573d6000803e3d6000fd5b50505050505b60006127108673ffffffffffffffffffffffffffffffffffffffff16631bcbfaba6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105d49190610ba9565b6105de9084610bc2565b6105e89190610bff565b905060006127108773ffffffffffffffffffffffffffffffffffffffff1663286966086040518163ffffffff1660e01b8152600401602060405180830381865afa15801561063a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061065e9190610ba9565b6106689084610bc2565b6106729190610bff565b6040517f9adf91ee000000000000000000000000000000000000000000000000000000008152600481018a905290915060009073ffffffffffffffffffffffffffffffffffffffff891690639adf91ee90602401602060405180830381865afa1580156106e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107079190610b0c565b905060008873ffffffffffffffffffffffffffffffffffffffff16633b19e84a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610756573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061077a9190610b0c565b6040517fa7400801000000000000000000000000000000000000000000000000000000008152600481018c905290915060009073ffffffffffffffffffffffffffffffffffffffff8b169063a740080190602401602060405180830381865afa1580156107eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061080f9190610b0c565b905060008073ffffffffffffffffffffffffffffffffffffffff8316610835888d610b92565b604051600081818185875af1925050503d8060008114610871576040519150601f19603f3d011682016040523d82523d6000602084013e610876565b606091505b5090925090508115156000036108c357806040517fce13343d0000000000000000000000000000000000000000000000000000000081526004016108ba9190610c3a565b60405180910390fd5b861561096f5773ffffffffffffffffffffffffffffffffffffffff84166108ea8789610b92565b604051600081818185875af1925050503d8060008114610926576040519150601f19603f3d011682016040523d82523d6000602084013e61092b565b606091505b50909250905081151560000361096f57806040517fbc98622d0000000000000000000000000000000000000000000000000000000081526004016108ba9190610c3a565b8515610a145760405173ffffffffffffffffffffffffffffffffffffffff8616908790600081818185875af1925050503d80600081146109cb576040519150601f19603f3d011682016040523d82523d6000602084013e6109d0565b606091505b509092509050811515600003610a1457806040517fe5ea83e70000000000000000000000000000000000000000000000000000000081526004016108ba9190610c3a565b8473ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f570610cb78811f3a7f90d272791d0a5e71648fb7368280519fc5866e5184db1d8f8a8f610a709190610b92565b8a610a7b818e610b92565b60408051948552602085019390935291830152606082015260800160405180910390a350505050505050505050505050565b9055565b600060208284031215610ac357600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610aec57600080fd5b50565b600060208284031215610b0157600080fd5b81356101fc81610aca565b600060208284031215610b1e57600080fd5b81516101fc81610aca565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115610b6b57610b6b610b29565b500190565b600060208284031215610b8257600080fd5b815180151581146101fc57600080fd5b600082821015610ba457610ba4610b29565b500390565b600060208284031215610bbb57600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615610bfa57610bfa610b29565b500290565b600082610c35577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600060208083528351808285015260005b81811015610c6757858101830151858201604001528201610c4b565b81811115610c79576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201604001939250505056fea26469706673582212202da726a0ebf275a4ea33e21c3cea1b07a08ff132835baf11a00a6a7218c4d46464736f6c634300080d0033", + "devdoc": { + "author": "Kiln", + "kind": "dev", + "methods": { + "getWithdrawer(bytes32)": { + "params": { + "_publicKeyRoot": "Public key root to get the owner" + } + }, + "initCLD(address)": { + "params": { + "_stakingContract": "Address of the Staking Contract" + } + } + }, + "title": "Consensus Layer Fee Recipient", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "constructor": { + "notice": "Constructor method allowing us to prevent calls to initCLFR by setting the appropriate version" + }, + "dispatch(bytes32)": { + "notice": "Performs a withdrawal on this contract's balance" + }, + "getStakingContract()": { + "notice": "Retrieve the staking contract address" + }, + "getWithdrawer(bytes32)": { + "notice": "Retrieve the assigned withdrawer for the given public key root" + }, + "initCLD(address)": { + "notice": "Initialize the contract by storing the staking contract" + } + }, + "notice": "This contract can be used to receive fees from a validator and split them with a node operator", + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/deployments/holesky_dev_safe/ExecutionLayerFeeDispatcher.json b/deployments/holesky_dev_safe/ExecutionLayerFeeDispatcher.json new file mode 100644 index 0000000..6494073 --- /dev/null +++ b/deployments/holesky_dev_safe/ExecutionLayerFeeDispatcher.json @@ -0,0 +1,241 @@ +{ + "address": "0x429Bf8D9a1ff237D0F52bd39a968Aee07C1A8C89", + "abi": [ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_version", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "errorData", + "type": "bytes" + } + ], + "name": "FeeRecipientReceiveError", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "errorData", + "type": "bytes" + } + ], + "name": "TreasuryReceiveError", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "errorData", + "type": "bytes" + } + ], + "name": "WithdrawerReceiveError", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroBalanceWithdrawal", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "withdrawer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "feeRecipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "pubKeyRoot", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "rewards", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nodeOperatorFee", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "treasuryFee", + "type": "uint256" + } + ], + "name": "Withdrawal", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "dispatch", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getStakingContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "getWithdrawer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_stakingContract", + "type": "address" + } + ], + "name": "initELD", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0xae84d127da377198c8bd0b4cebb6159a1dc6c5042b98e8a7b5f0a6d89bef124e", + "receipt": { + "to": null, + "from": "0xdA53Ce2F763A3270638127CEA2826e32Cd3428e5", + "contractAddress": "0x429Bf8D9a1ff237D0F52bd39a968Aee07C1A8C89", + "transactionIndex": 20, + "gasUsed": "642423", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x48af8545fb1a097029eda7ea807cbe913e9a027d9d43e7c3d25bc032e1cf70ea", + "transactionHash": "0xae84d127da377198c8bd0b4cebb6159a1dc6c5042b98e8a7b5f0a6d89bef124e", + "logs": [], + "blockNumber": 2182293, + "cumulativeGasUsed": "2864952", + "status": 1, + "byzantium": true + }, + "args": [ + 0 + ], + "numDeployments": 1, + "solcInputHash": "e20c74109a1c61285ef1a2afbbbc70eb", + "metadata": "{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_version\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AlreadyInitialized\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorData\",\"type\":\"bytes\"}],\"name\":\"FeeRecipientReceiveError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorData\",\"type\":\"bytes\"}],\"name\":\"TreasuryReceiveError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorData\",\"type\":\"bytes\"}],\"name\":\"WithdrawerReceiveError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroBalanceWithdrawal\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"withdrawer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeRecipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"pubKeyRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rewards\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorFee\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"treasuryFee\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"dispatch\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStakingContract\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"getWithdrawer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_stakingContract\",\"type\":\"address\"}],\"name\":\"initELD\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"author\":\"Kiln\",\"kind\":\"dev\",\"methods\":{\"getWithdrawer(bytes32)\":{\"params\":{\"_publicKeyRoot\":\"Public key root to get the owner\"}},\"initELD(address)\":{\"params\":{\"_stakingContract\":\"Address of the Staking Contract\"}}},\"title\":\"Execution Layer Fee Recipient\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"constructor\":{\"notice\":\"Constructor method allowing us to prevent calls to initCLFR by setting the appropriate version\"},\"dispatch(bytes32)\":{\"notice\":\"Performs a withdrawal on this contract's balance\"},\"getStakingContract()\":{\"notice\":\"Retrieve the staking contract address\"},\"getWithdrawer(bytes32)\":{\"notice\":\"Retrieve the assigned withdrawer for the given public key root\"},\"initELD(address)\":{\"notice\":\"Initialize the contract by storing the staking contract and the public key in storage\"}},\"notice\":\"This contract can be used to receive fees from a validator and split them with a node operator\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/contracts/ExecutionLayerFeeDispatcher.sol\":\"ExecutionLayerFeeDispatcher\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"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\",\"keccak256\":\"0x6e103df70cd81c29aee072103eacab01a66c3dfdde2b22f08459c7f280295081\",\"license\":\"BUSL-1.1\"},\"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\",\"keccak256\":\"0x75efa5a697b32235188a62f730b7ab2a2fc5c422a93010aa0b18e93ea1bade45\",\"license\":\"MIT\"},\"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\",\"keccak256\":\"0xca5bfd023b6c479ab82320ebced9a560f9b8ffa61df27c02e83e7cb7fa23f569\",\"license\":\"MIT\"},\"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\",\"keccak256\":\"0x585a11c0ae6f9fda70d5d242ce02336bfd60fd75afed28495156307ba944f6cc\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x608060405234801561001057600080fd5b50604051610b3e380380610b3e83398101604081905261002f91610070565b610066817f2a83e6f793b1db0be720b1fadaabc888b1b7e5af00b045d6d837e073182510b161006c60201b6108921790919060201c565b50610089565b9055565b60006020828403121561008257600080fd5b5051919050565b610aa6806100986000396000f3fe6080604052600436106100435760003560e01c8063068a8c8d146100ac57806327de9016146100ce5780638e68dce414610117578063ce1206501461012c5761007a565b3661007a576040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3480156100b857600080fd5b506100cc6100c73660046108bb565b61013f565b005b3480156100da57600080fd5b506100ee6100e93660046108d8565b610202565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b34801561012357600080fd5b506100ee6102c6565b6100cc61013a3660046108d8565b6102f5565b60016101697f2a83e6f793b1db0be720b1fadaabc888b1b7e5af00b045d6d837e073182510b15490565b610174906001610920565b81146101ac576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6101d57f2a83e6f793b1db0be720b1fadaabc888b1b7e5af00b045d6d837e073182510b1829055565b6101fe7fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff0112511839055565b5050565b60008061022d7fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff01125115490565b6040517fa74008010000000000000000000000000000000000000000000000000000000081526004810185905290915073ffffffffffffffffffffffffffffffffffffffff82169063a740080190602401602060405180830381865afa15801561029b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102bf9190610938565b9392505050565b60006102f07fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff01125115490565b905090565b476000819003610331576040517fb6562b0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061035b7fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff01125115490565b6040517fa74008010000000000000000000000000000000000000000000000000000000081526004810185905290915060009073ffffffffffffffffffffffffffffffffffffffff83169063a740080190602401602060405180830381865afa1580156103cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f09190610938565b6040517f9adf91ee0000000000000000000000000000000000000000000000000000000081526004810186905290915060009073ffffffffffffffffffffffffffffffffffffffff841690639adf91ee90602401602060405180830381865afa158015610461573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104859190610938565b905060008373ffffffffffffffffffffffffffffffffffffffff16633b19e84a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104f89190610938565b905060006127108573ffffffffffffffffffffffffffffffffffffffff16631bcbfaba6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561054a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061056e9190610955565b610578908861096e565b61058291906109ab565b905060006127108673ffffffffffffffffffffffffffffffffffffffff1663286966086040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f89190610955565b610602908461096e565b61060c91906109ab565b905060008073ffffffffffffffffffffffffffffffffffffffff8716610632858b6109e6565b604051600081818185875af1925050503d806000811461066e576040519150601f19603f3d011682016040523d82523d6000602084013e610673565b606091505b5090925090508115156000036106c057806040517fce13343d0000000000000000000000000000000000000000000000000000000081526004016106b791906109fd565b60405180910390fd5b831561076c5773ffffffffffffffffffffffffffffffffffffffff85166106e784866109e6565b604051600081818185875af1925050503d8060008114610723576040519150601f19603f3d011682016040523d82523d6000602084013e610728565b606091505b50909250905081151560000361076c57806040517fbc98622d0000000000000000000000000000000000000000000000000000000081526004016106b791906109fd565b82156108115760405173ffffffffffffffffffffffffffffffffffffffff8716908490600081818185875af1925050503d80600081146107c8576040519150601f19603f3d011682016040523d82523d6000602084013e6107cd565b606091505b50909250905081151560000361081157806040517fe5ea83e70000000000000000000000000000000000000000000000000000000081526004016106b791906109fd565b73ffffffffffffffffffffffffffffffffffffffff8087169088167f570610cb78811f3a7f90d272791d0a5e71648fb7368280519fc5866e5184db1d8c610858888e6109e6565b87610863818b6109e6565b60408051948552602085019390935291830152606082015260800160405180910390a350505050505050505050565b9055565b73ffffffffffffffffffffffffffffffffffffffff811681146108b857600080fd5b50565b6000602082840312156108cd57600080fd5b81356102bf81610896565b6000602082840312156108ea57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115610933576109336108f1565b500190565b60006020828403121561094a57600080fd5b81516102bf81610896565b60006020828403121561096757600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156109a6576109a66108f1565b500290565b6000826109e1577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000828210156109f8576109f86108f1565b500390565b600060208083528351808285015260005b81811015610a2a57858101830151858201604001528201610a0e565b81811115610a3c576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201604001939250505056fea2646970667358221220cf1c9da271b4075e9074a57cf80bfb44a566a3883e3d23fa21455207ed622c6764736f6c634300080d0033", + "deployedBytecode": "0x6080604052600436106100435760003560e01c8063068a8c8d146100ac57806327de9016146100ce5780638e68dce414610117578063ce1206501461012c5761007a565b3661007a576040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3480156100b857600080fd5b506100cc6100c73660046108bb565b61013f565b005b3480156100da57600080fd5b506100ee6100e93660046108d8565b610202565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b34801561012357600080fd5b506100ee6102c6565b6100cc61013a3660046108d8565b6102f5565b60016101697f2a83e6f793b1db0be720b1fadaabc888b1b7e5af00b045d6d837e073182510b15490565b610174906001610920565b81146101ac576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6101d57f2a83e6f793b1db0be720b1fadaabc888b1b7e5af00b045d6d837e073182510b1829055565b6101fe7fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff0112511839055565b5050565b60008061022d7fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff01125115490565b6040517fa74008010000000000000000000000000000000000000000000000000000000081526004810185905290915073ffffffffffffffffffffffffffffffffffffffff82169063a740080190602401602060405180830381865afa15801561029b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102bf9190610938565b9392505050565b60006102f07fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff01125115490565b905090565b476000819003610331576040517fb6562b0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061035b7fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff01125115490565b6040517fa74008010000000000000000000000000000000000000000000000000000000081526004810185905290915060009073ffffffffffffffffffffffffffffffffffffffff83169063a740080190602401602060405180830381865afa1580156103cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f09190610938565b6040517f9adf91ee0000000000000000000000000000000000000000000000000000000081526004810186905290915060009073ffffffffffffffffffffffffffffffffffffffff841690639adf91ee90602401602060405180830381865afa158015610461573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104859190610938565b905060008373ffffffffffffffffffffffffffffffffffffffff16633b19e84a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104f89190610938565b905060006127108573ffffffffffffffffffffffffffffffffffffffff16631bcbfaba6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561054a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061056e9190610955565b610578908861096e565b61058291906109ab565b905060006127108673ffffffffffffffffffffffffffffffffffffffff1663286966086040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f89190610955565b610602908461096e565b61060c91906109ab565b905060008073ffffffffffffffffffffffffffffffffffffffff8716610632858b6109e6565b604051600081818185875af1925050503d806000811461066e576040519150601f19603f3d011682016040523d82523d6000602084013e610673565b606091505b5090925090508115156000036106c057806040517fce13343d0000000000000000000000000000000000000000000000000000000081526004016106b791906109fd565b60405180910390fd5b831561076c5773ffffffffffffffffffffffffffffffffffffffff85166106e784866109e6565b604051600081818185875af1925050503d8060008114610723576040519150601f19603f3d011682016040523d82523d6000602084013e610728565b606091505b50909250905081151560000361076c57806040517fbc98622d0000000000000000000000000000000000000000000000000000000081526004016106b791906109fd565b82156108115760405173ffffffffffffffffffffffffffffffffffffffff8716908490600081818185875af1925050503d80600081146107c8576040519150601f19603f3d011682016040523d82523d6000602084013e6107cd565b606091505b50909250905081151560000361081157806040517fe5ea83e70000000000000000000000000000000000000000000000000000000081526004016106b791906109fd565b73ffffffffffffffffffffffffffffffffffffffff8087169088167f570610cb78811f3a7f90d272791d0a5e71648fb7368280519fc5866e5184db1d8c610858888e6109e6565b87610863818b6109e6565b60408051948552602085019390935291830152606082015260800160405180910390a350505050505050505050565b9055565b73ffffffffffffffffffffffffffffffffffffffff811681146108b857600080fd5b50565b6000602082840312156108cd57600080fd5b81356102bf81610896565b6000602082840312156108ea57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115610933576109336108f1565b500190565b60006020828403121561094a57600080fd5b81516102bf81610896565b60006020828403121561096757600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156109a6576109a66108f1565b500290565b6000826109e1577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000828210156109f8576109f86108f1565b500390565b600060208083528351808285015260005b81811015610a2a57858101830151858201604001528201610a0e565b81811115610a3c576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201604001939250505056fea2646970667358221220cf1c9da271b4075e9074a57cf80bfb44a566a3883e3d23fa21455207ed622c6764736f6c634300080d0033", + "devdoc": { + "author": "Kiln", + "kind": "dev", + "methods": { + "getWithdrawer(bytes32)": { + "params": { + "_publicKeyRoot": "Public key root to get the owner" + } + }, + "initELD(address)": { + "params": { + "_stakingContract": "Address of the Staking Contract" + } + } + }, + "title": "Execution Layer Fee Recipient", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "constructor": { + "notice": "Constructor method allowing us to prevent calls to initCLFR by setting the appropriate version" + }, + "dispatch(bytes32)": { + "notice": "Performs a withdrawal on this contract's balance" + }, + "getStakingContract()": { + "notice": "Retrieve the staking contract address" + }, + "getWithdrawer(bytes32)": { + "notice": "Retrieve the assigned withdrawer for the given public key root" + }, + "initELD(address)": { + "notice": "Initialize the contract by storing the staking contract and the public key in storage" + } + }, + "notice": "This contract can be used to receive fees from a validator and split them with a node operator", + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/deployments/holesky_dev_safe/FeeRecipient.json b/deployments/holesky_dev_safe/FeeRecipient.json new file mode 100644 index 0000000..af4e0fd --- /dev/null +++ b/deployments/holesky_dev_safe/FeeRecipient.json @@ -0,0 +1,169 @@ +{ + "address": "0x2f1aEd559Efb6107b6f713f6573CC66d6AAB3A35", + "abi": [ + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "getPublicKeyRoot", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getWithdrawer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_dispatcher", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "init", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0x2c910d0f2ed4a3f5dc6d7aa2eb475a4283412ab936ee789dfc0bef4eeefcbd1a", + "receipt": { + "to": null, + "from": "0xdA53Ce2F763A3270638127CEA2826e32Cd3428e5", + "contractAddress": "0x2f1aEd559Efb6107b6f713f6573CC66d6AAB3A35", + "transactionIndex": 13, + "gasUsed": "233362", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x6b509a32c7b555bc7962ecffbb019bce9f23f2529265182239a389b0c80b793a", + "transactionHash": "0x2c910d0f2ed4a3f5dc6d7aa2eb475a4283412ab936ee789dfc0bef4eeefcbd1a", + "logs": [], + "blockNumber": 2182290, + "cumulativeGasUsed": "1944940", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "e20c74109a1c61285ef1a2afbbbc70eb", + "metadata": "{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"AlreadyInitialized\",\"type\":\"error\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"getPublicKeyRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWithdrawer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_dispatcher\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"init\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"init(address,bytes32)\":{\"params\":{\"_dispatcher\":\"Address that will handle the fee dispatching\",\"_publicKeyRoot\":\"Public Key root assigned to this receiver\"}},\"withdraw()\":{\"details\":\"Can be called by any wallet as recipients are not parameters\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"getPublicKeyRoot()\":{\"notice\":\"Retrieve the assigned public key root\"},\"getWithdrawer()\":{\"notice\":\"retrieve the assigned withdrawer\"},\"init(address,bytes32)\":{\"notice\":\"Initializes the receiver\"},\"withdraw()\":{\"notice\":\"Triggers a withdrawal by sending its funds + its public key root to the dispatcher\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/contracts/FeeRecipient.sol\":\"FeeRecipient\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"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\",\"keccak256\":\"0xf788bf2e0e2112b0322904479f0704ac8fb23601f760326fec7f564ec0101090\",\"license\":\"BUSL-1.1\"},\"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\",\"keccak256\":\"0x75efa5a697b32235188a62f730b7ab2a2fc5c422a93010aa0b18e93ea1bade45\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x608060405234801561001057600080fd5b50610348806100206000396000f3fe6080604052600436106100405760003560e01c80632cc0b254146100495780633ccfd60b146100695780637d38d21f1461007e5780637f763702146100bd57005b3661004757005b005b34801561005557600080fd5b506100476100643660046102c2565b6100db565b34801561007557600080fd5b5061004761016c565b34801561008a57600080fd5b506100936101fc565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156100c957600080fd5b506001546040519081526020016100b4565b60005460ff1615610118576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff909316610100027fffffffffffffffffffffff0000000000000000000000000000000000000000009093169290921760019081179092559055565b6000546001546040517fce120650000000000000000000000000000000000000000000000000000000008152600481019190915261010090910473ffffffffffffffffffffffffffffffffffffffff169063ce1206509047906024016000604051808303818588803b1580156101e157600080fd5b505af11580156101f5573d6000803e3d6000fd5b5050505050565b600080546001546040517f27de9016000000000000000000000000000000000000000000000000000000008152600481019190915261010090910473ffffffffffffffffffffffffffffffffffffffff16906327de901690602401602060405180830381865afa158015610274573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061029891906102ee565b905090565b73ffffffffffffffffffffffffffffffffffffffff811681146102bf57600080fd5b50565b600080604083850312156102d557600080fd5b82356102e08161029d565b946020939093013593505050565b60006020828403121561030057600080fd5b815161030b8161029d565b939250505056fea2646970667358221220b126dc994bed0492fb480c2169ce4878eff8e5f378432ddaf44ae3ceeb35a64064736f6c634300080d0033", + "deployedBytecode": "0x6080604052600436106100405760003560e01c80632cc0b254146100495780633ccfd60b146100695780637d38d21f1461007e5780637f763702146100bd57005b3661004757005b005b34801561005557600080fd5b506100476100643660046102c2565b6100db565b34801561007557600080fd5b5061004761016c565b34801561008a57600080fd5b506100936101fc565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156100c957600080fd5b506001546040519081526020016100b4565b60005460ff1615610118576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff909316610100027fffffffffffffffffffffff0000000000000000000000000000000000000000009093169290921760019081179092559055565b6000546001546040517fce120650000000000000000000000000000000000000000000000000000000008152600481019190915261010090910473ffffffffffffffffffffffffffffffffffffffff169063ce1206509047906024016000604051808303818588803b1580156101e157600080fd5b505af11580156101f5573d6000803e3d6000fd5b5050505050565b600080546001546040517f27de9016000000000000000000000000000000000000000000000000000000008152600481019190915261010090910473ffffffffffffffffffffffffffffffffffffffff16906327de901690602401602060405180830381865afa158015610274573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061029891906102ee565b905090565b73ffffffffffffffffffffffffffffffffffffffff811681146102bf57600080fd5b50565b600080604083850312156102d557600080fd5b82356102e08161029d565b946020939093013593505050565b60006020828403121561030057600080fd5b815161030b8161029d565b939250505056fea2646970667358221220b126dc994bed0492fb480c2169ce4878eff8e5f378432ddaf44ae3ceeb35a64064736f6c634300080d0033", + "devdoc": { + "kind": "dev", + "methods": { + "init(address,bytes32)": { + "params": { + "_dispatcher": "Address that will handle the fee dispatching", + "_publicKeyRoot": "Public Key root assigned to this receiver" + } + }, + "withdraw()": { + "details": "Can be called by any wallet as recipients are not parameters" + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "getPublicKeyRoot()": { + "notice": "Retrieve the assigned public key root" + }, + "getWithdrawer()": { + "notice": "retrieve the assigned withdrawer" + }, + "init(address,bytes32)": { + "notice": "Initializes the receiver" + }, + "withdraw()": { + "notice": "Triggers a withdrawal by sending its funds + its public key root to the dispatcher" + } + }, + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 1850, + "contract": "src/contracts/FeeRecipient.sol:FeeRecipient", + "label": "initialized", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 1854, + "contract": "src/contracts/FeeRecipient.sol:FeeRecipient", + "label": "dispatcher", + "offset": 1, + "slot": "0", + "type": "t_contract(IFeeDispatcher)5045" + }, + { + "astId": 1857, + "contract": "src/contracts/FeeRecipient.sol:FeeRecipient", + "label": "publicKeyRoot", + "offset": 0, + "slot": "1", + "type": "t_bytes32" + } + ], + "types": { + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "encoding": "inplace", + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_contract(IFeeDispatcher)5045": { + "encoding": "inplace", + "label": "contract IFeeDispatcher", + "numberOfBytes": "20" + } + } + } +} \ No newline at end of file diff --git a/deployments/holesky_dev_safe/StakingContract.json b/deployments/holesky_dev_safe/StakingContract.json new file mode 100644 index 0000000..b19c74b --- /dev/null +++ b/deployments/holesky_dev_safe/StakingContract.json @@ -0,0 +1,1600 @@ +{ + "address": "0x124E9Ef64689C56ed1577b9a112c117eEfdAD21B", + "abi": [ + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "Deactivated", + "type": "error" + }, + { + "inputs": [], + "name": "DepositFailure", + "type": "error" + }, + { + "inputs": [], + "name": "DepositsStopped", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "DuplicateValidatorKey", + "type": "error" + }, + { + "inputs": [], + "name": "Forbidden", + "type": "error" + }, + { + "inputs": [], + "name": "FundedValidatorDeletionAttempt", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidArgument", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCall", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDepositValue", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidFee", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPublicKeys", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSignatures", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidValidatorCount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidWithdrawer", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "LastEditAfterSnapshot", + "type": "error" + }, + { + "inputs": [], + "name": "MaximumOperatorCountAlreadyReached", + "type": "error" + }, + { + "inputs": [], + "name": "NoOperators", + "type": "error" + }, + { + "inputs": [], + "name": "NotEnoughValidators", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "keyCount", + "type": "uint256" + } + ], + "name": "OperatorLimitTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "PublicKeyNotInContract", + "type": "error" + }, + { + "inputs": [], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "UnsortedIndexes", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + } + ], + "name": "ActivatedOperator", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "ChangedAdmin", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "isStopped", + "type": "bool" + } + ], + "name": "ChangedDepositsStopped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "newGlobalFee", + "type": "uint256" + } + ], + "name": "ChangedGlobalFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "operatorIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "operatorAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "feeRecipientAddress", + "type": "address" + } + ], + "name": "ChangedOperatorAddresses", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "newOperatorFee", + "type": "uint256" + } + ], + "name": "ChangedOperatorFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "operatorIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "ChangedOperatorLimit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newTreasury", + "type": "address" + } + ], + "name": "ChangedTreasury", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "address", + "name": "newWithdrawer", + "type": "address" + } + ], + "name": "ChangedWithdrawer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + } + ], + "name": "DeactivatedOperator", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "withdrawer", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "pubkey", + "type": "bytes" + } + ], + "name": "ExitRequest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "operatorAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "feeRecipientAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "NewOperator", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "_status", + "type": "bool" + } + ], + "name": "SetWithdrawerCustomizationStatus", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "operatorIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + } + ], + "name": "ValidatorKeyRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "operatorIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "publicKeys", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "signatures", + "type": "bytes" + } + ], + "name": "ValidatorKeysAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "ValidatorsEdited", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "DEPOSIT_SIZE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PUBLIC_KEY_LENGTH", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SIGNATURE_LENGTH", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_newFeeRecipient", + "type": "address" + } + ], + "name": "activateOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_operatorAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_feeRecipientAddress", + "type": "address" + } + ], + "name": "addOperator", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_keyCount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_publicKeys", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "addValidators", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKeys", + "type": "bytes" + } + ], + "name": "batchWithdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKeys", + "type": "bytes" + } + ], + "name": "batchWithdrawCLFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKeys", + "type": "bytes" + } + ], + "name": "batchWithdrawELFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_temporaryFeeRecipient", + "type": "address" + } + ], + "name": "deactivateOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "deposit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAvailableValidatorCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKey", + "type": "bytes" + } + ], + "name": "getCLFeeRecipient", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getDepositsStopped", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKey", + "type": "bytes" + } + ], + "name": "getELFeeRecipient", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "getEnabledFromPublicKeyRoot", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "getExitRequestedFromRoot", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGlobalFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + } + ], + "name": "getOperator", + "outputs": [ + { + "internalType": "address", + "name": "operatorAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "feeRecipientAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "keys", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "funded", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "available", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "deactivated", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getOperatorFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "pubKeyRoot", + "type": "bytes32" + } + ], + "name": "getOperatorFeeRecipient", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPendingAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTreasury", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_validatorIndex", + "type": "uint256" + } + ], + "name": "getValidator", + "outputs": [ + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "address", + "name": "withdrawer", + "type": "address" + }, + { + "internalType": "bool", + "name": "funded", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKey", + "type": "bytes" + } + ], + "name": "getWithdrawer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "getWithdrawerFromPublicKeyRoot", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "getWithdrawnFromPublicKeyRoot", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_admin", + "type": "address" + }, + { + "internalType": "address", + "name": "_treasury", + "type": "address" + }, + { + "internalType": "address", + "name": "_depositContract", + "type": "address" + }, + { + "internalType": "address", + "name": "_elDispatcher", + "type": "address" + }, + { + "internalType": "address", + "name": "_clDispatcher", + "type": "address" + }, + { + "internalType": "address", + "name": "_feeRecipientImplementation", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_globalFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_operatorFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "globalCommissionLimitBPS", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "operatorCommissionLimitBPS", + "type": "uint256" + } + ], + "name": "initialize_1", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "globalCommissionLimitBPS", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "operatorCommissionLimitBPS", + "type": "uint256" + } + ], + "name": "initialize_2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "_indexes", + "type": "uint256[]" + } + ], + "name": "removeValidators", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKeys", + "type": "bytes" + } + ], + "name": "requestValidatorsExit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "val", + "type": "bool" + } + ], + "name": "setDepositsStopped", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_globalFee", + "type": "uint256" + } + ], + "name": "setGlobalFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_operatorAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_feeRecipientAddress", + "type": "address" + } + ], + "name": "setOperatorAddresses", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorFee", + "type": "uint256" + } + ], + "name": "setOperatorFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_limit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_snapshot", + "type": "uint256" + } + ], + "name": "setOperatorLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newTreasury", + "type": "address" + } + ], + "name": "setTreasury", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKey", + "type": "bytes" + }, + { + "internalType": "address", + "name": "_newWithdrawer", + "type": "address" + } + ], + "name": "setWithdrawer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_enabled", + "type": "bool" + } + ], + "name": "setWithdrawerCustomizationEnabled", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "toggleWithdrawnFromPublicKeyRoot", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newAdmin", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKey", + "type": "bytes" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKey", + "type": "bytes" + } + ], + "name": "withdrawCLFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKey", + "type": "bytes" + } + ], + "name": "withdrawELFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0x62ce00047deeaf08646a1f4f663d68b887fe53cffc10ccfb26b5c0951f8d7e19", + "receipt": { + "to": null, + "from": "0xdA53Ce2F763A3270638127CEA2826e32Cd3428e5", + "contractAddress": "0x124E9Ef64689C56ed1577b9a112c117eEfdAD21B", + "transactionIndex": 18, + "gasUsed": "5075909", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x5bc274f68e17afe5c11675a06f86f330a2ab6a12224e54ccd9afe0763d473822", + "transactionHash": "0x62ce00047deeaf08646a1f4f663d68b887fe53cffc10ccfb26b5c0951f8d7e19", + "logs": [], + "blockNumber": 2182295, + "cumulativeGasUsed": "7424303", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "e20c74109a1c61285ef1a2afbbbc70eb", + "metadata": "{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"AlreadyInitialized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Deactivated\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DepositFailure\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DepositsStopped\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"DuplicateValidatorKey\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Forbidden\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FundedValidatorDeletionAttempt\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidArgument\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDepositValue\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidFee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPublicKeys\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidValidatorCount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidWithdrawer\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidZeroAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LastEditAfterSnapshot\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaximumOperatorCountAlreadyReached\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoOperators\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotEnoughValidators\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"limit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"keyCount\",\"type\":\"uint256\"}],\"name\":\"OperatorLimitTooHigh\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PublicKeyNotInContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsortedIndexes\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"}],\"name\":\"ActivatedOperator\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"ChangedAdmin\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isStopped\",\"type\":\"bool\"}],\"name\":\"ChangedDepositsStopped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newGlobalFee\",\"type\":\"uint256\"}],\"name\":\"ChangedGlobalFee\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"operatorIndex\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"operatorAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeRecipientAddress\",\"type\":\"address\"}],\"name\":\"ChangedOperatorAddresses\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newOperatorFee\",\"type\":\"uint256\"}],\"name\":\"ChangedOperatorFee\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"operatorIndex\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"limit\",\"type\":\"uint256\"}],\"name\":\"ChangedOperatorLimit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newTreasury\",\"type\":\"address\"}],\"name\":\"ChangedTreasury\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"publicKey\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newWithdrawer\",\"type\":\"address\"}],\"name\":\"ChangedWithdrawer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"}],\"name\":\"DeactivatedOperator\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"withdrawer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"publicKey\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"pubkey\",\"type\":\"bytes\"}],\"name\":\"ExitRequest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"operatorAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeRecipientAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"NewOperator\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"_status\",\"type\":\"bool\"}],\"name\":\"SetWithdrawerCustomizationStatus\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"operatorIndex\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"publicKey\",\"type\":\"bytes\"}],\"name\":\"ValidatorKeyRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"operatorIndex\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"publicKeys\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"}],\"name\":\"ValidatorKeysAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"ValidatorsEdited\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"DEPOSIT_SIZE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PUBLIC_KEY_LENGTH\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SIGNATURE_LENGTH\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_newFeeRecipient\",\"type\":\"address\"}],\"name\":\"activateOperator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_operatorAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_feeRecipientAddress\",\"type\":\"address\"}],\"name\":\"addOperator\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_keyCount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"_publicKeys\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"_signatures\",\"type\":\"bytes\"}],\"name\":\"addValidators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKeys\",\"type\":\"bytes\"}],\"name\":\"batchWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKeys\",\"type\":\"bytes\"}],\"name\":\"batchWithdrawCLFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKeys\",\"type\":\"bytes\"}],\"name\":\"batchWithdrawELFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_temporaryFeeRecipient\",\"type\":\"address\"}],\"name\":\"deactivateOperator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAvailableValidatorCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKey\",\"type\":\"bytes\"}],\"name\":\"getCLFeeRecipient\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDepositsStopped\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKey\",\"type\":\"bytes\"}],\"name\":\"getELFeeRecipient\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"getEnabledFromPublicKeyRoot\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"getExitRequestedFromRoot\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getGlobalFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"}],\"name\":\"getOperator\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"operatorAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeRecipientAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"limit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"keys\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"funded\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"deactivated\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getOperatorFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"pubKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"getOperatorFeeRecipient\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPendingAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTreasury\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_validatorIndex\",\"type\":\"uint256\"}],\"name\":\"getValidator\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"publicKey\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"withdrawer\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"funded\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKey\",\"type\":\"bytes\"}],\"name\":\"getWithdrawer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"getWithdrawerFromPublicKeyRoot\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"getWithdrawnFromPublicKeyRoot\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_admin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_treasury\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_depositContract\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_elDispatcher\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_clDispatcher\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_feeRecipientImplementation\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_globalFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_operatorFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"globalCommissionLimitBPS\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"operatorCommissionLimitBPS\",\"type\":\"uint256\"}],\"name\":\"initialize_1\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"globalCommissionLimitBPS\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"operatorCommissionLimitBPS\",\"type\":\"uint256\"}],\"name\":\"initialize_2\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"_indexes\",\"type\":\"uint256[]\"}],\"name\":\"removeValidators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKeys\",\"type\":\"bytes\"}],\"name\":\"requestValidatorsExit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"val\",\"type\":\"bool\"}],\"name\":\"setDepositsStopped\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_globalFee\",\"type\":\"uint256\"}],\"name\":\"setGlobalFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_operatorAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_feeRecipientAddress\",\"type\":\"address\"}],\"name\":\"setOperatorAddresses\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorFee\",\"type\":\"uint256\"}],\"name\":\"setOperatorFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_limit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_snapshot\",\"type\":\"uint256\"}],\"name\":\"setOperatorLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newTreasury\",\"type\":\"address\"}],\"name\":\"setTreasury\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKey\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"_newWithdrawer\",\"type\":\"address\"}],\"name\":\"setWithdrawer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"_enabled\",\"type\":\"bool\"}],\"name\":\"setWithdrawerCustomizationEnabled\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"toggleWithdrawnFromPublicKeyRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newAdmin\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKey\",\"type\":\"bytes\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKey\",\"type\":\"bytes\"}],\"name\":\"withdrawCLFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKey\",\"type\":\"bytes\"}],\"name\":\"withdrawELFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"author\":\"Kiln\",\"kind\":\"dev\",\"methods\":{\"acceptOwnership()\":{\"details\":\"Only callable by new admin\"},\"activateOperator(uint256,address)\":{\"params\":{\"_newFeeRecipient\":\"Sets the fee recipient address\",\"_operatorIndex\":\"Operator Index\"}},\"addOperator(address,address)\":{\"details\":\"Only callable by admin\",\"params\":{\"_feeRecipientAddress\":\"Privileged operator address used to manage rewards and operator addresses\",\"_operatorAddress\":\"Operator address allowed to add / remove validators\"}},\"addValidators(uint256,uint256,bytes,bytes)\":{\"details\":\"Only callable by operator\",\"params\":{\"_keyCount\":\"Number of keys added\",\"_operatorIndex\":\"Operator Index\",\"_publicKeys\":\"Concatenated _keyCount public keys\",\"_signatures\":\"Concatenated _keyCount signatures\"}},\"batchWithdraw(bytes)\":{\"details\":\"Funds are sent to the withdrawer account\",\"params\":{\"_publicKeys\":\"Validators to withdraw fees from\"}},\"batchWithdrawCLFee(bytes)\":{\"details\":\"Funds are sent to the withdrawer accountThis method is public on purpose\",\"params\":{\"_publicKeys\":\"Validators to withdraw Consensus Layer Fees from\"}},\"batchWithdrawELFee(bytes)\":{\"details\":\"Funds are sent to the withdrawer accountThis method is public on purpose\",\"params\":{\"_publicKeys\":\"Validators to withdraw Execution Layer Fees from\"}},\"deactivateOperator(uint256,address)\":{\"params\":{\"_operatorIndex\":\"Operator Index\",\"_temporaryFeeRecipient\":\"Temporary address to receive funds decided by the system admin\"}},\"deposit()\":{\"details\":\"A multiple of 32 ETH should be sent\"},\"getCLFeeRecipient(bytes)\":{\"params\":{\"_publicKey\":\"Validator to get the recipient\"}},\"getELFeeRecipient(bytes)\":{\"params\":{\"_publicKey\":\"Validator to get the recipient\"}},\"getEnabledFromPublicKeyRoot(bytes32)\":{\"params\":{\"_publicKeyRoot\":\"Hash of the public key\"}},\"getExitRequestedFromRoot(bytes32)\":{\"params\":{\"_publicKeyRoot\":\"Public Key Root to check\"}},\"getOperator(uint256)\":{\"params\":{\"_operatorIndex\":\"Operator index\"}},\"getValidator(uint256,uint256)\":{\"params\":{\"_operatorIndex\":\"Index of the operator running the validator\",\"_validatorIndex\":\"Index of the validator\"}},\"getWithdrawer(bytes)\":{\"params\":{\"_publicKey\":\"Public Key to check\"}},\"getWithdrawerFromPublicKeyRoot(bytes32)\":{\"params\":{\"_publicKeyRoot\":\"Hash of the public key\"}},\"getWithdrawnFromPublicKeyRoot(bytes32)\":{\"params\":{\"_publicKeyRoot\":\"Public Key Root of the validator\"}},\"removeValidators(uint256,uint256[])\":{\"details\":\"Only callable by operatorIndexes should be provided in decreasing orderThe limit will be set to the lowest removed operator index to ensure all changes above the lowest removed validator key are verified by the system administrator\",\"params\":{\"_indexes\":\"List of indexes to delete, in decreasing order\",\"_operatorIndex\":\"Operator Index\"}},\"setGlobalFee(uint256)\":{\"params\":{\"_globalFee\":\"Fee in Basis Point\"}},\"setOperatorAddresses(uint256,address,address)\":{\"details\":\"Only callable by fee recipient address manager\",\"params\":{\"_feeRecipientAddress\":\"New operator address for reward management\",\"_operatorAddress\":\"New operator address for operations management\",\"_operatorIndex\":\"Index of the operator to update\"}},\"setOperatorFee(uint256)\":{\"params\":{\"_operatorFee\":\"Fee in Basis Point\"}},\"setOperatorLimit(uint256,uint256,uint256)\":{\"details\":\"Only callable by adminLimit should not exceed the validator key count of the operatorKeys should be registered before limit is increasedAllows all keys to be verified by the system admin before limit is increased\",\"params\":{\"_limit\":\"New staking limit\",\"_operatorIndex\":\"Operator Index\",\"_snapshot\":\"Block number at which verification was done\"}},\"setTreasury(address)\":{\"details\":\"Only callable by admin\",\"params\":{\"_newTreasury\":\"New Treasury address\"}},\"setWithdrawer(bytes,address)\":{\"details\":\"Only callable by current public key withdrawer\",\"params\":{\"_newWithdrawer\":\"New withdrawer address\",\"_publicKey\":\"Public key to change withdrawer\"}},\"setWithdrawerCustomizationEnabled(bool)\":{\"params\":{\"_enabled\":\"True to allow users to customize the withdrawer\"}},\"toggleWithdrawnFromPublicKeyRoot(bytes32)\":{\"params\":{\"_publicKeyRoot\":\"Public Key Root of the validator\"}},\"transferOwnership(address)\":{\"details\":\"Only callable by admin\",\"params\":{\"_newAdmin\":\"New Administrator address\"}},\"withdraw(bytes)\":{\"details\":\"Reverts if any is null\",\"params\":{\"_publicKey\":\"Validator to withdraw Execution and Consensus Layer Fees from\"}},\"withdrawCLFee(bytes)\":{\"details\":\"Funds are sent to the withdrawer account\",\"params\":{\"_publicKey\":\"Validator to withdraw Consensus Layer Fees from\"}},\"withdrawELFee(bytes)\":{\"details\":\"Funds are sent to the withdrawer account\",\"params\":{\"_publicKey\":\"Validator to withdraw Execution Layer Fees from\"}}},\"title\":\"Ethereum Staking Contract\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"acceptOwnership()\":{\"notice\":\"New admin must accept its role by calling this method\"},\"activateOperator(uint256,address)\":{\"notice\":\"Activates an operator, without changing its 0 staking limit\"},\"addOperator(address,address)\":{\"notice\":\"Add new operator\"},\"addValidators(uint256,uint256,bytes,bytes)\":{\"notice\":\"Add new validator public keys and signatures\"},\"batchWithdraw(bytes)\":{\"notice\":\"Withdraw both Consensus and Execution Layer Fees for given validators public keys\"},\"batchWithdrawCLFee(bytes)\":{\"notice\":\"Withdraw the Consensus Layer Fee for given validators public keys\"},\"batchWithdrawELFee(bytes)\":{\"notice\":\"Withdraw the Execution Layer Fee for given validators public keys\"},\"deactivateOperator(uint256,address)\":{\"notice\":\"Deactivates an operator and changes the fee recipient address and the staking limit\"},\"deposit()\":{\"notice\":\"Explicit deposit method using msg.sender\"},\"getAdmin()\":{\"notice\":\"Retrieve system admin\"},\"getAvailableValidatorCount()\":{\"notice\":\"Get the total available keys that are ready to be used for deposits\"},\"getCLFeeRecipient(bytes)\":{\"notice\":\"Compute the Consensus Layer Fee recipient address for a given validator public key\"},\"getDepositsStopped()\":{\"notice\":\"Returns false if the users can deposit, true if deposits are stopped\"},\"getELFeeRecipient(bytes)\":{\"notice\":\"Compute the Execution Layer Fee recipient address for a given validator public key\"},\"getEnabledFromPublicKeyRoot(bytes32)\":{\"notice\":\"Retrieve the enabled status of public key root, true if the key is in the contract\"},\"getExitRequestedFromRoot(bytes32)\":{\"notice\":\"Retrieve whether the validator exit has been requestedIn case the validator is not enabled, it will return false\"},\"getGlobalFee()\":{\"notice\":\"Retrieve the global fee\"},\"getOperator(uint256)\":{\"notice\":\"Retrieve operator details\"},\"getOperatorFee()\":{\"notice\":\"Retrieve the operator fee\"},\"getOperatorFeeRecipient(bytes32)\":{\"notice\":\"Retrieve the Execution & Consensus Layer Fee operator recipient for a given public key\"},\"getPendingAdmin()\":{\"notice\":\"Get the new admin's address previously set for an ownership transfer\"},\"getTreasury()\":{\"notice\":\"Retrieve system treasury\"},\"getValidator(uint256,uint256)\":{\"notice\":\"Get details about a validator\"},\"getWithdrawer(bytes)\":{\"notice\":\"Retrieve withdrawer of public keyIn case the validator is not enabled, it will return address(0)\"},\"getWithdrawerFromPublicKeyRoot(bytes32)\":{\"notice\":\"Retrieve withdrawer of public key rootIn case the validator is not enabled, it will return address(0)\"},\"getWithdrawnFromPublicKeyRoot(bytes32)\":{\"notice\":\"Return true if the validator already went through the exit logicIn case the validator is not enabled, it will return false\"},\"removeValidators(uint256,uint256[])\":{\"notice\":\"Remove unfunded validators\"},\"setDepositsStopped(bool)\":{\"notice\":\"Utility to stop or allow deposits\"},\"setGlobalFee(uint256)\":{\"notice\":\"Change the Global fee\"},\"setOperatorAddresses(uint256,address,address)\":{\"notice\":\"Set new operator addresses (operations and reward management)\"},\"setOperatorFee(uint256)\":{\"notice\":\"Change the Operator fee\"},\"setOperatorLimit(uint256,uint256,uint256)\":{\"notice\":\"Set operator staking limits\"},\"setTreasury(address)\":{\"notice\":\"Set new treasury\"},\"setWithdrawer(bytes,address)\":{\"notice\":\"Set withdrawer for public key\"},\"setWithdrawerCustomizationEnabled(bool)\":{\"notice\":\"Changes the behavior of the withdrawer customization logic\"},\"toggleWithdrawnFromPublicKeyRoot(bytes32)\":{\"notice\":\"Allows the CLDispatcher to signal a validator went through the exit logic\"},\"transferOwnership(address)\":{\"notice\":\"Set new admin\"},\"withdraw(bytes)\":{\"notice\":\"Withdraw both Consensus and Execution Layer Fee for a given validator public key\"},\"withdrawCLFee(bytes)\":{\"notice\":\"Withdraw the Consensus Layer Fee for a given validator public key\"},\"withdrawELFee(bytes)\":{\"notice\":\"Withdraw the Execution Layer Fee for a given validator public key\"}},\"notice\":\"You can use this contract to store validator keys and have users fund them and trigger deposits.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/contracts/StakingContract.sol\":\"StakingContract\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"@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\",\"keccak256\":\"0x1cc0efb01cbf008b768fd7b334786a6e358809198bb7e67f1c530af4957c6a21\",\"license\":\"MIT\"},\"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 /// \\u2588\\u2588 \\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588\\n /// \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\n /// \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588\\n /// \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\n /// \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\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\",\"keccak256\":\"0x48b93da8defc2f12c821b6490cce69bede2dacdd813fe205bbdbeb6107a6bd96\",\"license\":\"BUSL-1.1\"},\"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\",\"keccak256\":\"0x10ced526f2842c879ff63bf37a47d121d56898609456f98df1f3cff0a768b2c9\",\"license\":\"MIT\"},\"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\",\"keccak256\":\"0x2448a6378aa26099508ce00bf1eff7ea293ae97eece97d931a6f500256a2c475\",\"license\":\"MIT\"},\"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\",\"keccak256\":\"0xf98a3b0b668d8850c79a255e0567fd85bf586503b276e95aa604add0b1033dbc\",\"license\":\"MIT\"},\"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\",\"keccak256\":\"0xafac6beeeb9d8b004d643056f2a0d860c586759a2e4464b057066f4d6792b7e9\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x608060405234801561001057600080fd5b50615b2e80620000216000396000f3fe6080604052600436106103015760003560e01c8063a74008011161018f578063d0e30db0116100e1578063e99454f51161008a578063f2fde38b11610064578063f2fde38b146108b1578063fe0e4191146108d1578063fe38231c146108f157610310565b8063e99454f514610851578063ef5e468214610871578063f0f442601461089157610310565b8063dfbe50dd116100bb578063dfbe50dd146107f1578063e00cb6ca14610811578063e8a0c1211461083157610310565b8063d0e30db0146107a9578063d243d69d146107b1578063d2a42747146107d157610310565b8063beee4bbf11610143578063c13d03501161011d578063c13d035014610744578063cac594df14610774578063d04681561461079457610310565b8063beee4bbf146106ef578063bf15af561461070f578063bf509bd41461072457610310565b8063b6b06dec11610174578063b6b06dec1461068f578063b747e7dd146106af578063b86bcaf7146106cf57610310565b8063a74008011461065a578063b4336b841461067a57610310565b806336bf3325116102535780637680fdf5116101fc5780638df4e474116101d65780638df4e474146105fa5780639adf91ee1461061a578063a4d8d2c41461063a57610310565b80637680fdf5146105a557806379ba5097146105c55780638a1af4c4146105da57610310565b806363b4118f1161022d57806363b4118f146105405780636d336fe0146105705780636e9960c31461059057610310565b806336bf3325146104f95780633b19e84a14610516578063540bc5ea1461052b57610310565b80631d095805116102b5578063286966081161028f57806328696608146104a4578063291206f6146104b95780632ba03a79146104d957610310565b80631d0958051461042c5780631ee133431461044c578063227e80fa1461048457610310565b80630ffab6c2116102e65780630ffab6c2146103c95780631864636c146103e95780631bcbfaba1461040957610310565b806305f63c8a146103425780630968f264146103a957610310565b366103105761030e610906565b005b6040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34801561034e57600080fd5b5061036261035d366004615249565b610a62565b604080516001600160a01b039889168152979096166020880152948601939093526060850191909152608084015260a0830152151560c082015260e0015b60405180910390f35b3480156103b557600080fd5b5061030e6103c43660046152ab565b610b78565b3480156103d557600080fd5b5061030e6103e43660046152ab565b610c49565b3480156103f557600080fd5b5061030e6104043660046152ed565b610cfe565b34801561041557600080fd5b5061041e611c77565b6040519081526020016103a0565b34801561043857600080fd5b5061030e610447366004615249565b611c86565b34801561045857600080fd5b5061046c6104673660046152ab565b611d3f565b6040516001600160a01b0390911681526020016103a0565b34801561049057600080fd5b5061030e61049f366004615388565b611d8c565b3480156104b057600080fd5b5061041e611f15565b3480156104c557600080fd5b5061030e6104d4366004615249565b611f1f565b3480156104e557600080fd5b5061030e6104f43660046152ab565b611fd1565b34801561050557600080fd5b5061041e6801bc16d674ec80000081565b34801561052257600080fd5b5061046c612010565b34801561053757600080fd5b5061041e606081565b34801561054c57600080fd5b5061056061055b3660046153b4565b61201a565b6040516103a0949392919061542e565b34801561057c57600080fd5b5061030e61058b3660046153b4565b61229b565b34801561059c57600080fd5b5061046c61237b565b3480156105b157600080fd5b5061030e6105c0366004615475565b612385565b3480156105d157600080fd5b5061030e6123fe565b3480156105e657600080fd5b5061041e6105f536600461549e565b61247e565b34801561060657600080fd5b5061030e610615366004615475565b61267a565b34801561062657600080fd5b5061046c610635366004615249565b6126ed565b34801561064657600080fd5b5061030e6106553660046154c8565b6127e8565b34801561066657600080fd5b5061046c610675366004615249565b6129c4565b34801561068657600080fd5b5061041e612a00565b34801561069b57600080fd5b5061030e6106aa3660046152ab565b612a0a565b3480156106bb57600080fd5b5061030e6106ca366004615504565b612b52565b3480156106db57600080fd5b5061030e6106ea366004615249565b612f1d565b3480156106fb57600080fd5b5061030e61070a366004615587565b612f7d565b34801561071b57600080fd5b5061041e603081565b34801561073057600080fd5b5061030e61073f3660046152ab565b613190565b34801561075057600080fd5b5061076461075f366004615249565b613212565b60405190151581526020016103a0565b34801561078057600080fd5b5061076461078f366004615249565b61321d565b3480156107a057600080fd5b5061046c61323b565b61030e613245565b3480156107bd57600080fd5b5061046c6107cc3660046152ab565b61324f565b3480156107dd57600080fd5b5061030e6107ec366004615388565b613290565b3480156107fd57600080fd5b5061030e61080c3660046155b3565b61348c565b34801561081d57600080fd5b5061046c61082c3660046152ab565b6135ef565b34801561083d57600080fd5b5061030e61084c3660046152ab565b613633565b34801561085d57600080fd5b5061030e61086c366004615651565b6136f6565b34801561087d57600080fd5b5061030e61088c3660046152ab565b61385d565b34801561089d57600080fd5b5061030e6108ac36600461569c565b613912565b3480156108bd57600080fd5b5061030e6108cc36600461569c565b61398f565b3480156108dd57600080fd5b506107646108ec366004615249565b6139d0565b3480156108fd57600080fd5b506107646139f8565b61090e6139fe565b15610945576040517f753ed9db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34158061096357506109606801bc16d674ec800000346156e6565b15155b1561099a576040517f428243e200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006109a4613a32565b905060006109bb6801bc16d674ec80000034615729565b9050818111156109f7576040517fae575a8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054600003610a53576040517fddf9d24500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5d8284613a5c565b505050565b6000808080808080807fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054909150891015610b6c576040805180820182526000808252602080830182815260028e901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f082529382205460068e901b60c0161c63ffffffff8181168552911c1690925290506000826000018b81548110610b0e57610b0e61573d565b600091825260209182902060069091020180546001820154600283015460038401546005909401549487015196516001600160a01b039384169f50919092169c50909a5090985063ffffffff93841697509290921694505060ff1691505b50919395979092949650565b610bb782828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613a7992505050565b610bff82828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509250610bfa9150613aed9050565b613b17565b610c4582828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250610bfa9150613ca29050565b5050565b610c546030826156e6565b15610c8b576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d576000610cdc84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b9050610ce781613a79565b610cf5816001610bfa613ca2565b50603001610c8e565b82610d07613e28565b6001600160a01b0316330361152a576000829003610d51576040517fa9cb9e0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051808201825260008082526020808301828152600289901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f082529390912054600688901b60c0161c63ffffffff8181168452911c1691829052907fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b39907fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec93902908686610e0260018261576c565b818110610e1157610e1161573d565b905060200201351015610e50576040517f34947ea100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8581101561140d57600081118015610ea557508686610e7360018461576c565b818110610e8257610e8261573d565b90506020020135878783818110610e9b57610e9b61573d565b9050602002013510155b15610edc576040517f35061dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610fc8846000018a81548110610ef657610ef661573d565b9060005260206000209060060201600301898985818110610f1957610f1961573d565b9050602002013581548110610f3057610f3061573d565b906000526020600020018054610f4590615783565b80601f0160208091040260200160405190810160405280929190818152602001828054610f7190615783565b8015610fbe5780601f10610f9357610100808354040283529160200191610fbe565b820191906000526020600020905b815481529060010190602001808311610fa157829003601f168201915b5050505050613e52565b600081815260208590526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000169055845490915089907f794aacb42d1ea2e7f72809b74e3ce124325a51c3715b873c36807d3ca37e4fd0908690839081106110375761103761573d565b90600052602060002090600602016003018a8a8681811061105a5761105a61573d565b90506020020135815481106110715761107161573d565b9060005260206000200160405161108891906157d6565b60405180910390a26001846000018a815481106110a7576110a761573d565b9060005260206000209060060201600301805490506110c6919061576c565b8888848181106110d8576110d861573d565b905060200201350361118f578360000189815481106110f9576110f961573d565b906000526020600020906006020160030180548061111957611119615896565b6001900381819060005260206000200160006111359190615085565b905583600001898154811061114c5761114c61573d565b906000526020600020906006020160040180548061116c5761116c615896565b6001900381819060005260206000200160006111889190615085565b9055611404565b8360000189815481106111a4576111a461573d565b90600052602060002090600602016003016001856000018b815481106111cc576111cc61573d565b9060005260206000209060060201600301805490506111eb919061576c565b815481106111fb576111fb61573d565b90600052602060002001846000018a8154811061121a5761121a61573d565b906000526020600020906006020160030189898581811061123d5761123d61573d565b90506020020135815481106112545761125461573d565b9060005260206000200190805461126a90615783565b6112759291906150bf565b5083600001898154811061128b5761128b61573d565b90600052602060002090600602016003018054806112ab576112ab615896565b6001900381819060005260206000200160006112c79190615085565b90558360000189815481106112de576112de61573d565b90600052602060002090600602016004016001856000018b815481106113065761130661573d565b906000526020600020906006020160040180549050611325919061576c565b815481106113355761133561573d565b90600052602060002001846000018a815481106113545761135461573d565b90600052602060002090600602016004018989858181106113775761137761573d565b905060200201358154811061138e5761138e61573d565b906000526020600020019080546113a490615783565b6113af9291906150bf565b508360000189815481106113c5576113c561573d565b90600052602060002090600602016004018054806113e5576113e5615896565b6001900381819060005260206000200160006114019190615085565b90555b50600101610e53565b508160000187815481106114235761142361573d565b6000918252602090912060026006909202010154868661144460018261576c565b8181106114535761145361573d565b90506020020135101561151157858561146d60018261576c565b81811061147c5761147c61573d565b905060200201358260000188815481106114985761149861573d565b60009182526020909120600260069092020101557fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e038787876114db60018261576c565b8181106114ea576114ea61573d565b90506020020135604051611508929190918252602082015260400190565b60405180910390a15b611519613ec7565b61152287613f05565b505050611c71565b611533816140fe565b600082900361156e576040517fa9cb9e0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051808201825260008082526020808301828152600289901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f082529390912054600688901b60c0161c63ffffffff8181168452911c1691829052907fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b39907fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec9390290868661161f60018261576c565b81811061162e5761162e61573d565b90506020020135101561166d576040517f34947ea100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b85811015611b58576000811180156116c25750868661169060018461576c565b81811061169f5761169f61573d565b905060200201358787838181106116b8576116b861573d565b9050602002013510155b156116f9576040517f35061dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611713846000018a81548110610ef657610ef661573d565b600081815260208590526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000169055845490915089907f794aacb42d1ea2e7f72809b74e3ce124325a51c3715b873c36807d3ca37e4fd0908690839081106117825761178261573d565b90600052602060002090600602016003018a8a868181106117a5576117a561573d565b90506020020135815481106117bc576117bc61573d565b906000526020600020016040516117d391906157d6565b60405180910390a26001846000018a815481106117f2576117f261573d565b906000526020600020906006020160030180549050611811919061576c565b8888848181106118235761182361573d565b90506020020135036118da578360000189815481106118445761184461573d565b906000526020600020906006020160030180548061186457611864615896565b6001900381819060005260206000200160006118809190615085565b90558360000189815481106118975761189761573d565b90600052602060002090600602016004018054806118b7576118b7615896565b6001900381819060005260206000200160006118d39190615085565b9055611b4f565b8360000189815481106118ef576118ef61573d565b90600052602060002090600602016003016001856000018b815481106119175761191761573d565b906000526020600020906006020160030180549050611936919061576c565b815481106119465761194661573d565b90600052602060002001846000018a815481106119655761196561573d565b90600052602060002090600602016003018989858181106119885761198861573d565b905060200201358154811061199f5761199f61573d565b906000526020600020019080546119b590615783565b6119c09291906150bf565b508360000189815481106119d6576119d661573d565b90600052602060002090600602016003018054806119f6576119f6615896565b600190038181906000526020600020016000611a129190615085565b9055836000018981548110611a2957611a2961573d565b90600052602060002090600602016004016001856000018b81548110611a5157611a5161573d565b906000526020600020906006020160040180549050611a70919061576c565b81548110611a8057611a8061573d565b90600052602060002001846000018a81548110611a9f57611a9f61573d565b9060005260206000209060060201600401898985818110611ac257611ac261573d565b9050602002013581548110611ad957611ad961573d565b90600052602060002001908054611aef90615783565b611afa9291906150bf565b50836000018981548110611b1057611b1061573d565b9060005260206000209060060201600401805480611b3057611b30615896565b600190038181906000526020600020016000611b4c9190615085565b90555b50600101611670565b50816000018781548110611b6e57611b6e61573d565b60009182526020909120600260069092020101548686611b8f60018261576c565b818110611b9e57611b9e61573d565b905060200201351015611c5c578585611bb860018261576c565b818110611bc757611bc761573d565b90506020020135826000018881548110611be357611be361573d565b60009182526020909120600260069092020101557fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e03878787611c2660018261576c565b818110611c3557611c3561573d565b90506020020135604051611c53929190918252602082015260400190565b60405180910390a15b611c64613ec7565b611c6d87613f05565b5050505b50505050565b6000611c816141ae565b905090565b611c8e613e28565b6001600160a01b0316336001600160a01b031614611cbe576040516282b42960e81b815260040160405180910390fd5b611cc66141d8565b811115611cff576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d0881614208565b6040518181527fd894096cd1f7e89d9b748c7c2358cb699a790a05e97dcde228fe5949b4e80743906020015b60405180910390a150565b6000611d8383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250614231915050565b90505b92915050565b611d94613e28565b6001600160a01b0316336001600160a01b031614611dc4576040516282b42960e81b815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054600090829085908110611dfc57611dfc61573d565b600091825260209182902060069190910201600501805460ff1916921515929092179091556040518481527f8d0ba049731b0417f85546bc7503de241522c61e7464e87cceff56d3f2586dbc910160405180910390a181816000018481548110611e6857611e6861573d565b906000526020600020906006020160010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f97568892aa9e69db7c72b9f32e6d4fe3c0e53b9728a8c0a0f1c2a7eb47c48ad383826000018581548110611ed457611ed461573d565b600091825260209182902060069091020154604080519384526001600160a01b039182169284019290925285169082015260600160405180910390a1505050565b6000611c816142db565b611f27613e28565b6001600160a01b0316336001600160a01b031614611f57576040516282b42960e81b815260040160405180910390fd5b611f5f614305565b811115611f98576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611fa181614335565b6040518181527fecd6fc650620aa722cafc3c3e871fa813947eae6f856982d7510a8ff101a507890602001611d34565b610bff82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613a7992505050565b6000611c8161435e565b606080600080807fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3990508060000187815481106120595761205961573d565b9060005260206000209060060201600301868154811061207b5761207b61573d565b90600052602060002001805461209090615783565b80601f01602080910402602001604051908101604052809291908181526020018280546120bc90615783565b80156121095780601f106120de57610100808354040283529160200191612109565b820191906000526020600020905b8154815290600101906020018083116120ec57829003601f168201915b505050505094508060000187815481106121255761212561573d565b906000526020600020906006020160040186815481106121475761214761573d565b90600052602060002001805461215c90615783565b80601f016020809104026020016040519081016040528092919081815260200182805461218890615783565b80156121d55780601f106121aa576101008083540402835291602001916121d5565b820191906000526020600020905b8154815290600101906020018083116121b857829003601f168201915b505050505093506122226121e886613e52565b60009081527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260409020546001600160a01b031690565b6040805180820182526000808252602080830182815260028d901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f08252939091205460068c901b60c0161c63ffffffff8181168452911c169091529093506020015163ffffffff16861091505092959194509250565b60026122a5614388565b6122b09060016158c5565b81146122e8576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6122f1816143b2565b61271083111561232d576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612336836143db565b612710821115612372576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5d8261440e565b6000611c81613e28565b61238d613e28565b6001600160a01b0316336001600160a01b0316146123bd576040516282b42960e81b815260040160405180910390fd5b60405181151581527f8caf502b0bfa5915a541f14f8b0d091d4837c201b74aa2a7a7f52487a5fe8a819060200160405180910390a16123fb8161443c565b50565b600061240861446a565b9050336001600160a01b03821614612432576040516282b42960e81b815260040160405180910390fd5b61243b81614493565b61244560006144bc565b6040516001600160a01b03821681527ff29c1089a9594c030706593e345bd8f70a26125bfb7bf4c54e757e20f456fd1c90602001611d34565b6000612488613e28565b6001600160a01b0316336001600160a01b0316146124b8576040516282b42960e81b815260040160405180910390fd5b6040805160c0810182526000808252602082018190529181018290526060808201819052608082015260a08101919091527fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054909190600103612548576040517fa20c741300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03808616825284811660208084019182528454600181810187556000878152839020865160069093020180549286167fffffffffffffffffffffffff00000000000000000000000000000000000000009384161781559351908401805491909516911617909255604083015160028201556060830151805184936125da92600385019291019061514a565b50608082015180516125f691600484019160209091019061514a565b5060a091909101516005909101805460ff191691151591909117905581546000906126239060019061576c565b604080516001600160a01b03808a168252881660208201529081018290529091507f2b3c4db2c0f4f51da09c2510a63e1d90235e486e8f075a609103a5c7a07422179060600160405180910390a195945050505050565b612682613e28565b6001600160a01b0316336001600160a01b0316146126b2576040516282b42960e81b815260040160405180910390fd5b6126bb816144e4565b60405181151581527f96639403db35a7ef32b25543bec54f73278d8c5ca82883ef1385ddcb27afa7dc90602001611d34565b60008181527fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec93902602052604081205460ff1615158103612758576040517f9e44bfc500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281527fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec9390260205260409020547fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980549091610100900463ffffffff169081106127c5576127c561573d565b60009182526020909120600160069092020101546001600160a01b031692915050565b8260007fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980548390811061281e5761281e61573d565b60009182526020909120600690910201600581015490915060ff1615612870576040517f1142a68c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018101546001600160a01b0316331461289c576040516282b42960e81b815260040160405180910390fd5b6128a58461450d565b6128ae8361450d565b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b39805485908290889081106128e5576128e561573d565b906000526020600020906006020160000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550838160000187815481106129305761293061573d565b60009182526020918290206006919091020160010180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0393841617905560408051898152888416928101929092529186168183015290517f97568892aa9e69db7c72b9f32e6d4fe3c0e53b9728a8c0a0f1c2a7eb47c48ad3916060908290030190a1505050505050565b60008181527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260408120546001600160a01b0316611d86565b6000611c81613a32565b612a156030826156e6565b15612a4c576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d576000612a9d84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b90506000612aaa82613e52565b60008181527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260409020549091506001600160a01b0316338114612b03576040516282b42960e81b815260040160405180910390fd5b612b0e82600161454d565b7fcb773ebd49049a1f68200f0814f547dec2c54293ff2c6a6a4f4d98d6d9cb12298184604051612b3f9291906158dd565b60405180910390a1505050603001612a4f565b85612b5c816140fe565b85600003612b96576040517fa9cb9e0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612ba16030856156e6565b151580612bb8575085612bb5603086615729565b14155b15612bef576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612bfa6060836156e6565b151580612c11575085612c0e606084615729565b14155b15612c48576040517f274cf40100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b397fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec9390260005b88811015612ec2576000612ce589898080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612cde925060309150869050615907565b6030613ccc565b90506000612d3788888080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612d30925060609150879050615907565b6060613ccc565b9050846000018c81548110612d4e57612d4e61573d565b600091825260208083206003600690930201919091018054600181018255908352918190208451612d869391909101918501906151a3565b50846000018c81548110612d9c57612d9c61573d565b600091825260208083206004600690930201919091018054600181018255908352918190208351612dd49391909101918401906151a3565b506000612de083613e52565b60008181526020879052604090205490915060ff1615612e3757826040517f5a303adb000000000000000000000000000000000000000000000000000000008152600401612e2e9190615944565b60405180910390fd5b60405180604001604052806001151581526020018e63ffffffff1681525085600001600083815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548163ffffffff021916908363ffffffff160217905550905050836001019350505050612c8d565b50887fb82c87b84f76f39d5f61ed59b411352603805dda6080a2c44b4a86eab141ff1b88888888604051612ef99493929190615982565b60405180910390a2612f09613ec7565b612f1289613f05565b505050505050505050565b612f25613ca2565b6001600160a01b0316336001600160a01b031614612f55576040516282b42960e81b815260040160405180910390fd5b6001612f5f614576565b60009283526020526040909120805460ff1916911515919091179055565b612f85613e28565b6001600160a01b0316336001600160a01b031614612fb5576040516282b42960e81b815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054819085908110612fea57612fea61573d565b600091825260209091206005600690920201015460ff1615613038576040517f1142a68c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081600001858154811061304f5761304f61573d565b9060005260206000209060060201600301805490509050838110156130aa576040517f62106cb30000000000000000000000000000000000000000000000000000000081526004810185905260248101829052604401612e2e565b838260000186815481106130c0576130c061573d565b9060005260206000209060060201600201541080156130e55750826130e36145a4565b115b1561311c576040517f474c62e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b838260000186815481106131325761313261573d565b90600052602060002090600602016002018190555061315085613f05565b60408051868152602081018690527fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e03910160405180910390a15050505050565b6131cf82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613a7992505050565b610c4582828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509250610bfa9150613aed9050565b6000611d86826145d4565b6000613227614576565b600092835260205250604090205460ff1690565b6000611c8161446a565b61324d610906565b565b6000611d8383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509250614231915050565b613298613e28565b6001600160a01b0316336001600160a01b0316146132c8576040516282b42960e81b815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980546000908290859081106133005761330061573d565b9060005260206000209060060201600201819055507fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e03836000604051613350929190918252602082015260400190565b60405180910390a1600181600001848154811061336f5761336f61573d565b600091825260209182902060069190910201600501805460ff1916921515929092179091556040518481527f4c644bb0e171ba9e5cf08f5d66836528c3947c3512b34dd8e27da30e803527b3910160405180910390a1818160000184815481106133db576133db61573d565b906000526020600020906006020160010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f97568892aa9e69db7c72b9f32e6d4fe3c0e53b9728a8c0a0f1c2a7eb47c48ad3838260000185815481106134475761344761573d565b600091825260209182902060069091020154604080519384526001600160a01b039182169284019290925285169082015260600160405180910390a1610a5d83613f05565b6001613496614388565b6134a19060016158c5565b81146134d9576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6134e2816143b2565b6134eb8b61450d565b6134f48b614493565b6134fd8a61450d565b6135068a6145de565b612710851115613542576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61354b85614335565b612710841115613587576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61359084614208565b6135998861450d565b6135a288614607565b6135ab8761450d565b6135b487614630565b6135bd8961450d565b6135c689614659565b6135cf8661450d565b6135d886614682565b6135e2838361229b565b5050505050505050505050565b6000611d836121e884848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613e5292505050565b61363e6030826156e6565b15613675576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d5760006136c684848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b90506136d181613a79565b6136df816000610bfa613aed565b6136ed816001610bfa613ca2565b50603001613678565b6136fe6146ab565b613734576040517fee90c46800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61373d8161450d565b600061377e84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613e5292505050565b905060007f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e2656000838152602082905260409020549091506001600160a01b031633146137dc576040516282b42960e81b815260040160405180910390fd5b7f833a0d0704fbdc6a0f748ebec819c31dde0b532ca9e3e29f56a5c4e8f3cbc0c985858560405161380f939291906159b4565b60405180910390a160009182526020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03929092169190911790555050565b6138686030826156e6565b1561389f576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d5760006138f084848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b90506138fb81613a79565b613909816000610bfa613aed565b506030016138a2565b61391a613e28565b6001600160a01b0316336001600160a01b03161461394a576040516282b42960e81b815260040160405180910390fd5b6040516001600160a01b03821681527f63cc689e9d3377465b51fb094ea4ca5e0a1436b21f1ad30d707c696111c665009060200160405180910390a16123fb816145de565b613997613e28565b6001600160a01b0316336001600160a01b0316146139c7576040516282b42960e81b815260040160405180910390fd5b6123fb816144bc565b60007fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec93902613227565b6000611c815b6000611c81613a2e60017fb2e94356976bf59104fa32f6a0cf50947281732def9f18014aacae55ac60c5dd61576c565b5490565b6000611c817f559ad51499ae00ca2e9d9d95aab46737c8904ab7da276613fefda282b2c2ac065490565b613a6e613a69838361576c565b6146d5565b610c456000836146fe565b613a856121e882613e52565b6001600160a01b0316336001600160a01b031614158015613ab6575033613aaa613e28565b6001600160a01b031614155b156123fb576040517fca455fa500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611c817faa81344d5857c875349bc4a95d531a580c46bdd94c41b35b1e072e4d627079f85490565b6000613b2284613e52565b9050600060028483604051602001613b44929190918252602082015260400190565b60408051601f1981840301815290829052613b5e916159e1565b602060405180830381855afa158015613b7b573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190613b9e91906159fd565b90506000613baa614aa3565b90506000613bb88284614acd565b9050806001600160a01b03163b600003613c5357613bd68284614b49565b506040517f2cc0b2540000000000000000000000000000000000000000000000000000000081526001600160a01b03868116600483015260248201869052821690632cc0b25490604401600060405180830381600087803b158015613c3a57600080fd5b505af1158015613c4e573d6000803e3d6000fd5b505050505b806001600160a01b0316633ccfd60b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015613c8e57600080fd5b505af11580156135e2573d6000803e3d6000fd5b6000611c817fb6d5e19fdd6cde5f03ed4f17c2670deffa47975541fd270b0b17e803297d76085490565b606081613cda81601f6158c5565b1015613d42576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401612e2e565b613d4c82846158c5565b84511015613db6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401612e2e565b606082158015613dd55760405191506000825260208201604052613e1f565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015613e0e578051835260209283019201613df6565b5050858452601f01601f1916604052505b50949350505050565b6000611c817ffbeda9bc03875013b12a1ec161efb8e5bf7e58e3cec96a1ea9efd3e264d26e645490565b6000600282600060801b604051602001613e6d929190615a16565b60408051601f1981840301815290829052613e87916159e1565b602060405180830381855afa158015613ea4573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190611d8691906159fd565b613ed043614c1a565b6040514381527f55c5aabfc91da783d60f72c41f352ac7f9d507fcd884f41e27e6852305f948ce9060200160405180910390a1565b60408051808201825260008082526020808301828152600286901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f0825293822054600686901b60c0161c63ffffffff8181168552911c1690925280517fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054929350916000908190849087908110613fa257613fa261573d565b9060005260206000209060060201600201549050846020015163ffffffff16811161403157602080860151600288901c60009081527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f083526040902080549190921b67ffffffff0000000016600689901b60c01690811b67ffffffffffffffff90911b199091161790556140b6565b60208501516140469063ffffffff168261576c565b602080870151600289901c60009081527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f083526040902080549190921b67ffffffff000000001663ffffffff84161760068a901b60c01690811b67ffffffffffffffff90911b1990911617905591505b8163ffffffff168363ffffffff16146140f6576140f68263ffffffff168463ffffffff166140e2613a32565b6140ec919061576c565b613a6991906158c5565b505050505050565b60007fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054839081106141335761413361573d565b60009182526020909120600690910201600581015490915060ff1615614185576040517f1142a68c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80546001600160a01b03163314610c45576040516282b42960e81b815260040160405180910390fd5b6000611c817fb88142a0318e2b174876bceb4db9dc318011849c83fb8a8bb2997386d562324a5490565b6000611c81613a2e60017f05896f012c7ab472c495a6810b73fc6183d339203dd1c74344bbfd2e1f5038fe61576c565b6123fb7f41118591c19026bdc7a484e34f80a8e7e632600aff1c72460e9c7dfe94a2dda6829055565b60008061423d84613e52565b905060006002848360405160200161425f929190918252602082015260400190565b60408051601f1981840301815290829052614279916159e1565b602060405180830381855afa158015614296573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906142b991906159fd565b905060006142c5614aa3565b90506142d18183614acd565b9695505050505050565b6000611c817f41118591c19026bdc7a484e34f80a8e7e632600aff1c72460e9c7dfe94a2dda65490565b6000611c81613a2e60017f2ad86e81c1a15749eb0399a4f2566324ade0cc7d7f8592a3db8896a29004bf8161576c565b6123fb7fb88142a0318e2b174876bceb4db9dc318011849c83fb8a8bb2997386d562324a829055565b6000611c817f10c92bb459c0223bf996150f2fb702a8288fb8354a33d5b0212e8e6b8273f55e5490565b6000611c817fd5c553085b8382c47128ae7612257fd5dc3b4fc4d3a108925604d3c8700c025b5490565b6123fb7fd5c553085b8382c47128ae7612257fd5dc3b4fc4d3a108925604d3c8700c025b829055565b6123fb61440960017f2ad86e81c1a15749eb0399a4f2566324ade0cc7d7f8592a3db8896a29004bf8161576c565b829055565b6123fb61440960017f05896f012c7ab472c495a6810b73fc6183d339203dd1c74344bbfd2e1f5038fe61576c565b6123fb61440960017fb2e94356976bf59104fa32f6a0cf50947281732def9f18014aacae55ac60c5dd61576c565b6000611c817e595eca1f8b39945ff4c404827bfa5fd1e295ef3f7d59d120a8ce3bae4e37a05490565b6123fb7ffbeda9bc03875013b12a1ec161efb8e5bf7e58e3cec96a1ea9efd3e264d26e64829055565b6123fb7e595eca1f8b39945ff4c404827bfa5fd1e295ef3f7d59d120a8ce3bae4e37a0829055565b6123fb7fce3b5bd42ef5aae9d7b01cd806ce2a8b66f0a48f2e8b5a15f77dfbbdbbf5144f829055565b6001600160a01b0381166123fb576040517ff6b2911f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80614556614c48565b60009384526020526040909220805460ff19169215159290921790915550565b600080611d8660017fd9ed3684910fa17a5f2ad83fd15b2ccb0abf740b7041631a9fe7daffda0cb9ee61576c565b6000611c81613a2e60017f7d3541d2c0079305636180d42008f210385563932c50e421bd8654ed4a6017eb61576c565b6000613227614c48565b6123fb7f10c92bb459c0223bf996150f2fb702a8288fb8354a33d5b0212e8e6b8273f55e829055565b6123fb7faa81344d5857c875349bc4a95d531a580c46bdd94c41b35b1e072e4d627079f8829055565b6123fb7fb6d5e19fdd6cde5f03ed4f17c2670deffa47975541fd270b0b17e803297d7608829055565b6123fb7fbc8b9852d17d50256bb221fdf6ee12d78dd493d807e907f7d223c40d65abd6b9829055565b6123fb7fd1c64973da70267569571a091966834c1a36bdba47f2a112b6a95bf41fc9c24e829055565b6000611c817fce3b5bd42ef5aae9d7b01cd806ce2a8b66f0a48f2e8b5a15f77dfbbdbbf5144f5490565b6123fb7f559ad51499ae00ca2e9d9d95aab46737c8904ab7da276613fefda282b2c2ac06829055565b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980546000908290859081106147365761473661573d565b6000918252602080832060408051808201825285815280840186815260028b901c87527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f08552919095205460068a811b60c0169190911c63ffffffff8181168852941c90931690819052929091020192505b84826020015163ffffffff166147be91906158c5565b8110156149f75760008360030182815481106147dc576147dc61573d565b9060005260206000200180546147f190615783565b80601f016020809104026020016040519081016040528092919081815260200182805461481d90615783565b801561486a5780601f1061483f5761010080835404028352916020019161486a565b820191906000526020600020905b81548152906001019060200180831161484d57829003601f168201915b5050505050905060008460040183815481106148885761488861573d565b90600052602060002001805461489d90615783565b80601f01602080910402602001604051908101604052809291908181526020018280546148c990615783565b80156149165780601f106148eb57610100808354040283529160200191614916565b820191906000526020600020905b8154815290600101906020018083116148f957829003601f168201915b50505050509050600061492a836001614231565b9050600061493782614c76565b9050600061494485613e52565b905061495285828685614cab565b60008181527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260409081902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001633908117909155905181907fac1020908b5f7134d59c1580838eba6fc42dd8c28bae65bf345676bba1913f8e906149df9089908990615a5d565b60405180910390a385600101955050505050506147a8565b50614a9c8585836000015163ffffffff16614a12919061576c565b86846020015163ffffffff16614a2891906158c5565b600283901c60009081527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f060209081526040909120805467ffffffffffffffff60c060069790971b9690961695861b191663ffffffff9490941667ffffffff000000009390921b929092161790921b179055565b5050505050565b6000611c817fd1c64973da70267569571a091966834c1a36bdba47f2a112b6a95bf41fc9c24e5490565b6000611d838383306040517f3d602d80600a3d3981f3363d3d373d3d3d363d730000000000000000000000008152606093841b60148201527f5af43d82803e903d91602b57fd5bf3ff000000000000000000000000000000006028820152921b6038830152604c8201526037808220606c830152605591012090565b60006040517f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000081528360601b60148201527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006028820152826037826000f59150506001600160a01b038116611d86576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f455243313136373a2063726561746532206661696c65640000000000000000006044820152606401612e2e565b6123fb61440960017f7d3541d2c0079305636180d42008f210385563932c50e421bd8654ed4a6017eb61576c565b600080611d8660017f68ed1186fe6f93f91e23b08dcba07651b0153d3ab058aab96212b33aaaf3a89961576c565b6000611d867f01000000000000000000000000000000000000000000000000000000000000006001600160a01b0384166158c5565b6000600280614cbd8560006040613ccc565b604051614cca91906159e1565b602060405180830381855afa158015614ce7573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614d0a91906159fd565b6002614d22866040614d1d81606061576c565b613ccc565b604051614d359190600090602001615a8b565b60408051601f1981840301815290829052614d4f916159e1565b602060405180830381855afa158015614d6c573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614d8f91906159fd565b60408051602081019390935282015260600160408051601f1981840301815290829052614dbb916159e1565b602060405180830381855afa158015614dd8573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614dfb91906159fd565b905060006002808685604051602001614e1e929190918252602082015260400190565b60408051601f1981840301815290829052614e38916159e1565b602060405180830381855afa158015614e55573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614e7891906159fd565b604080517e40597307000000000000000000000000000000000000000000000000000000602082015290810185905260029060600160408051601f1981840301815290829052614ec7916159e1565b602060405180830381855afa158015614ee4573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614f0791906159fd565b60408051602081019390935282015260600160408051601f1981840301815290829052614f33916159e1565b602060405180830381855afa158015614f50573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614f7391906159fd565b90506000614f8a6801bc16d674ec8000004761576c565b9050614f9461505b565b6001600160a01b031663228951186801bc16d674ec8000008987604051602001614fc091815260200190565b60405160208183030381529060405289876040518663ffffffff1660e01b8152600401614ff09493929190615aad565b6000604051808303818588803b15801561500957600080fd5b505af115801561501d573d6000803e3d6000fd5b5050505050804714611c6d576040517f6596d2b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611c817fbc8b9852d17d50256bb221fdf6ee12d78dd493d807e907f7d223c40d65abd6b95490565b50805461509190615783565b6000825580601f106150a1575050565b601f0160209004906000526020600020908101906123fb9190615217565b8280546150cb90615783565b90600052602060002090601f0160209004810192826150ed576000855561513a565b82601f106150fe578054855561513a565b8280016001018555821561513a57600052602060002091601f016020900482015b8281111561513a57825482559160010191906001019061511f565b50615146929150615217565b5090565b828054828255906000526020600020908101928215615197579160200282015b8281111561519757825180516151879184916020909101906151a3565b509160200191906001019061516a565b5061514692915061522c565b8280546151af90615783565b90600052602060002090601f0160209004810192826151d1576000855561513a565b82601f106151ea57805160ff191683800117855561513a565b8280016001018555821561513a579182015b8281111561513a5782518255916020019190600101906151fc565b5b808211156151465760008155600101615218565b808211156151465760006152408282615085565b5060010161522c565b60006020828403121561525b57600080fd5b5035919050565b60008083601f84011261527457600080fd5b50813567ffffffffffffffff81111561528c57600080fd5b6020830191508360208285010111156152a457600080fd5b9250929050565b600080602083850312156152be57600080fd5b823567ffffffffffffffff8111156152d557600080fd5b6152e185828601615262565b90969095509350505050565b60008060006040848603121561530257600080fd5b83359250602084013567ffffffffffffffff8082111561532157600080fd5b818601915086601f83011261533557600080fd5b81358181111561534457600080fd5b8760208260051b850101111561535957600080fd5b6020830194508093505050509250925092565b80356001600160a01b038116811461538357600080fd5b919050565b6000806040838503121561539b57600080fd5b823591506153ab6020840161536c565b90509250929050565b600080604083850312156153c757600080fd5b50508035926020909101359150565b60005b838110156153f15781810151838201526020016153d9565b83811115611c715750506000910152565b6000815180845261541a8160208601602086016153d6565b601f01601f19169290920160200192915050565b6080815260006154416080830187615402565b82810360208401526154538187615402565b6001600160a01b03959095166040840152505090151560609091015292915050565b60006020828403121561548757600080fd5b8135801515811461549757600080fd5b9392505050565b600080604083850312156154b157600080fd5b6154ba8361536c565b91506153ab6020840161536c565b6000806000606084860312156154dd57600080fd5b833592506154ed6020850161536c565b91506154fb6040850161536c565b90509250925092565b6000806000806000806080878903121561551d57600080fd5b8635955060208701359450604087013567ffffffffffffffff8082111561554357600080fd5b61554f8a838b01615262565b9096509450606089013591508082111561556857600080fd5b5061557589828a01615262565b979a9699509497509295939492505050565b60008060006060848603121561559c57600080fd5b505081359360208301359350604090920135919050565b6000806000806000806000806000806101408b8d0312156155d357600080fd5b6155dc8b61536c565b99506155ea60208c0161536c565b98506155f860408c0161536c565b975061560660608c0161536c565b965061561460808c0161536c565b955061562260a08c0161536c565b945060c08b0135935060e08b013592506101008b013591506101208b013590509295989b9194979a5092959850565b60008060006040848603121561566657600080fd5b833567ffffffffffffffff81111561567d57600080fd5b61568986828701615262565b90945092506154fb90506020850161536c565b6000602082840312156156ae57600080fd5b611d838261536c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826156f5576156f56156b7565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082615738576157386156b7565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008282101561577e5761577e6156fa565b500390565b600181811c9082168061579757607f821691505b6020821081036157d0577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600060208083526000845481600182811c9150808316806157f857607f831692505b858310810361582e577f4e487b710000000000000000000000000000000000000000000000000000000085526022600452602485fd5b87860183815260200181801561584b576001811461585c57615887565b60ff19861682528782019650615887565b60008b81526020902060005b8681101561588157815484820152908501908901615868565b83019750505b50949998505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600082198211156158d8576158d86156fa565b500190565b6001600160a01b03831681526040602082015260006158ff6040830184615402565b949350505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561593f5761593f6156fa565b500290565b602081526000611d836020830184615402565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b604081526000615996604083018688615957565b82810360208401526159a9818587615957565b979650505050505050565b6040815260006159c8604083018587615957565b90506001600160a01b0383166020830152949350505050565b600082516159f38184602087016153d6565b9190910192915050565b600060208284031215615a0f57600080fd5b5051919050565b60008351615a288184602088016153d6565b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000939093169190920190815260100192915050565b604081526000615a706040830185615402565b8281036020840152615a828185615402565b95945050505050565b60008351615a9d8184602088016153d6565b9190910191825250602001919050565b608081526000615ac06080830187615402565b8281036020840152615ad28187615402565b90508281036040840152615ae68186615402565b9150508260608301529594505050505056fea264697066735822122082f275dcc95ed77ce7470cc6de8ad96480198f43db9aa8fe8aa8314355ecbd8364736f6c634300080d0033", + "deployedBytecode": "0x6080604052600436106103015760003560e01c8063a74008011161018f578063d0e30db0116100e1578063e99454f51161008a578063f2fde38b11610064578063f2fde38b146108b1578063fe0e4191146108d1578063fe38231c146108f157610310565b8063e99454f514610851578063ef5e468214610871578063f0f442601461089157610310565b8063dfbe50dd116100bb578063dfbe50dd146107f1578063e00cb6ca14610811578063e8a0c1211461083157610310565b8063d0e30db0146107a9578063d243d69d146107b1578063d2a42747146107d157610310565b8063beee4bbf11610143578063c13d03501161011d578063c13d035014610744578063cac594df14610774578063d04681561461079457610310565b8063beee4bbf146106ef578063bf15af561461070f578063bf509bd41461072457610310565b8063b6b06dec11610174578063b6b06dec1461068f578063b747e7dd146106af578063b86bcaf7146106cf57610310565b8063a74008011461065a578063b4336b841461067a57610310565b806336bf3325116102535780637680fdf5116101fc5780638df4e474116101d65780638df4e474146105fa5780639adf91ee1461061a578063a4d8d2c41461063a57610310565b80637680fdf5146105a557806379ba5097146105c55780638a1af4c4146105da57610310565b806363b4118f1161022d57806363b4118f146105405780636d336fe0146105705780636e9960c31461059057610310565b806336bf3325146104f95780633b19e84a14610516578063540bc5ea1461052b57610310565b80631d095805116102b5578063286966081161028f57806328696608146104a4578063291206f6146104b95780632ba03a79146104d957610310565b80631d0958051461042c5780631ee133431461044c578063227e80fa1461048457610310565b80630ffab6c2116102e65780630ffab6c2146103c95780631864636c146103e95780631bcbfaba1461040957610310565b806305f63c8a146103425780630968f264146103a957610310565b366103105761030e610906565b005b6040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34801561034e57600080fd5b5061036261035d366004615249565b610a62565b604080516001600160a01b039889168152979096166020880152948601939093526060850191909152608084015260a0830152151560c082015260e0015b60405180910390f35b3480156103b557600080fd5b5061030e6103c43660046152ab565b610b78565b3480156103d557600080fd5b5061030e6103e43660046152ab565b610c49565b3480156103f557600080fd5b5061030e6104043660046152ed565b610cfe565b34801561041557600080fd5b5061041e611c77565b6040519081526020016103a0565b34801561043857600080fd5b5061030e610447366004615249565b611c86565b34801561045857600080fd5b5061046c6104673660046152ab565b611d3f565b6040516001600160a01b0390911681526020016103a0565b34801561049057600080fd5b5061030e61049f366004615388565b611d8c565b3480156104b057600080fd5b5061041e611f15565b3480156104c557600080fd5b5061030e6104d4366004615249565b611f1f565b3480156104e557600080fd5b5061030e6104f43660046152ab565b611fd1565b34801561050557600080fd5b5061041e6801bc16d674ec80000081565b34801561052257600080fd5b5061046c612010565b34801561053757600080fd5b5061041e606081565b34801561054c57600080fd5b5061056061055b3660046153b4565b61201a565b6040516103a0949392919061542e565b34801561057c57600080fd5b5061030e61058b3660046153b4565b61229b565b34801561059c57600080fd5b5061046c61237b565b3480156105b157600080fd5b5061030e6105c0366004615475565b612385565b3480156105d157600080fd5b5061030e6123fe565b3480156105e657600080fd5b5061041e6105f536600461549e565b61247e565b34801561060657600080fd5b5061030e610615366004615475565b61267a565b34801561062657600080fd5b5061046c610635366004615249565b6126ed565b34801561064657600080fd5b5061030e6106553660046154c8565b6127e8565b34801561066657600080fd5b5061046c610675366004615249565b6129c4565b34801561068657600080fd5b5061041e612a00565b34801561069b57600080fd5b5061030e6106aa3660046152ab565b612a0a565b3480156106bb57600080fd5b5061030e6106ca366004615504565b612b52565b3480156106db57600080fd5b5061030e6106ea366004615249565b612f1d565b3480156106fb57600080fd5b5061030e61070a366004615587565b612f7d565b34801561071b57600080fd5b5061041e603081565b34801561073057600080fd5b5061030e61073f3660046152ab565b613190565b34801561075057600080fd5b5061076461075f366004615249565b613212565b60405190151581526020016103a0565b34801561078057600080fd5b5061076461078f366004615249565b61321d565b3480156107a057600080fd5b5061046c61323b565b61030e613245565b3480156107bd57600080fd5b5061046c6107cc3660046152ab565b61324f565b3480156107dd57600080fd5b5061030e6107ec366004615388565b613290565b3480156107fd57600080fd5b5061030e61080c3660046155b3565b61348c565b34801561081d57600080fd5b5061046c61082c3660046152ab565b6135ef565b34801561083d57600080fd5b5061030e61084c3660046152ab565b613633565b34801561085d57600080fd5b5061030e61086c366004615651565b6136f6565b34801561087d57600080fd5b5061030e61088c3660046152ab565b61385d565b34801561089d57600080fd5b5061030e6108ac36600461569c565b613912565b3480156108bd57600080fd5b5061030e6108cc36600461569c565b61398f565b3480156108dd57600080fd5b506107646108ec366004615249565b6139d0565b3480156108fd57600080fd5b506107646139f8565b61090e6139fe565b15610945576040517f753ed9db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34158061096357506109606801bc16d674ec800000346156e6565b15155b1561099a576040517f428243e200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006109a4613a32565b905060006109bb6801bc16d674ec80000034615729565b9050818111156109f7576040517fae575a8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054600003610a53576040517fddf9d24500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5d8284613a5c565b505050565b6000808080808080807fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054909150891015610b6c576040805180820182526000808252602080830182815260028e901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f082529382205460068e901b60c0161c63ffffffff8181168552911c1690925290506000826000018b81548110610b0e57610b0e61573d565b600091825260209182902060069091020180546001820154600283015460038401546005909401549487015196516001600160a01b039384169f50919092169c50909a5090985063ffffffff93841697509290921694505060ff1691505b50919395979092949650565b610bb782828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613a7992505050565b610bff82828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509250610bfa9150613aed9050565b613b17565b610c4582828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250610bfa9150613ca29050565b5050565b610c546030826156e6565b15610c8b576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d576000610cdc84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b9050610ce781613a79565b610cf5816001610bfa613ca2565b50603001610c8e565b82610d07613e28565b6001600160a01b0316330361152a576000829003610d51576040517fa9cb9e0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051808201825260008082526020808301828152600289901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f082529390912054600688901b60c0161c63ffffffff8181168452911c1691829052907fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b39907fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec93902908686610e0260018261576c565b818110610e1157610e1161573d565b905060200201351015610e50576040517f34947ea100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8581101561140d57600081118015610ea557508686610e7360018461576c565b818110610e8257610e8261573d565b90506020020135878783818110610e9b57610e9b61573d565b9050602002013510155b15610edc576040517f35061dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610fc8846000018a81548110610ef657610ef661573d565b9060005260206000209060060201600301898985818110610f1957610f1961573d565b9050602002013581548110610f3057610f3061573d565b906000526020600020018054610f4590615783565b80601f0160208091040260200160405190810160405280929190818152602001828054610f7190615783565b8015610fbe5780601f10610f9357610100808354040283529160200191610fbe565b820191906000526020600020905b815481529060010190602001808311610fa157829003601f168201915b5050505050613e52565b600081815260208590526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000169055845490915089907f794aacb42d1ea2e7f72809b74e3ce124325a51c3715b873c36807d3ca37e4fd0908690839081106110375761103761573d565b90600052602060002090600602016003018a8a8681811061105a5761105a61573d565b90506020020135815481106110715761107161573d565b9060005260206000200160405161108891906157d6565b60405180910390a26001846000018a815481106110a7576110a761573d565b9060005260206000209060060201600301805490506110c6919061576c565b8888848181106110d8576110d861573d565b905060200201350361118f578360000189815481106110f9576110f961573d565b906000526020600020906006020160030180548061111957611119615896565b6001900381819060005260206000200160006111359190615085565b905583600001898154811061114c5761114c61573d565b906000526020600020906006020160040180548061116c5761116c615896565b6001900381819060005260206000200160006111889190615085565b9055611404565b8360000189815481106111a4576111a461573d565b90600052602060002090600602016003016001856000018b815481106111cc576111cc61573d565b9060005260206000209060060201600301805490506111eb919061576c565b815481106111fb576111fb61573d565b90600052602060002001846000018a8154811061121a5761121a61573d565b906000526020600020906006020160030189898581811061123d5761123d61573d565b90506020020135815481106112545761125461573d565b9060005260206000200190805461126a90615783565b6112759291906150bf565b5083600001898154811061128b5761128b61573d565b90600052602060002090600602016003018054806112ab576112ab615896565b6001900381819060005260206000200160006112c79190615085565b90558360000189815481106112de576112de61573d565b90600052602060002090600602016004016001856000018b815481106113065761130661573d565b906000526020600020906006020160040180549050611325919061576c565b815481106113355761133561573d565b90600052602060002001846000018a815481106113545761135461573d565b90600052602060002090600602016004018989858181106113775761137761573d565b905060200201358154811061138e5761138e61573d565b906000526020600020019080546113a490615783565b6113af9291906150bf565b508360000189815481106113c5576113c561573d565b90600052602060002090600602016004018054806113e5576113e5615896565b6001900381819060005260206000200160006114019190615085565b90555b50600101610e53565b508160000187815481106114235761142361573d565b6000918252602090912060026006909202010154868661144460018261576c565b8181106114535761145361573d565b90506020020135101561151157858561146d60018261576c565b81811061147c5761147c61573d565b905060200201358260000188815481106114985761149861573d565b60009182526020909120600260069092020101557fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e038787876114db60018261576c565b8181106114ea576114ea61573d565b90506020020135604051611508929190918252602082015260400190565b60405180910390a15b611519613ec7565b61152287613f05565b505050611c71565b611533816140fe565b600082900361156e576040517fa9cb9e0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051808201825260008082526020808301828152600289901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f082529390912054600688901b60c0161c63ffffffff8181168452911c1691829052907fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b39907fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec9390290868661161f60018261576c565b81811061162e5761162e61573d565b90506020020135101561166d576040517f34947ea100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b85811015611b58576000811180156116c25750868661169060018461576c565b81811061169f5761169f61573d565b905060200201358787838181106116b8576116b861573d565b9050602002013510155b156116f9576040517f35061dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611713846000018a81548110610ef657610ef661573d565b600081815260208590526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000169055845490915089907f794aacb42d1ea2e7f72809b74e3ce124325a51c3715b873c36807d3ca37e4fd0908690839081106117825761178261573d565b90600052602060002090600602016003018a8a868181106117a5576117a561573d565b90506020020135815481106117bc576117bc61573d565b906000526020600020016040516117d391906157d6565b60405180910390a26001846000018a815481106117f2576117f261573d565b906000526020600020906006020160030180549050611811919061576c565b8888848181106118235761182361573d565b90506020020135036118da578360000189815481106118445761184461573d565b906000526020600020906006020160030180548061186457611864615896565b6001900381819060005260206000200160006118809190615085565b90558360000189815481106118975761189761573d565b90600052602060002090600602016004018054806118b7576118b7615896565b6001900381819060005260206000200160006118d39190615085565b9055611b4f565b8360000189815481106118ef576118ef61573d565b90600052602060002090600602016003016001856000018b815481106119175761191761573d565b906000526020600020906006020160030180549050611936919061576c565b815481106119465761194661573d565b90600052602060002001846000018a815481106119655761196561573d565b90600052602060002090600602016003018989858181106119885761198861573d565b905060200201358154811061199f5761199f61573d565b906000526020600020019080546119b590615783565b6119c09291906150bf565b508360000189815481106119d6576119d661573d565b90600052602060002090600602016003018054806119f6576119f6615896565b600190038181906000526020600020016000611a129190615085565b9055836000018981548110611a2957611a2961573d565b90600052602060002090600602016004016001856000018b81548110611a5157611a5161573d565b906000526020600020906006020160040180549050611a70919061576c565b81548110611a8057611a8061573d565b90600052602060002001846000018a81548110611a9f57611a9f61573d565b9060005260206000209060060201600401898985818110611ac257611ac261573d565b9050602002013581548110611ad957611ad961573d565b90600052602060002001908054611aef90615783565b611afa9291906150bf565b50836000018981548110611b1057611b1061573d565b9060005260206000209060060201600401805480611b3057611b30615896565b600190038181906000526020600020016000611b4c9190615085565b90555b50600101611670565b50816000018781548110611b6e57611b6e61573d565b60009182526020909120600260069092020101548686611b8f60018261576c565b818110611b9e57611b9e61573d565b905060200201351015611c5c578585611bb860018261576c565b818110611bc757611bc761573d565b90506020020135826000018881548110611be357611be361573d565b60009182526020909120600260069092020101557fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e03878787611c2660018261576c565b818110611c3557611c3561573d565b90506020020135604051611c53929190918252602082015260400190565b60405180910390a15b611c64613ec7565b611c6d87613f05565b5050505b50505050565b6000611c816141ae565b905090565b611c8e613e28565b6001600160a01b0316336001600160a01b031614611cbe576040516282b42960e81b815260040160405180910390fd5b611cc66141d8565b811115611cff576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d0881614208565b6040518181527fd894096cd1f7e89d9b748c7c2358cb699a790a05e97dcde228fe5949b4e80743906020015b60405180910390a150565b6000611d8383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250614231915050565b90505b92915050565b611d94613e28565b6001600160a01b0316336001600160a01b031614611dc4576040516282b42960e81b815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054600090829085908110611dfc57611dfc61573d565b600091825260209182902060069190910201600501805460ff1916921515929092179091556040518481527f8d0ba049731b0417f85546bc7503de241522c61e7464e87cceff56d3f2586dbc910160405180910390a181816000018481548110611e6857611e6861573d565b906000526020600020906006020160010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f97568892aa9e69db7c72b9f32e6d4fe3c0e53b9728a8c0a0f1c2a7eb47c48ad383826000018581548110611ed457611ed461573d565b600091825260209182902060069091020154604080519384526001600160a01b039182169284019290925285169082015260600160405180910390a1505050565b6000611c816142db565b611f27613e28565b6001600160a01b0316336001600160a01b031614611f57576040516282b42960e81b815260040160405180910390fd5b611f5f614305565b811115611f98576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611fa181614335565b6040518181527fecd6fc650620aa722cafc3c3e871fa813947eae6f856982d7510a8ff101a507890602001611d34565b610bff82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613a7992505050565b6000611c8161435e565b606080600080807fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3990508060000187815481106120595761205961573d565b9060005260206000209060060201600301868154811061207b5761207b61573d565b90600052602060002001805461209090615783565b80601f01602080910402602001604051908101604052809291908181526020018280546120bc90615783565b80156121095780601f106120de57610100808354040283529160200191612109565b820191906000526020600020905b8154815290600101906020018083116120ec57829003601f168201915b505050505094508060000187815481106121255761212561573d565b906000526020600020906006020160040186815481106121475761214761573d565b90600052602060002001805461215c90615783565b80601f016020809104026020016040519081016040528092919081815260200182805461218890615783565b80156121d55780601f106121aa576101008083540402835291602001916121d5565b820191906000526020600020905b8154815290600101906020018083116121b857829003601f168201915b505050505093506122226121e886613e52565b60009081527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260409020546001600160a01b031690565b6040805180820182526000808252602080830182815260028d901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f08252939091205460068c901b60c0161c63ffffffff8181168452911c169091529093506020015163ffffffff16861091505092959194509250565b60026122a5614388565b6122b09060016158c5565b81146122e8576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6122f1816143b2565b61271083111561232d576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612336836143db565b612710821115612372576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5d8261440e565b6000611c81613e28565b61238d613e28565b6001600160a01b0316336001600160a01b0316146123bd576040516282b42960e81b815260040160405180910390fd5b60405181151581527f8caf502b0bfa5915a541f14f8b0d091d4837c201b74aa2a7a7f52487a5fe8a819060200160405180910390a16123fb8161443c565b50565b600061240861446a565b9050336001600160a01b03821614612432576040516282b42960e81b815260040160405180910390fd5b61243b81614493565b61244560006144bc565b6040516001600160a01b03821681527ff29c1089a9594c030706593e345bd8f70a26125bfb7bf4c54e757e20f456fd1c90602001611d34565b6000612488613e28565b6001600160a01b0316336001600160a01b0316146124b8576040516282b42960e81b815260040160405180910390fd5b6040805160c0810182526000808252602082018190529181018290526060808201819052608082015260a08101919091527fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054909190600103612548576040517fa20c741300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03808616825284811660208084019182528454600181810187556000878152839020865160069093020180549286167fffffffffffffffffffffffff00000000000000000000000000000000000000009384161781559351908401805491909516911617909255604083015160028201556060830151805184936125da92600385019291019061514a565b50608082015180516125f691600484019160209091019061514a565b5060a091909101516005909101805460ff191691151591909117905581546000906126239060019061576c565b604080516001600160a01b03808a168252881660208201529081018290529091507f2b3c4db2c0f4f51da09c2510a63e1d90235e486e8f075a609103a5c7a07422179060600160405180910390a195945050505050565b612682613e28565b6001600160a01b0316336001600160a01b0316146126b2576040516282b42960e81b815260040160405180910390fd5b6126bb816144e4565b60405181151581527f96639403db35a7ef32b25543bec54f73278d8c5ca82883ef1385ddcb27afa7dc90602001611d34565b60008181527fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec93902602052604081205460ff1615158103612758576040517f9e44bfc500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281527fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec9390260205260409020547fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980549091610100900463ffffffff169081106127c5576127c561573d565b60009182526020909120600160069092020101546001600160a01b031692915050565b8260007fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980548390811061281e5761281e61573d565b60009182526020909120600690910201600581015490915060ff1615612870576040517f1142a68c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018101546001600160a01b0316331461289c576040516282b42960e81b815260040160405180910390fd5b6128a58461450d565b6128ae8361450d565b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b39805485908290889081106128e5576128e561573d565b906000526020600020906006020160000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550838160000187815481106129305761293061573d565b60009182526020918290206006919091020160010180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0393841617905560408051898152888416928101929092529186168183015290517f97568892aa9e69db7c72b9f32e6d4fe3c0e53b9728a8c0a0f1c2a7eb47c48ad3916060908290030190a1505050505050565b60008181527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260408120546001600160a01b0316611d86565b6000611c81613a32565b612a156030826156e6565b15612a4c576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d576000612a9d84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b90506000612aaa82613e52565b60008181527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260409020549091506001600160a01b0316338114612b03576040516282b42960e81b815260040160405180910390fd5b612b0e82600161454d565b7fcb773ebd49049a1f68200f0814f547dec2c54293ff2c6a6a4f4d98d6d9cb12298184604051612b3f9291906158dd565b60405180910390a1505050603001612a4f565b85612b5c816140fe565b85600003612b96576040517fa9cb9e0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612ba16030856156e6565b151580612bb8575085612bb5603086615729565b14155b15612bef576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612bfa6060836156e6565b151580612c11575085612c0e606084615729565b14155b15612c48576040517f274cf40100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b397fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec9390260005b88811015612ec2576000612ce589898080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612cde925060309150869050615907565b6030613ccc565b90506000612d3788888080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612d30925060609150879050615907565b6060613ccc565b9050846000018c81548110612d4e57612d4e61573d565b600091825260208083206003600690930201919091018054600181018255908352918190208451612d869391909101918501906151a3565b50846000018c81548110612d9c57612d9c61573d565b600091825260208083206004600690930201919091018054600181018255908352918190208351612dd49391909101918401906151a3565b506000612de083613e52565b60008181526020879052604090205490915060ff1615612e3757826040517f5a303adb000000000000000000000000000000000000000000000000000000008152600401612e2e9190615944565b60405180910390fd5b60405180604001604052806001151581526020018e63ffffffff1681525085600001600083815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548163ffffffff021916908363ffffffff160217905550905050836001019350505050612c8d565b50887fb82c87b84f76f39d5f61ed59b411352603805dda6080a2c44b4a86eab141ff1b88888888604051612ef99493929190615982565b60405180910390a2612f09613ec7565b612f1289613f05565b505050505050505050565b612f25613ca2565b6001600160a01b0316336001600160a01b031614612f55576040516282b42960e81b815260040160405180910390fd5b6001612f5f614576565b60009283526020526040909120805460ff1916911515919091179055565b612f85613e28565b6001600160a01b0316336001600160a01b031614612fb5576040516282b42960e81b815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054819085908110612fea57612fea61573d565b600091825260209091206005600690920201015460ff1615613038576040517f1142a68c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081600001858154811061304f5761304f61573d565b9060005260206000209060060201600301805490509050838110156130aa576040517f62106cb30000000000000000000000000000000000000000000000000000000081526004810185905260248101829052604401612e2e565b838260000186815481106130c0576130c061573d565b9060005260206000209060060201600201541080156130e55750826130e36145a4565b115b1561311c576040517f474c62e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b838260000186815481106131325761313261573d565b90600052602060002090600602016002018190555061315085613f05565b60408051868152602081018690527fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e03910160405180910390a15050505050565b6131cf82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613a7992505050565b610c4582828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509250610bfa9150613aed9050565b6000611d86826145d4565b6000613227614576565b600092835260205250604090205460ff1690565b6000611c8161446a565b61324d610906565b565b6000611d8383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509250614231915050565b613298613e28565b6001600160a01b0316336001600160a01b0316146132c8576040516282b42960e81b815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980546000908290859081106133005761330061573d565b9060005260206000209060060201600201819055507fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e03836000604051613350929190918252602082015260400190565b60405180910390a1600181600001848154811061336f5761336f61573d565b600091825260209182902060069190910201600501805460ff1916921515929092179091556040518481527f4c644bb0e171ba9e5cf08f5d66836528c3947c3512b34dd8e27da30e803527b3910160405180910390a1818160000184815481106133db576133db61573d565b906000526020600020906006020160010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f97568892aa9e69db7c72b9f32e6d4fe3c0e53b9728a8c0a0f1c2a7eb47c48ad3838260000185815481106134475761344761573d565b600091825260209182902060069091020154604080519384526001600160a01b039182169284019290925285169082015260600160405180910390a1610a5d83613f05565b6001613496614388565b6134a19060016158c5565b81146134d9576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6134e2816143b2565b6134eb8b61450d565b6134f48b614493565b6134fd8a61450d565b6135068a6145de565b612710851115613542576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61354b85614335565b612710841115613587576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61359084614208565b6135998861450d565b6135a288614607565b6135ab8761450d565b6135b487614630565b6135bd8961450d565b6135c689614659565b6135cf8661450d565b6135d886614682565b6135e2838361229b565b5050505050505050505050565b6000611d836121e884848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613e5292505050565b61363e6030826156e6565b15613675576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d5760006136c684848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b90506136d181613a79565b6136df816000610bfa613aed565b6136ed816001610bfa613ca2565b50603001613678565b6136fe6146ab565b613734576040517fee90c46800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61373d8161450d565b600061377e84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613e5292505050565b905060007f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e2656000838152602082905260409020549091506001600160a01b031633146137dc576040516282b42960e81b815260040160405180910390fd5b7f833a0d0704fbdc6a0f748ebec819c31dde0b532ca9e3e29f56a5c4e8f3cbc0c985858560405161380f939291906159b4565b60405180910390a160009182526020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03929092169190911790555050565b6138686030826156e6565b1561389f576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d5760006138f084848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b90506138fb81613a79565b613909816000610bfa613aed565b506030016138a2565b61391a613e28565b6001600160a01b0316336001600160a01b03161461394a576040516282b42960e81b815260040160405180910390fd5b6040516001600160a01b03821681527f63cc689e9d3377465b51fb094ea4ca5e0a1436b21f1ad30d707c696111c665009060200160405180910390a16123fb816145de565b613997613e28565b6001600160a01b0316336001600160a01b0316146139c7576040516282b42960e81b815260040160405180910390fd5b6123fb816144bc565b60007fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec93902613227565b6000611c815b6000611c81613a2e60017fb2e94356976bf59104fa32f6a0cf50947281732def9f18014aacae55ac60c5dd61576c565b5490565b6000611c817f559ad51499ae00ca2e9d9d95aab46737c8904ab7da276613fefda282b2c2ac065490565b613a6e613a69838361576c565b6146d5565b610c456000836146fe565b613a856121e882613e52565b6001600160a01b0316336001600160a01b031614158015613ab6575033613aaa613e28565b6001600160a01b031614155b156123fb576040517fca455fa500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611c817faa81344d5857c875349bc4a95d531a580c46bdd94c41b35b1e072e4d627079f85490565b6000613b2284613e52565b9050600060028483604051602001613b44929190918252602082015260400190565b60408051601f1981840301815290829052613b5e916159e1565b602060405180830381855afa158015613b7b573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190613b9e91906159fd565b90506000613baa614aa3565b90506000613bb88284614acd565b9050806001600160a01b03163b600003613c5357613bd68284614b49565b506040517f2cc0b2540000000000000000000000000000000000000000000000000000000081526001600160a01b03868116600483015260248201869052821690632cc0b25490604401600060405180830381600087803b158015613c3a57600080fd5b505af1158015613c4e573d6000803e3d6000fd5b505050505b806001600160a01b0316633ccfd60b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015613c8e57600080fd5b505af11580156135e2573d6000803e3d6000fd5b6000611c817fb6d5e19fdd6cde5f03ed4f17c2670deffa47975541fd270b0b17e803297d76085490565b606081613cda81601f6158c5565b1015613d42576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401612e2e565b613d4c82846158c5565b84511015613db6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401612e2e565b606082158015613dd55760405191506000825260208201604052613e1f565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015613e0e578051835260209283019201613df6565b5050858452601f01601f1916604052505b50949350505050565b6000611c817ffbeda9bc03875013b12a1ec161efb8e5bf7e58e3cec96a1ea9efd3e264d26e645490565b6000600282600060801b604051602001613e6d929190615a16565b60408051601f1981840301815290829052613e87916159e1565b602060405180830381855afa158015613ea4573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190611d8691906159fd565b613ed043614c1a565b6040514381527f55c5aabfc91da783d60f72c41f352ac7f9d507fcd884f41e27e6852305f948ce9060200160405180910390a1565b60408051808201825260008082526020808301828152600286901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f0825293822054600686901b60c0161c63ffffffff8181168552911c1690925280517fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054929350916000908190849087908110613fa257613fa261573d565b9060005260206000209060060201600201549050846020015163ffffffff16811161403157602080860151600288901c60009081527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f083526040902080549190921b67ffffffff0000000016600689901b60c01690811b67ffffffffffffffff90911b199091161790556140b6565b60208501516140469063ffffffff168261576c565b602080870151600289901c60009081527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f083526040902080549190921b67ffffffff000000001663ffffffff84161760068a901b60c01690811b67ffffffffffffffff90911b1990911617905591505b8163ffffffff168363ffffffff16146140f6576140f68263ffffffff168463ffffffff166140e2613a32565b6140ec919061576c565b613a6991906158c5565b505050505050565b60007fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054839081106141335761413361573d565b60009182526020909120600690910201600581015490915060ff1615614185576040517f1142a68c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80546001600160a01b03163314610c45576040516282b42960e81b815260040160405180910390fd5b6000611c817fb88142a0318e2b174876bceb4db9dc318011849c83fb8a8bb2997386d562324a5490565b6000611c81613a2e60017f05896f012c7ab472c495a6810b73fc6183d339203dd1c74344bbfd2e1f5038fe61576c565b6123fb7f41118591c19026bdc7a484e34f80a8e7e632600aff1c72460e9c7dfe94a2dda6829055565b60008061423d84613e52565b905060006002848360405160200161425f929190918252602082015260400190565b60408051601f1981840301815290829052614279916159e1565b602060405180830381855afa158015614296573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906142b991906159fd565b905060006142c5614aa3565b90506142d18183614acd565b9695505050505050565b6000611c817f41118591c19026bdc7a484e34f80a8e7e632600aff1c72460e9c7dfe94a2dda65490565b6000611c81613a2e60017f2ad86e81c1a15749eb0399a4f2566324ade0cc7d7f8592a3db8896a29004bf8161576c565b6123fb7fb88142a0318e2b174876bceb4db9dc318011849c83fb8a8bb2997386d562324a829055565b6000611c817f10c92bb459c0223bf996150f2fb702a8288fb8354a33d5b0212e8e6b8273f55e5490565b6000611c817fd5c553085b8382c47128ae7612257fd5dc3b4fc4d3a108925604d3c8700c025b5490565b6123fb7fd5c553085b8382c47128ae7612257fd5dc3b4fc4d3a108925604d3c8700c025b829055565b6123fb61440960017f2ad86e81c1a15749eb0399a4f2566324ade0cc7d7f8592a3db8896a29004bf8161576c565b829055565b6123fb61440960017f05896f012c7ab472c495a6810b73fc6183d339203dd1c74344bbfd2e1f5038fe61576c565b6123fb61440960017fb2e94356976bf59104fa32f6a0cf50947281732def9f18014aacae55ac60c5dd61576c565b6000611c817e595eca1f8b39945ff4c404827bfa5fd1e295ef3f7d59d120a8ce3bae4e37a05490565b6123fb7ffbeda9bc03875013b12a1ec161efb8e5bf7e58e3cec96a1ea9efd3e264d26e64829055565b6123fb7e595eca1f8b39945ff4c404827bfa5fd1e295ef3f7d59d120a8ce3bae4e37a0829055565b6123fb7fce3b5bd42ef5aae9d7b01cd806ce2a8b66f0a48f2e8b5a15f77dfbbdbbf5144f829055565b6001600160a01b0381166123fb576040517ff6b2911f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80614556614c48565b60009384526020526040909220805460ff19169215159290921790915550565b600080611d8660017fd9ed3684910fa17a5f2ad83fd15b2ccb0abf740b7041631a9fe7daffda0cb9ee61576c565b6000611c81613a2e60017f7d3541d2c0079305636180d42008f210385563932c50e421bd8654ed4a6017eb61576c565b6000613227614c48565b6123fb7f10c92bb459c0223bf996150f2fb702a8288fb8354a33d5b0212e8e6b8273f55e829055565b6123fb7faa81344d5857c875349bc4a95d531a580c46bdd94c41b35b1e072e4d627079f8829055565b6123fb7fb6d5e19fdd6cde5f03ed4f17c2670deffa47975541fd270b0b17e803297d7608829055565b6123fb7fbc8b9852d17d50256bb221fdf6ee12d78dd493d807e907f7d223c40d65abd6b9829055565b6123fb7fd1c64973da70267569571a091966834c1a36bdba47f2a112b6a95bf41fc9c24e829055565b6000611c817fce3b5bd42ef5aae9d7b01cd806ce2a8b66f0a48f2e8b5a15f77dfbbdbbf5144f5490565b6123fb7f559ad51499ae00ca2e9d9d95aab46737c8904ab7da276613fefda282b2c2ac06829055565b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980546000908290859081106147365761473661573d565b6000918252602080832060408051808201825285815280840186815260028b901c87527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f08552919095205460068a811b60c0169190911c63ffffffff8181168852941c90931690819052929091020192505b84826020015163ffffffff166147be91906158c5565b8110156149f75760008360030182815481106147dc576147dc61573d565b9060005260206000200180546147f190615783565b80601f016020809104026020016040519081016040528092919081815260200182805461481d90615783565b801561486a5780601f1061483f5761010080835404028352916020019161486a565b820191906000526020600020905b81548152906001019060200180831161484d57829003601f168201915b5050505050905060008460040183815481106148885761488861573d565b90600052602060002001805461489d90615783565b80601f01602080910402602001604051908101604052809291908181526020018280546148c990615783565b80156149165780601f106148eb57610100808354040283529160200191614916565b820191906000526020600020905b8154815290600101906020018083116148f957829003601f168201915b50505050509050600061492a836001614231565b9050600061493782614c76565b9050600061494485613e52565b905061495285828685614cab565b60008181527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260409081902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001633908117909155905181907fac1020908b5f7134d59c1580838eba6fc42dd8c28bae65bf345676bba1913f8e906149df9089908990615a5d565b60405180910390a385600101955050505050506147a8565b50614a9c8585836000015163ffffffff16614a12919061576c565b86846020015163ffffffff16614a2891906158c5565b600283901c60009081527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f060209081526040909120805467ffffffffffffffff60c060069790971b9690961695861b191663ffffffff9490941667ffffffff000000009390921b929092161790921b179055565b5050505050565b6000611c817fd1c64973da70267569571a091966834c1a36bdba47f2a112b6a95bf41fc9c24e5490565b6000611d838383306040517f3d602d80600a3d3981f3363d3d373d3d3d363d730000000000000000000000008152606093841b60148201527f5af43d82803e903d91602b57fd5bf3ff000000000000000000000000000000006028820152921b6038830152604c8201526037808220606c830152605591012090565b60006040517f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000081528360601b60148201527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006028820152826037826000f59150506001600160a01b038116611d86576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f455243313136373a2063726561746532206661696c65640000000000000000006044820152606401612e2e565b6123fb61440960017f7d3541d2c0079305636180d42008f210385563932c50e421bd8654ed4a6017eb61576c565b600080611d8660017f68ed1186fe6f93f91e23b08dcba07651b0153d3ab058aab96212b33aaaf3a89961576c565b6000611d867f01000000000000000000000000000000000000000000000000000000000000006001600160a01b0384166158c5565b6000600280614cbd8560006040613ccc565b604051614cca91906159e1565b602060405180830381855afa158015614ce7573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614d0a91906159fd565b6002614d22866040614d1d81606061576c565b613ccc565b604051614d359190600090602001615a8b565b60408051601f1981840301815290829052614d4f916159e1565b602060405180830381855afa158015614d6c573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614d8f91906159fd565b60408051602081019390935282015260600160408051601f1981840301815290829052614dbb916159e1565b602060405180830381855afa158015614dd8573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614dfb91906159fd565b905060006002808685604051602001614e1e929190918252602082015260400190565b60408051601f1981840301815290829052614e38916159e1565b602060405180830381855afa158015614e55573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614e7891906159fd565b604080517e40597307000000000000000000000000000000000000000000000000000000602082015290810185905260029060600160408051601f1981840301815290829052614ec7916159e1565b602060405180830381855afa158015614ee4573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614f0791906159fd565b60408051602081019390935282015260600160408051601f1981840301815290829052614f33916159e1565b602060405180830381855afa158015614f50573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614f7391906159fd565b90506000614f8a6801bc16d674ec8000004761576c565b9050614f9461505b565b6001600160a01b031663228951186801bc16d674ec8000008987604051602001614fc091815260200190565b60405160208183030381529060405289876040518663ffffffff1660e01b8152600401614ff09493929190615aad565b6000604051808303818588803b15801561500957600080fd5b505af115801561501d573d6000803e3d6000fd5b5050505050804714611c6d576040517f6596d2b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611c817fbc8b9852d17d50256bb221fdf6ee12d78dd493d807e907f7d223c40d65abd6b95490565b50805461509190615783565b6000825580601f106150a1575050565b601f0160209004906000526020600020908101906123fb9190615217565b8280546150cb90615783565b90600052602060002090601f0160209004810192826150ed576000855561513a565b82601f106150fe578054855561513a565b8280016001018555821561513a57600052602060002091601f016020900482015b8281111561513a57825482559160010191906001019061511f565b50615146929150615217565b5090565b828054828255906000526020600020908101928215615197579160200282015b8281111561519757825180516151879184916020909101906151a3565b509160200191906001019061516a565b5061514692915061522c565b8280546151af90615783565b90600052602060002090601f0160209004810192826151d1576000855561513a565b82601f106151ea57805160ff191683800117855561513a565b8280016001018555821561513a579182015b8281111561513a5782518255916020019190600101906151fc565b5b808211156151465760008155600101615218565b808211156151465760006152408282615085565b5060010161522c565b60006020828403121561525b57600080fd5b5035919050565b60008083601f84011261527457600080fd5b50813567ffffffffffffffff81111561528c57600080fd5b6020830191508360208285010111156152a457600080fd5b9250929050565b600080602083850312156152be57600080fd5b823567ffffffffffffffff8111156152d557600080fd5b6152e185828601615262565b90969095509350505050565b60008060006040848603121561530257600080fd5b83359250602084013567ffffffffffffffff8082111561532157600080fd5b818601915086601f83011261533557600080fd5b81358181111561534457600080fd5b8760208260051b850101111561535957600080fd5b6020830194508093505050509250925092565b80356001600160a01b038116811461538357600080fd5b919050565b6000806040838503121561539b57600080fd5b823591506153ab6020840161536c565b90509250929050565b600080604083850312156153c757600080fd5b50508035926020909101359150565b60005b838110156153f15781810151838201526020016153d9565b83811115611c715750506000910152565b6000815180845261541a8160208601602086016153d6565b601f01601f19169290920160200192915050565b6080815260006154416080830187615402565b82810360208401526154538187615402565b6001600160a01b03959095166040840152505090151560609091015292915050565b60006020828403121561548757600080fd5b8135801515811461549757600080fd5b9392505050565b600080604083850312156154b157600080fd5b6154ba8361536c565b91506153ab6020840161536c565b6000806000606084860312156154dd57600080fd5b833592506154ed6020850161536c565b91506154fb6040850161536c565b90509250925092565b6000806000806000806080878903121561551d57600080fd5b8635955060208701359450604087013567ffffffffffffffff8082111561554357600080fd5b61554f8a838b01615262565b9096509450606089013591508082111561556857600080fd5b5061557589828a01615262565b979a9699509497509295939492505050565b60008060006060848603121561559c57600080fd5b505081359360208301359350604090920135919050565b6000806000806000806000806000806101408b8d0312156155d357600080fd5b6155dc8b61536c565b99506155ea60208c0161536c565b98506155f860408c0161536c565b975061560660608c0161536c565b965061561460808c0161536c565b955061562260a08c0161536c565b945060c08b0135935060e08b013592506101008b013591506101208b013590509295989b9194979a5092959850565b60008060006040848603121561566657600080fd5b833567ffffffffffffffff81111561567d57600080fd5b61568986828701615262565b90945092506154fb90506020850161536c565b6000602082840312156156ae57600080fd5b611d838261536c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826156f5576156f56156b7565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082615738576157386156b7565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008282101561577e5761577e6156fa565b500390565b600181811c9082168061579757607f821691505b6020821081036157d0577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600060208083526000845481600182811c9150808316806157f857607f831692505b858310810361582e577f4e487b710000000000000000000000000000000000000000000000000000000085526022600452602485fd5b87860183815260200181801561584b576001811461585c57615887565b60ff19861682528782019650615887565b60008b81526020902060005b8681101561588157815484820152908501908901615868565b83019750505b50949998505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600082198211156158d8576158d86156fa565b500190565b6001600160a01b03831681526040602082015260006158ff6040830184615402565b949350505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561593f5761593f6156fa565b500290565b602081526000611d836020830184615402565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b604081526000615996604083018688615957565b82810360208401526159a9818587615957565b979650505050505050565b6040815260006159c8604083018587615957565b90506001600160a01b0383166020830152949350505050565b600082516159f38184602087016153d6565b9190910192915050565b600060208284031215615a0f57600080fd5b5051919050565b60008351615a288184602088016153d6565b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000939093169190920190815260100192915050565b604081526000615a706040830185615402565b8281036020840152615a828185615402565b95945050505050565b60008351615a9d8184602088016153d6565b9190910191825250602001919050565b608081526000615ac06080830187615402565b8281036020840152615ad28187615402565b90508281036040840152615ae68186615402565b9150508260608301529594505050505056fea264697066735822122082f275dcc95ed77ce7470cc6de8ad96480198f43db9aa8fe8aa8314355ecbd8364736f6c634300080d0033", + "devdoc": { + "author": "Kiln", + "kind": "dev", + "methods": { + "acceptOwnership()": { + "details": "Only callable by new admin" + }, + "activateOperator(uint256,address)": { + "params": { + "_newFeeRecipient": "Sets the fee recipient address", + "_operatorIndex": "Operator Index" + } + }, + "addOperator(address,address)": { + "details": "Only callable by admin", + "params": { + "_feeRecipientAddress": "Privileged operator address used to manage rewards and operator addresses", + "_operatorAddress": "Operator address allowed to add / remove validators" + } + }, + "addValidators(uint256,uint256,bytes,bytes)": { + "details": "Only callable by operator", + "params": { + "_keyCount": "Number of keys added", + "_operatorIndex": "Operator Index", + "_publicKeys": "Concatenated _keyCount public keys", + "_signatures": "Concatenated _keyCount signatures" + } + }, + "batchWithdraw(bytes)": { + "details": "Funds are sent to the withdrawer account", + "params": { + "_publicKeys": "Validators to withdraw fees from" + } + }, + "batchWithdrawCLFee(bytes)": { + "details": "Funds are sent to the withdrawer accountThis method is public on purpose", + "params": { + "_publicKeys": "Validators to withdraw Consensus Layer Fees from" + } + }, + "batchWithdrawELFee(bytes)": { + "details": "Funds are sent to the withdrawer accountThis method is public on purpose", + "params": { + "_publicKeys": "Validators to withdraw Execution Layer Fees from" + } + }, + "deactivateOperator(uint256,address)": { + "params": { + "_operatorIndex": "Operator Index", + "_temporaryFeeRecipient": "Temporary address to receive funds decided by the system admin" + } + }, + "deposit()": { + "details": "A multiple of 32 ETH should be sent" + }, + "getCLFeeRecipient(bytes)": { + "params": { + "_publicKey": "Validator to get the recipient" + } + }, + "getELFeeRecipient(bytes)": { + "params": { + "_publicKey": "Validator to get the recipient" + } + }, + "getEnabledFromPublicKeyRoot(bytes32)": { + "params": { + "_publicKeyRoot": "Hash of the public key" + } + }, + "getExitRequestedFromRoot(bytes32)": { + "params": { + "_publicKeyRoot": "Public Key Root to check" + } + }, + "getOperator(uint256)": { + "params": { + "_operatorIndex": "Operator index" + } + }, + "getValidator(uint256,uint256)": { + "params": { + "_operatorIndex": "Index of the operator running the validator", + "_validatorIndex": "Index of the validator" + } + }, + "getWithdrawer(bytes)": { + "params": { + "_publicKey": "Public Key to check" + } + }, + "getWithdrawerFromPublicKeyRoot(bytes32)": { + "params": { + "_publicKeyRoot": "Hash of the public key" + } + }, + "getWithdrawnFromPublicKeyRoot(bytes32)": { + "params": { + "_publicKeyRoot": "Public Key Root of the validator" + } + }, + "removeValidators(uint256,uint256[])": { + "details": "Only callable by operatorIndexes should be provided in decreasing orderThe limit will be set to the lowest removed operator index to ensure all changes above the lowest removed validator key are verified by the system administrator", + "params": { + "_indexes": "List of indexes to delete, in decreasing order", + "_operatorIndex": "Operator Index" + } + }, + "setGlobalFee(uint256)": { + "params": { + "_globalFee": "Fee in Basis Point" + } + }, + "setOperatorAddresses(uint256,address,address)": { + "details": "Only callable by fee recipient address manager", + "params": { + "_feeRecipientAddress": "New operator address for reward management", + "_operatorAddress": "New operator address for operations management", + "_operatorIndex": "Index of the operator to update" + } + }, + "setOperatorFee(uint256)": { + "params": { + "_operatorFee": "Fee in Basis Point" + } + }, + "setOperatorLimit(uint256,uint256,uint256)": { + "details": "Only callable by adminLimit should not exceed the validator key count of the operatorKeys should be registered before limit is increasedAllows all keys to be verified by the system admin before limit is increased", + "params": { + "_limit": "New staking limit", + "_operatorIndex": "Operator Index", + "_snapshot": "Block number at which verification was done" + } + }, + "setTreasury(address)": { + "details": "Only callable by admin", + "params": { + "_newTreasury": "New Treasury address" + } + }, + "setWithdrawer(bytes,address)": { + "details": "Only callable by current public key withdrawer", + "params": { + "_newWithdrawer": "New withdrawer address", + "_publicKey": "Public key to change withdrawer" + } + }, + "setWithdrawerCustomizationEnabled(bool)": { + "params": { + "_enabled": "True to allow users to customize the withdrawer" + } + }, + "toggleWithdrawnFromPublicKeyRoot(bytes32)": { + "params": { + "_publicKeyRoot": "Public Key Root of the validator" + } + }, + "transferOwnership(address)": { + "details": "Only callable by admin", + "params": { + "_newAdmin": "New Administrator address" + } + }, + "withdraw(bytes)": { + "details": "Reverts if any is null", + "params": { + "_publicKey": "Validator to withdraw Execution and Consensus Layer Fees from" + } + }, + "withdrawCLFee(bytes)": { + "details": "Funds are sent to the withdrawer account", + "params": { + "_publicKey": "Validator to withdraw Consensus Layer Fees from" + } + }, + "withdrawELFee(bytes)": { + "details": "Funds are sent to the withdrawer account", + "params": { + "_publicKey": "Validator to withdraw Execution Layer Fees from" + } + } + }, + "title": "Ethereum Staking Contract", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "acceptOwnership()": { + "notice": "New admin must accept its role by calling this method" + }, + "activateOperator(uint256,address)": { + "notice": "Activates an operator, without changing its 0 staking limit" + }, + "addOperator(address,address)": { + "notice": "Add new operator" + }, + "addValidators(uint256,uint256,bytes,bytes)": { + "notice": "Add new validator public keys and signatures" + }, + "batchWithdraw(bytes)": { + "notice": "Withdraw both Consensus and Execution Layer Fees for given validators public keys" + }, + "batchWithdrawCLFee(bytes)": { + "notice": "Withdraw the Consensus Layer Fee for given validators public keys" + }, + "batchWithdrawELFee(bytes)": { + "notice": "Withdraw the Execution Layer Fee for given validators public keys" + }, + "deactivateOperator(uint256,address)": { + "notice": "Deactivates an operator and changes the fee recipient address and the staking limit" + }, + "deposit()": { + "notice": "Explicit deposit method using msg.sender" + }, + "getAdmin()": { + "notice": "Retrieve system admin" + }, + "getAvailableValidatorCount()": { + "notice": "Get the total available keys that are ready to be used for deposits" + }, + "getCLFeeRecipient(bytes)": { + "notice": "Compute the Consensus Layer Fee recipient address for a given validator public key" + }, + "getDepositsStopped()": { + "notice": "Returns false if the users can deposit, true if deposits are stopped" + }, + "getELFeeRecipient(bytes)": { + "notice": "Compute the Execution Layer Fee recipient address for a given validator public key" + }, + "getEnabledFromPublicKeyRoot(bytes32)": { + "notice": "Retrieve the enabled status of public key root, true if the key is in the contract" + }, + "getExitRequestedFromRoot(bytes32)": { + "notice": "Retrieve whether the validator exit has been requestedIn case the validator is not enabled, it will return false" + }, + "getGlobalFee()": { + "notice": "Retrieve the global fee" + }, + "getOperator(uint256)": { + "notice": "Retrieve operator details" + }, + "getOperatorFee()": { + "notice": "Retrieve the operator fee" + }, + "getOperatorFeeRecipient(bytes32)": { + "notice": "Retrieve the Execution & Consensus Layer Fee operator recipient for a given public key" + }, + "getPendingAdmin()": { + "notice": "Get the new admin's address previously set for an ownership transfer" + }, + "getTreasury()": { + "notice": "Retrieve system treasury" + }, + "getValidator(uint256,uint256)": { + "notice": "Get details about a validator" + }, + "getWithdrawer(bytes)": { + "notice": "Retrieve withdrawer of public keyIn case the validator is not enabled, it will return address(0)" + }, + "getWithdrawerFromPublicKeyRoot(bytes32)": { + "notice": "Retrieve withdrawer of public key rootIn case the validator is not enabled, it will return address(0)" + }, + "getWithdrawnFromPublicKeyRoot(bytes32)": { + "notice": "Return true if the validator already went through the exit logicIn case the validator is not enabled, it will return false" + }, + "removeValidators(uint256,uint256[])": { + "notice": "Remove unfunded validators" + }, + "setDepositsStopped(bool)": { + "notice": "Utility to stop or allow deposits" + }, + "setGlobalFee(uint256)": { + "notice": "Change the Global fee" + }, + "setOperatorAddresses(uint256,address,address)": { + "notice": "Set new operator addresses (operations and reward management)" + }, + "setOperatorFee(uint256)": { + "notice": "Change the Operator fee" + }, + "setOperatorLimit(uint256,uint256,uint256)": { + "notice": "Set operator staking limits" + }, + "setTreasury(address)": { + "notice": "Set new treasury" + }, + "setWithdrawer(bytes,address)": { + "notice": "Set withdrawer for public key" + }, + "setWithdrawerCustomizationEnabled(bool)": { + "notice": "Changes the behavior of the withdrawer customization logic" + }, + "toggleWithdrawnFromPublicKeyRoot(bytes32)": { + "notice": "Allows the CLDispatcher to signal a validator went through the exit logic" + }, + "transferOwnership(address)": { + "notice": "Set new admin" + }, + "withdraw(bytes)": { + "notice": "Withdraw both Consensus and Execution Layer Fee for a given validator public key" + }, + "withdrawCLFee(bytes)": { + "notice": "Withdraw the Consensus Layer Fee for a given validator public key" + }, + "withdrawELFee(bytes)": { + "notice": "Withdraw the Execution Layer Fee for a given validator public key" + } + }, + "notice": "You can use this contract to store validator keys and have users fund them and trigger deposits.", + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/deployments/holesky_dev_safe/solcInputs/e20c74109a1c61285ef1a2afbbbc70eb.json b/deployments/holesky_dev_safe/solcInputs/e20c74109a1c61285ef1a2afbbbc70eb.json new file mode 100644 index 0000000..9c74564 --- /dev/null +++ b/deployments/holesky_dev_safe/solcInputs/e20c74109a1c61285ef1a2afbbbc70eb.json @@ -0,0 +1,100 @@ +{ + "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": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates", + "devdoc", + "userdoc" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/deployments/mainnet_safe/.chainId b/deployments/mainnet_safe/.chainId new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/deployments/mainnet_safe/.chainId @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/deployments/mainnet_safe/ConsensusLayerFeeDispatcher.json b/deployments/mainnet_safe/ConsensusLayerFeeDispatcher.json new file mode 100644 index 0000000..2473e89 --- /dev/null +++ b/deployments/mainnet_safe/ConsensusLayerFeeDispatcher.json @@ -0,0 +1,241 @@ +{ + "address": "0x2EA6BB1376531f6272e877eaF86608D18CB278d3", + "abi": [ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_version", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "errorData", + "type": "bytes" + } + ], + "name": "FeeRecipientReceiveError", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "errorData", + "type": "bytes" + } + ], + "name": "TreasuryReceiveError", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "errorData", + "type": "bytes" + } + ], + "name": "WithdrawerReceiveError", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroBalanceWithdrawal", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "withdrawer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "feeRecipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "pubKeyRoot", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "rewards", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nodeOperatorFee", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "treasuryFee", + "type": "uint256" + } + ], + "name": "Withdrawal", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "dispatch", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getStakingContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "getWithdrawer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_stakingContract", + "type": "address" + } + ], + "name": "initCLD", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0x71f1ab9454b41631880738389a0d6484b7962a68da01daf49119ed7bc3c90f28", + "receipt": { + "to": null, + "from": "0x6C74FDa18Ea90E1C61e800Dd9B4508Ac782e0Dc8", + "contractAddress": "0x2EA6BB1376531f6272e877eaF86608D18CB278d3", + "transactionIndex": 20, + "gasUsed": "765166", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xc4d3773068abb67ade9c21d788743b23953c2df33e23ff0c1a5501ab46d50029", + "transactionHash": "0x71f1ab9454b41631880738389a0d6484b7962a68da01daf49119ed7bc3c90f28", + "logs": [], + "blockNumber": 20583410, + "cumulativeGasUsed": "4647113", + "status": 1, + "byzantium": true + }, + "args": [ + 0 + ], + "numDeployments": 1, + "solcInputHash": "e20c74109a1c61285ef1a2afbbbc70eb", + "metadata": "{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_version\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AlreadyInitialized\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorData\",\"type\":\"bytes\"}],\"name\":\"FeeRecipientReceiveError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorData\",\"type\":\"bytes\"}],\"name\":\"TreasuryReceiveError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorData\",\"type\":\"bytes\"}],\"name\":\"WithdrawerReceiveError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroBalanceWithdrawal\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"withdrawer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeRecipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"pubKeyRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rewards\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorFee\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"treasuryFee\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"dispatch\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStakingContract\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"getWithdrawer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_stakingContract\",\"type\":\"address\"}],\"name\":\"initCLD\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"author\":\"Kiln\",\"kind\":\"dev\",\"methods\":{\"getWithdrawer(bytes32)\":{\"params\":{\"_publicKeyRoot\":\"Public key root to get the owner\"}},\"initCLD(address)\":{\"params\":{\"_stakingContract\":\"Address of the Staking Contract\"}}},\"title\":\"Consensus Layer Fee Recipient\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"constructor\":{\"notice\":\"Constructor method allowing us to prevent calls to initCLFR by setting the appropriate version\"},\"dispatch(bytes32)\":{\"notice\":\"Performs a withdrawal on this contract's balance\"},\"getStakingContract()\":{\"notice\":\"Retrieve the staking contract address\"},\"getWithdrawer(bytes32)\":{\"notice\":\"Retrieve the assigned withdrawer for the given public key root\"},\"initCLD(address)\":{\"notice\":\"Initialize the contract by storing the staking contract\"}},\"notice\":\"This contract can be used to receive fees from a validator and split them with a node operator\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/contracts/ConsensusLayerFeeDispatcher.sol\":\"ConsensusLayerFeeDispatcher\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"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\",\"keccak256\":\"0x5852cbcf53e2454bb6b9c662fb32e2f35aeb3cdd5f0c341a9eb6c7d8ad0e6523\",\"license\":\"BUSL-1.1\"},\"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\",\"keccak256\":\"0x75efa5a697b32235188a62f730b7ab2a2fc5c422a93010aa0b18e93ea1bade45\",\"license\":\"MIT\"},\"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\",\"keccak256\":\"0xca5bfd023b6c479ab82320ebced9a560f9b8ffa61df27c02e83e7cb7fa23f569\",\"license\":\"MIT\"},\"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\",\"keccak256\":\"0x585a11c0ae6f9fda70d5d242ce02336bfd60fd75afed28495156307ba944f6cc\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x608060405234801561001057600080fd5b50604051610d7b380380610d7b83398101604081905261002f91610070565b610066817f6081a8ce97ad8a3951bac5b885ad6097d6b55cb45a6dd62b5069939f8c95ffef61006c60201b610aad1790919060201c565b50610089565b9055565b60006020828403121561008257600080fd5b5051919050565b610ce3806100986000396000f3fe6080604052600436106100435760003560e01c806327de9016146100ac5780635f0f28d9146100f55780638e68dce414610117578063ce1206501461012c5761007a565b3661007a576040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3480156100b857600080fd5b506100cc6100c7366004610ab1565b61013f565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b34801561010157600080fd5b50610115610110366004610aef565b610203565b005b34801561012357600080fd5b506100cc6102c6565b61011561013a366004610ab1565b6102f5565b60008061016a7ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a515490565b6040517fa74008010000000000000000000000000000000000000000000000000000000081526004810185905290915073ffffffffffffffffffffffffffffffffffffffff82169063a740080190602401602060405180830381865afa1580156101d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101fc9190610b0c565b9392505050565b600161022d7f6081a8ce97ad8a3951bac5b885ad6097d6b55cb45a6dd62b5069939f8c95ffef5490565b610238906001610b58565b8114610270576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6102997f6081a8ce97ad8a3951bac5b885ad6097d6b55cb45a6dd62b5069939f8c95ffef829055565b6102c27ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a51839055565b5050565b60006102f07ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a515490565b905090565b600061031f7ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a515490565b905047600081900361035d576040517fb6562b0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fc13d03500000000000000000000000000000000000000000000000000000000081526004810184905260009073ffffffffffffffffffffffffffffffffffffffff84169063c13d035090602401602060405180830381865afa1580156103cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ef9190610b70565b6040517fcac594df0000000000000000000000000000000000000000000000000000000081526004810186905290915060009073ffffffffffffffffffffffffffffffffffffffff85169063cac594df90602401602060405180830381865afa158015610460573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104849190610b70565b90508282801561049d57506801ae361fc1451c00008410155b80156104a7575081155b156105605760006801bc16d674ec80000082116104c457816104cf565b6801bc16d674ec8000005b90506104db8183610b92565b6040517fb86bcaf70000000000000000000000000000000000000000000000000000000081526004810189905290925073ffffffffffffffffffffffffffffffffffffffff87169063b86bcaf790602401600060405180830381600087803b15801561054657600080fd5b505af115801561055a573d6000803e3d6000fd5b50505050505b60006127108673ffffffffffffffffffffffffffffffffffffffff16631bcbfaba6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105d49190610ba9565b6105de9084610bc2565b6105e89190610bff565b905060006127108773ffffffffffffffffffffffffffffffffffffffff1663286966086040518163ffffffff1660e01b8152600401602060405180830381865afa15801561063a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061065e9190610ba9565b6106689084610bc2565b6106729190610bff565b6040517f9adf91ee000000000000000000000000000000000000000000000000000000008152600481018a905290915060009073ffffffffffffffffffffffffffffffffffffffff891690639adf91ee90602401602060405180830381865afa1580156106e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107079190610b0c565b905060008873ffffffffffffffffffffffffffffffffffffffff16633b19e84a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610756573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061077a9190610b0c565b6040517fa7400801000000000000000000000000000000000000000000000000000000008152600481018c905290915060009073ffffffffffffffffffffffffffffffffffffffff8b169063a740080190602401602060405180830381865afa1580156107eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061080f9190610b0c565b905060008073ffffffffffffffffffffffffffffffffffffffff8316610835888d610b92565b604051600081818185875af1925050503d8060008114610871576040519150601f19603f3d011682016040523d82523d6000602084013e610876565b606091505b5090925090508115156000036108c357806040517fce13343d0000000000000000000000000000000000000000000000000000000081526004016108ba9190610c3a565b60405180910390fd5b861561096f5773ffffffffffffffffffffffffffffffffffffffff84166108ea8789610b92565b604051600081818185875af1925050503d8060008114610926576040519150601f19603f3d011682016040523d82523d6000602084013e61092b565b606091505b50909250905081151560000361096f57806040517fbc98622d0000000000000000000000000000000000000000000000000000000081526004016108ba9190610c3a565b8515610a145760405173ffffffffffffffffffffffffffffffffffffffff8616908790600081818185875af1925050503d80600081146109cb576040519150601f19603f3d011682016040523d82523d6000602084013e6109d0565b606091505b509092509050811515600003610a1457806040517fe5ea83e70000000000000000000000000000000000000000000000000000000081526004016108ba9190610c3a565b8473ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f570610cb78811f3a7f90d272791d0a5e71648fb7368280519fc5866e5184db1d8f8a8f610a709190610b92565b8a610a7b818e610b92565b60408051948552602085019390935291830152606082015260800160405180910390a350505050505050505050505050565b9055565b600060208284031215610ac357600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610aec57600080fd5b50565b600060208284031215610b0157600080fd5b81356101fc81610aca565b600060208284031215610b1e57600080fd5b81516101fc81610aca565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115610b6b57610b6b610b29565b500190565b600060208284031215610b8257600080fd5b815180151581146101fc57600080fd5b600082821015610ba457610ba4610b29565b500390565b600060208284031215610bbb57600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615610bfa57610bfa610b29565b500290565b600082610c35577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600060208083528351808285015260005b81811015610c6757858101830151858201604001528201610c4b565b81811115610c79576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201604001939250505056fea26469706673582212202da726a0ebf275a4ea33e21c3cea1b07a08ff132835baf11a00a6a7218c4d46464736f6c634300080d0033", + "deployedBytecode": "0x6080604052600436106100435760003560e01c806327de9016146100ac5780635f0f28d9146100f55780638e68dce414610117578063ce1206501461012c5761007a565b3661007a576040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3480156100b857600080fd5b506100cc6100c7366004610ab1565b61013f565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b34801561010157600080fd5b50610115610110366004610aef565b610203565b005b34801561012357600080fd5b506100cc6102c6565b61011561013a366004610ab1565b6102f5565b60008061016a7ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a515490565b6040517fa74008010000000000000000000000000000000000000000000000000000000081526004810185905290915073ffffffffffffffffffffffffffffffffffffffff82169063a740080190602401602060405180830381865afa1580156101d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101fc9190610b0c565b9392505050565b600161022d7f6081a8ce97ad8a3951bac5b885ad6097d6b55cb45a6dd62b5069939f8c95ffef5490565b610238906001610b58565b8114610270576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6102997f6081a8ce97ad8a3951bac5b885ad6097d6b55cb45a6dd62b5069939f8c95ffef829055565b6102c27ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a51839055565b5050565b60006102f07ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a515490565b905090565b600061031f7ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a515490565b905047600081900361035d576040517fb6562b0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fc13d03500000000000000000000000000000000000000000000000000000000081526004810184905260009073ffffffffffffffffffffffffffffffffffffffff84169063c13d035090602401602060405180830381865afa1580156103cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ef9190610b70565b6040517fcac594df0000000000000000000000000000000000000000000000000000000081526004810186905290915060009073ffffffffffffffffffffffffffffffffffffffff85169063cac594df90602401602060405180830381865afa158015610460573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104849190610b70565b90508282801561049d57506801ae361fc1451c00008410155b80156104a7575081155b156105605760006801bc16d674ec80000082116104c457816104cf565b6801bc16d674ec8000005b90506104db8183610b92565b6040517fb86bcaf70000000000000000000000000000000000000000000000000000000081526004810189905290925073ffffffffffffffffffffffffffffffffffffffff87169063b86bcaf790602401600060405180830381600087803b15801561054657600080fd5b505af115801561055a573d6000803e3d6000fd5b50505050505b60006127108673ffffffffffffffffffffffffffffffffffffffff16631bcbfaba6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105d49190610ba9565b6105de9084610bc2565b6105e89190610bff565b905060006127108773ffffffffffffffffffffffffffffffffffffffff1663286966086040518163ffffffff1660e01b8152600401602060405180830381865afa15801561063a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061065e9190610ba9565b6106689084610bc2565b6106729190610bff565b6040517f9adf91ee000000000000000000000000000000000000000000000000000000008152600481018a905290915060009073ffffffffffffffffffffffffffffffffffffffff891690639adf91ee90602401602060405180830381865afa1580156106e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107079190610b0c565b905060008873ffffffffffffffffffffffffffffffffffffffff16633b19e84a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610756573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061077a9190610b0c565b6040517fa7400801000000000000000000000000000000000000000000000000000000008152600481018c905290915060009073ffffffffffffffffffffffffffffffffffffffff8b169063a740080190602401602060405180830381865afa1580156107eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061080f9190610b0c565b905060008073ffffffffffffffffffffffffffffffffffffffff8316610835888d610b92565b604051600081818185875af1925050503d8060008114610871576040519150601f19603f3d011682016040523d82523d6000602084013e610876565b606091505b5090925090508115156000036108c357806040517fce13343d0000000000000000000000000000000000000000000000000000000081526004016108ba9190610c3a565b60405180910390fd5b861561096f5773ffffffffffffffffffffffffffffffffffffffff84166108ea8789610b92565b604051600081818185875af1925050503d8060008114610926576040519150601f19603f3d011682016040523d82523d6000602084013e61092b565b606091505b50909250905081151560000361096f57806040517fbc98622d0000000000000000000000000000000000000000000000000000000081526004016108ba9190610c3a565b8515610a145760405173ffffffffffffffffffffffffffffffffffffffff8616908790600081818185875af1925050503d80600081146109cb576040519150601f19603f3d011682016040523d82523d6000602084013e6109d0565b606091505b509092509050811515600003610a1457806040517fe5ea83e70000000000000000000000000000000000000000000000000000000081526004016108ba9190610c3a565b8473ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f570610cb78811f3a7f90d272791d0a5e71648fb7368280519fc5866e5184db1d8f8a8f610a709190610b92565b8a610a7b818e610b92565b60408051948552602085019390935291830152606082015260800160405180910390a350505050505050505050505050565b9055565b600060208284031215610ac357600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610aec57600080fd5b50565b600060208284031215610b0157600080fd5b81356101fc81610aca565b600060208284031215610b1e57600080fd5b81516101fc81610aca565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115610b6b57610b6b610b29565b500190565b600060208284031215610b8257600080fd5b815180151581146101fc57600080fd5b600082821015610ba457610ba4610b29565b500390565b600060208284031215610bbb57600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615610bfa57610bfa610b29565b500290565b600082610c35577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600060208083528351808285015260005b81811015610c6757858101830151858201604001528201610c4b565b81811115610c79576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201604001939250505056fea26469706673582212202da726a0ebf275a4ea33e21c3cea1b07a08ff132835baf11a00a6a7218c4d46464736f6c634300080d0033", + "devdoc": { + "author": "Kiln", + "kind": "dev", + "methods": { + "getWithdrawer(bytes32)": { + "params": { + "_publicKeyRoot": "Public key root to get the owner" + } + }, + "initCLD(address)": { + "params": { + "_stakingContract": "Address of the Staking Contract" + } + } + }, + "title": "Consensus Layer Fee Recipient", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "constructor": { + "notice": "Constructor method allowing us to prevent calls to initCLFR by setting the appropriate version" + }, + "dispatch(bytes32)": { + "notice": "Performs a withdrawal on this contract's balance" + }, + "getStakingContract()": { + "notice": "Retrieve the staking contract address" + }, + "getWithdrawer(bytes32)": { + "notice": "Retrieve the assigned withdrawer for the given public key root" + }, + "initCLD(address)": { + "notice": "Initialize the contract by storing the staking contract" + } + }, + "notice": "This contract can be used to receive fees from a validator and split them with a node operator", + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/deployments/mainnet_safe/ExecutionLayerFeeDispatcher.json b/deployments/mainnet_safe/ExecutionLayerFeeDispatcher.json new file mode 100644 index 0000000..2ff8cf8 --- /dev/null +++ b/deployments/mainnet_safe/ExecutionLayerFeeDispatcher.json @@ -0,0 +1,241 @@ +{ + "address": "0xf26dB8ca5C95BA640F011653e17b2B1AEa17ABb6", + "abi": [ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_version", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "errorData", + "type": "bytes" + } + ], + "name": "FeeRecipientReceiveError", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "errorData", + "type": "bytes" + } + ], + "name": "TreasuryReceiveError", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "errorData", + "type": "bytes" + } + ], + "name": "WithdrawerReceiveError", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroBalanceWithdrawal", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "withdrawer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "feeRecipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "pubKeyRoot", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "rewards", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nodeOperatorFee", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "treasuryFee", + "type": "uint256" + } + ], + "name": "Withdrawal", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "dispatch", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getStakingContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "getWithdrawer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_stakingContract", + "type": "address" + } + ], + "name": "initELD", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0x6b568501c4243fcbecf6e6a14defeff1bf9ccdb9932f6858ac4cd56bfd5858da", + "receipt": { + "to": null, + "from": "0x6C74FDa18Ea90E1C61e800Dd9B4508Ac782e0Dc8", + "contractAddress": "0xf26dB8ca5C95BA640F011653e17b2B1AEa17ABb6", + "transactionIndex": 193, + "gasUsed": "642423", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xbfb8db4e0cb67e2959dfbf4a356707da427cda90be5f2eb85800f2004b8ffd76", + "transactionHash": "0x6b568501c4243fcbecf6e6a14defeff1bf9ccdb9932f6858ac4cd56bfd5858da", + "logs": [], + "blockNumber": 20583412, + "cumulativeGasUsed": "16618446", + "status": 1, + "byzantium": true + }, + "args": [ + 0 + ], + "numDeployments": 1, + "solcInputHash": "e20c74109a1c61285ef1a2afbbbc70eb", + "metadata": "{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_version\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AlreadyInitialized\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorData\",\"type\":\"bytes\"}],\"name\":\"FeeRecipientReceiveError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorData\",\"type\":\"bytes\"}],\"name\":\"TreasuryReceiveError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorData\",\"type\":\"bytes\"}],\"name\":\"WithdrawerReceiveError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroBalanceWithdrawal\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"withdrawer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeRecipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"pubKeyRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rewards\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorFee\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"treasuryFee\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"dispatch\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStakingContract\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"getWithdrawer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_stakingContract\",\"type\":\"address\"}],\"name\":\"initELD\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"author\":\"Kiln\",\"kind\":\"dev\",\"methods\":{\"getWithdrawer(bytes32)\":{\"params\":{\"_publicKeyRoot\":\"Public key root to get the owner\"}},\"initELD(address)\":{\"params\":{\"_stakingContract\":\"Address of the Staking Contract\"}}},\"title\":\"Execution Layer Fee Recipient\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"constructor\":{\"notice\":\"Constructor method allowing us to prevent calls to initCLFR by setting the appropriate version\"},\"dispatch(bytes32)\":{\"notice\":\"Performs a withdrawal on this contract's balance\"},\"getStakingContract()\":{\"notice\":\"Retrieve the staking contract address\"},\"getWithdrawer(bytes32)\":{\"notice\":\"Retrieve the assigned withdrawer for the given public key root\"},\"initELD(address)\":{\"notice\":\"Initialize the contract by storing the staking contract and the public key in storage\"}},\"notice\":\"This contract can be used to receive fees from a validator and split them with a node operator\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/contracts/ExecutionLayerFeeDispatcher.sol\":\"ExecutionLayerFeeDispatcher\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"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\",\"keccak256\":\"0x6e103df70cd81c29aee072103eacab01a66c3dfdde2b22f08459c7f280295081\",\"license\":\"BUSL-1.1\"},\"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\",\"keccak256\":\"0x75efa5a697b32235188a62f730b7ab2a2fc5c422a93010aa0b18e93ea1bade45\",\"license\":\"MIT\"},\"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\",\"keccak256\":\"0xca5bfd023b6c479ab82320ebced9a560f9b8ffa61df27c02e83e7cb7fa23f569\",\"license\":\"MIT\"},\"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\",\"keccak256\":\"0x585a11c0ae6f9fda70d5d242ce02336bfd60fd75afed28495156307ba944f6cc\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x608060405234801561001057600080fd5b50604051610b3e380380610b3e83398101604081905261002f91610070565b610066817f2a83e6f793b1db0be720b1fadaabc888b1b7e5af00b045d6d837e073182510b161006c60201b6108921790919060201c565b50610089565b9055565b60006020828403121561008257600080fd5b5051919050565b610aa6806100986000396000f3fe6080604052600436106100435760003560e01c8063068a8c8d146100ac57806327de9016146100ce5780638e68dce414610117578063ce1206501461012c5761007a565b3661007a576040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3480156100b857600080fd5b506100cc6100c73660046108bb565b61013f565b005b3480156100da57600080fd5b506100ee6100e93660046108d8565b610202565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b34801561012357600080fd5b506100ee6102c6565b6100cc61013a3660046108d8565b6102f5565b60016101697f2a83e6f793b1db0be720b1fadaabc888b1b7e5af00b045d6d837e073182510b15490565b610174906001610920565b81146101ac576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6101d57f2a83e6f793b1db0be720b1fadaabc888b1b7e5af00b045d6d837e073182510b1829055565b6101fe7fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff0112511839055565b5050565b60008061022d7fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff01125115490565b6040517fa74008010000000000000000000000000000000000000000000000000000000081526004810185905290915073ffffffffffffffffffffffffffffffffffffffff82169063a740080190602401602060405180830381865afa15801561029b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102bf9190610938565b9392505050565b60006102f07fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff01125115490565b905090565b476000819003610331576040517fb6562b0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061035b7fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff01125115490565b6040517fa74008010000000000000000000000000000000000000000000000000000000081526004810185905290915060009073ffffffffffffffffffffffffffffffffffffffff83169063a740080190602401602060405180830381865afa1580156103cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f09190610938565b6040517f9adf91ee0000000000000000000000000000000000000000000000000000000081526004810186905290915060009073ffffffffffffffffffffffffffffffffffffffff841690639adf91ee90602401602060405180830381865afa158015610461573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104859190610938565b905060008373ffffffffffffffffffffffffffffffffffffffff16633b19e84a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104f89190610938565b905060006127108573ffffffffffffffffffffffffffffffffffffffff16631bcbfaba6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561054a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061056e9190610955565b610578908861096e565b61058291906109ab565b905060006127108673ffffffffffffffffffffffffffffffffffffffff1663286966086040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f89190610955565b610602908461096e565b61060c91906109ab565b905060008073ffffffffffffffffffffffffffffffffffffffff8716610632858b6109e6565b604051600081818185875af1925050503d806000811461066e576040519150601f19603f3d011682016040523d82523d6000602084013e610673565b606091505b5090925090508115156000036106c057806040517fce13343d0000000000000000000000000000000000000000000000000000000081526004016106b791906109fd565b60405180910390fd5b831561076c5773ffffffffffffffffffffffffffffffffffffffff85166106e784866109e6565b604051600081818185875af1925050503d8060008114610723576040519150601f19603f3d011682016040523d82523d6000602084013e610728565b606091505b50909250905081151560000361076c57806040517fbc98622d0000000000000000000000000000000000000000000000000000000081526004016106b791906109fd565b82156108115760405173ffffffffffffffffffffffffffffffffffffffff8716908490600081818185875af1925050503d80600081146107c8576040519150601f19603f3d011682016040523d82523d6000602084013e6107cd565b606091505b50909250905081151560000361081157806040517fe5ea83e70000000000000000000000000000000000000000000000000000000081526004016106b791906109fd565b73ffffffffffffffffffffffffffffffffffffffff8087169088167f570610cb78811f3a7f90d272791d0a5e71648fb7368280519fc5866e5184db1d8c610858888e6109e6565b87610863818b6109e6565b60408051948552602085019390935291830152606082015260800160405180910390a350505050505050505050565b9055565b73ffffffffffffffffffffffffffffffffffffffff811681146108b857600080fd5b50565b6000602082840312156108cd57600080fd5b81356102bf81610896565b6000602082840312156108ea57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115610933576109336108f1565b500190565b60006020828403121561094a57600080fd5b81516102bf81610896565b60006020828403121561096757600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156109a6576109a66108f1565b500290565b6000826109e1577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000828210156109f8576109f86108f1565b500390565b600060208083528351808285015260005b81811015610a2a57858101830151858201604001528201610a0e565b81811115610a3c576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201604001939250505056fea2646970667358221220cf1c9da271b4075e9074a57cf80bfb44a566a3883e3d23fa21455207ed622c6764736f6c634300080d0033", + "deployedBytecode": "0x6080604052600436106100435760003560e01c8063068a8c8d146100ac57806327de9016146100ce5780638e68dce414610117578063ce1206501461012c5761007a565b3661007a576040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3480156100b857600080fd5b506100cc6100c73660046108bb565b61013f565b005b3480156100da57600080fd5b506100ee6100e93660046108d8565b610202565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b34801561012357600080fd5b506100ee6102c6565b6100cc61013a3660046108d8565b6102f5565b60016101697f2a83e6f793b1db0be720b1fadaabc888b1b7e5af00b045d6d837e073182510b15490565b610174906001610920565b81146101ac576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6101d57f2a83e6f793b1db0be720b1fadaabc888b1b7e5af00b045d6d837e073182510b1829055565b6101fe7fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff0112511839055565b5050565b60008061022d7fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff01125115490565b6040517fa74008010000000000000000000000000000000000000000000000000000000081526004810185905290915073ffffffffffffffffffffffffffffffffffffffff82169063a740080190602401602060405180830381865afa15801561029b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102bf9190610938565b9392505050565b60006102f07fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff01125115490565b905090565b476000819003610331576040517fb6562b0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061035b7fda87ab80a3be8afba92a2e1fa9eebe6df07c3e4f871642fcbd8b843ff01125115490565b6040517fa74008010000000000000000000000000000000000000000000000000000000081526004810185905290915060009073ffffffffffffffffffffffffffffffffffffffff83169063a740080190602401602060405180830381865afa1580156103cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f09190610938565b6040517f9adf91ee0000000000000000000000000000000000000000000000000000000081526004810186905290915060009073ffffffffffffffffffffffffffffffffffffffff841690639adf91ee90602401602060405180830381865afa158015610461573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104859190610938565b905060008373ffffffffffffffffffffffffffffffffffffffff16633b19e84a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104f89190610938565b905060006127108573ffffffffffffffffffffffffffffffffffffffff16631bcbfaba6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561054a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061056e9190610955565b610578908861096e565b61058291906109ab565b905060006127108673ffffffffffffffffffffffffffffffffffffffff1663286966086040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f89190610955565b610602908461096e565b61060c91906109ab565b905060008073ffffffffffffffffffffffffffffffffffffffff8716610632858b6109e6565b604051600081818185875af1925050503d806000811461066e576040519150601f19603f3d011682016040523d82523d6000602084013e610673565b606091505b5090925090508115156000036106c057806040517fce13343d0000000000000000000000000000000000000000000000000000000081526004016106b791906109fd565b60405180910390fd5b831561076c5773ffffffffffffffffffffffffffffffffffffffff85166106e784866109e6565b604051600081818185875af1925050503d8060008114610723576040519150601f19603f3d011682016040523d82523d6000602084013e610728565b606091505b50909250905081151560000361076c57806040517fbc98622d0000000000000000000000000000000000000000000000000000000081526004016106b791906109fd565b82156108115760405173ffffffffffffffffffffffffffffffffffffffff8716908490600081818185875af1925050503d80600081146107c8576040519150601f19603f3d011682016040523d82523d6000602084013e6107cd565b606091505b50909250905081151560000361081157806040517fe5ea83e70000000000000000000000000000000000000000000000000000000081526004016106b791906109fd565b73ffffffffffffffffffffffffffffffffffffffff8087169088167f570610cb78811f3a7f90d272791d0a5e71648fb7368280519fc5866e5184db1d8c610858888e6109e6565b87610863818b6109e6565b60408051948552602085019390935291830152606082015260800160405180910390a350505050505050505050565b9055565b73ffffffffffffffffffffffffffffffffffffffff811681146108b857600080fd5b50565b6000602082840312156108cd57600080fd5b81356102bf81610896565b6000602082840312156108ea57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115610933576109336108f1565b500190565b60006020828403121561094a57600080fd5b81516102bf81610896565b60006020828403121561096757600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156109a6576109a66108f1565b500290565b6000826109e1577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000828210156109f8576109f86108f1565b500390565b600060208083528351808285015260005b81811015610a2a57858101830151858201604001528201610a0e565b81811115610a3c576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201604001939250505056fea2646970667358221220cf1c9da271b4075e9074a57cf80bfb44a566a3883e3d23fa21455207ed622c6764736f6c634300080d0033", + "devdoc": { + "author": "Kiln", + "kind": "dev", + "methods": { + "getWithdrawer(bytes32)": { + "params": { + "_publicKeyRoot": "Public key root to get the owner" + } + }, + "initELD(address)": { + "params": { + "_stakingContract": "Address of the Staking Contract" + } + } + }, + "title": "Execution Layer Fee Recipient", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "constructor": { + "notice": "Constructor method allowing us to prevent calls to initCLFR by setting the appropriate version" + }, + "dispatch(bytes32)": { + "notice": "Performs a withdrawal on this contract's balance" + }, + "getStakingContract()": { + "notice": "Retrieve the staking contract address" + }, + "getWithdrawer(bytes32)": { + "notice": "Retrieve the assigned withdrawer for the given public key root" + }, + "initELD(address)": { + "notice": "Initialize the contract by storing the staking contract and the public key in storage" + } + }, + "notice": "This contract can be used to receive fees from a validator and split them with a node operator", + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/deployments/mainnet_safe/FeeRecipient.json b/deployments/mainnet_safe/FeeRecipient.json new file mode 100644 index 0000000..9abb623 --- /dev/null +++ b/deployments/mainnet_safe/FeeRecipient.json @@ -0,0 +1,169 @@ +{ + "address": "0x223Ca2Fe219df1AaD87787a0de4b65F541f08542", + "abi": [ + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "getPublicKeyRoot", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getWithdrawer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_dispatcher", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "init", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0x68d624332ef23a67008a9f1c1a99eff937a9f9239c14e0da520d6083c74d271c", + "receipt": { + "to": null, + "from": "0x6C74FDa18Ea90E1C61e800Dd9B4508Ac782e0Dc8", + "contractAddress": "0x223Ca2Fe219df1AaD87787a0de4b65F541f08542", + "transactionIndex": 191, + "gasUsed": "233362", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x070b35e0181b7ca413d9e33432c87ff611e738dba7903460f33714506eec4bf2", + "transactionHash": "0x68d624332ef23a67008a9f1c1a99eff937a9f9239c14e0da520d6083c74d271c", + "logs": [], + "blockNumber": 20583409, + "cumulativeGasUsed": "13087249", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "e20c74109a1c61285ef1a2afbbbc70eb", + "metadata": "{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"AlreadyInitialized\",\"type\":\"error\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"getPublicKeyRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWithdrawer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_dispatcher\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"init\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"init(address,bytes32)\":{\"params\":{\"_dispatcher\":\"Address that will handle the fee dispatching\",\"_publicKeyRoot\":\"Public Key root assigned to this receiver\"}},\"withdraw()\":{\"details\":\"Can be called by any wallet as recipients are not parameters\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"getPublicKeyRoot()\":{\"notice\":\"Retrieve the assigned public key root\"},\"getWithdrawer()\":{\"notice\":\"retrieve the assigned withdrawer\"},\"init(address,bytes32)\":{\"notice\":\"Initializes the receiver\"},\"withdraw()\":{\"notice\":\"Triggers a withdrawal by sending its funds + its public key root to the dispatcher\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/contracts/FeeRecipient.sol\":\"FeeRecipient\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"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\",\"keccak256\":\"0xf788bf2e0e2112b0322904479f0704ac8fb23601f760326fec7f564ec0101090\",\"license\":\"BUSL-1.1\"},\"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\",\"keccak256\":\"0x75efa5a697b32235188a62f730b7ab2a2fc5c422a93010aa0b18e93ea1bade45\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x608060405234801561001057600080fd5b50610348806100206000396000f3fe6080604052600436106100405760003560e01c80632cc0b254146100495780633ccfd60b146100695780637d38d21f1461007e5780637f763702146100bd57005b3661004757005b005b34801561005557600080fd5b506100476100643660046102c2565b6100db565b34801561007557600080fd5b5061004761016c565b34801561008a57600080fd5b506100936101fc565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156100c957600080fd5b506001546040519081526020016100b4565b60005460ff1615610118576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff909316610100027fffffffffffffffffffffff0000000000000000000000000000000000000000009093169290921760019081179092559055565b6000546001546040517fce120650000000000000000000000000000000000000000000000000000000008152600481019190915261010090910473ffffffffffffffffffffffffffffffffffffffff169063ce1206509047906024016000604051808303818588803b1580156101e157600080fd5b505af11580156101f5573d6000803e3d6000fd5b5050505050565b600080546001546040517f27de9016000000000000000000000000000000000000000000000000000000008152600481019190915261010090910473ffffffffffffffffffffffffffffffffffffffff16906327de901690602401602060405180830381865afa158015610274573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061029891906102ee565b905090565b73ffffffffffffffffffffffffffffffffffffffff811681146102bf57600080fd5b50565b600080604083850312156102d557600080fd5b82356102e08161029d565b946020939093013593505050565b60006020828403121561030057600080fd5b815161030b8161029d565b939250505056fea2646970667358221220b126dc994bed0492fb480c2169ce4878eff8e5f378432ddaf44ae3ceeb35a64064736f6c634300080d0033", + "deployedBytecode": "0x6080604052600436106100405760003560e01c80632cc0b254146100495780633ccfd60b146100695780637d38d21f1461007e5780637f763702146100bd57005b3661004757005b005b34801561005557600080fd5b506100476100643660046102c2565b6100db565b34801561007557600080fd5b5061004761016c565b34801561008a57600080fd5b506100936101fc565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156100c957600080fd5b506001546040519081526020016100b4565b60005460ff1615610118576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff909316610100027fffffffffffffffffffffff0000000000000000000000000000000000000000009093169290921760019081179092559055565b6000546001546040517fce120650000000000000000000000000000000000000000000000000000000008152600481019190915261010090910473ffffffffffffffffffffffffffffffffffffffff169063ce1206509047906024016000604051808303818588803b1580156101e157600080fd5b505af11580156101f5573d6000803e3d6000fd5b5050505050565b600080546001546040517f27de9016000000000000000000000000000000000000000000000000000000008152600481019190915261010090910473ffffffffffffffffffffffffffffffffffffffff16906327de901690602401602060405180830381865afa158015610274573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061029891906102ee565b905090565b73ffffffffffffffffffffffffffffffffffffffff811681146102bf57600080fd5b50565b600080604083850312156102d557600080fd5b82356102e08161029d565b946020939093013593505050565b60006020828403121561030057600080fd5b815161030b8161029d565b939250505056fea2646970667358221220b126dc994bed0492fb480c2169ce4878eff8e5f378432ddaf44ae3ceeb35a64064736f6c634300080d0033", + "devdoc": { + "kind": "dev", + "methods": { + "init(address,bytes32)": { + "params": { + "_dispatcher": "Address that will handle the fee dispatching", + "_publicKeyRoot": "Public Key root assigned to this receiver" + } + }, + "withdraw()": { + "details": "Can be called by any wallet as recipients are not parameters" + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "getPublicKeyRoot()": { + "notice": "Retrieve the assigned public key root" + }, + "getWithdrawer()": { + "notice": "retrieve the assigned withdrawer" + }, + "init(address,bytes32)": { + "notice": "Initializes the receiver" + }, + "withdraw()": { + "notice": "Triggers a withdrawal by sending its funds + its public key root to the dispatcher" + } + }, + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 1850, + "contract": "src/contracts/FeeRecipient.sol:FeeRecipient", + "label": "initialized", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 1854, + "contract": "src/contracts/FeeRecipient.sol:FeeRecipient", + "label": "dispatcher", + "offset": 1, + "slot": "0", + "type": "t_contract(IFeeDispatcher)5045" + }, + { + "astId": 1857, + "contract": "src/contracts/FeeRecipient.sol:FeeRecipient", + "label": "publicKeyRoot", + "offset": 0, + "slot": "1", + "type": "t_bytes32" + } + ], + "types": { + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "encoding": "inplace", + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_contract(IFeeDispatcher)5045": { + "encoding": "inplace", + "label": "contract IFeeDispatcher", + "numberOfBytes": "20" + } + } + } +} \ No newline at end of file diff --git a/deployments/mainnet_safe/StakingContract.json b/deployments/mainnet_safe/StakingContract.json new file mode 100644 index 0000000..e764c2c --- /dev/null +++ b/deployments/mainnet_safe/StakingContract.json @@ -0,0 +1,1600 @@ +{ + "address": "0xC9db9bf0c9c8Fa30B09c45233E6A2D5eEfF9aBF3", + "abi": [ + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "Deactivated", + "type": "error" + }, + { + "inputs": [], + "name": "DepositFailure", + "type": "error" + }, + { + "inputs": [], + "name": "DepositsStopped", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "DuplicateValidatorKey", + "type": "error" + }, + { + "inputs": [], + "name": "Forbidden", + "type": "error" + }, + { + "inputs": [], + "name": "FundedValidatorDeletionAttempt", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidArgument", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCall", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDepositValue", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidFee", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPublicKeys", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSignatures", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidValidatorCount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidWithdrawer", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "LastEditAfterSnapshot", + "type": "error" + }, + { + "inputs": [], + "name": "MaximumOperatorCountAlreadyReached", + "type": "error" + }, + { + "inputs": [], + "name": "NoOperators", + "type": "error" + }, + { + "inputs": [], + "name": "NotEnoughValidators", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "keyCount", + "type": "uint256" + } + ], + "name": "OperatorLimitTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "PublicKeyNotInContract", + "type": "error" + }, + { + "inputs": [], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "UnsortedIndexes", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + } + ], + "name": "ActivatedOperator", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "ChangedAdmin", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "isStopped", + "type": "bool" + } + ], + "name": "ChangedDepositsStopped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "newGlobalFee", + "type": "uint256" + } + ], + "name": "ChangedGlobalFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "operatorIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "operatorAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "feeRecipientAddress", + "type": "address" + } + ], + "name": "ChangedOperatorAddresses", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "newOperatorFee", + "type": "uint256" + } + ], + "name": "ChangedOperatorFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "operatorIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "ChangedOperatorLimit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newTreasury", + "type": "address" + } + ], + "name": "ChangedTreasury", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "address", + "name": "newWithdrawer", + "type": "address" + } + ], + "name": "ChangedWithdrawer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + } + ], + "name": "DeactivatedOperator", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "withdrawer", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "pubkey", + "type": "bytes" + } + ], + "name": "ExitRequest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "operatorAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "feeRecipientAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "NewOperator", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "_status", + "type": "bool" + } + ], + "name": "SetWithdrawerCustomizationStatus", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "operatorIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + } + ], + "name": "ValidatorKeyRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "operatorIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "publicKeys", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "signatures", + "type": "bytes" + } + ], + "name": "ValidatorKeysAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "ValidatorsEdited", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "DEPOSIT_SIZE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PUBLIC_KEY_LENGTH", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SIGNATURE_LENGTH", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_newFeeRecipient", + "type": "address" + } + ], + "name": "activateOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_operatorAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_feeRecipientAddress", + "type": "address" + } + ], + "name": "addOperator", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_keyCount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_publicKeys", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "addValidators", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKeys", + "type": "bytes" + } + ], + "name": "batchWithdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKeys", + "type": "bytes" + } + ], + "name": "batchWithdrawCLFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKeys", + "type": "bytes" + } + ], + "name": "batchWithdrawELFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_temporaryFeeRecipient", + "type": "address" + } + ], + "name": "deactivateOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "deposit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAvailableValidatorCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKey", + "type": "bytes" + } + ], + "name": "getCLFeeRecipient", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getDepositsStopped", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKey", + "type": "bytes" + } + ], + "name": "getELFeeRecipient", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "getEnabledFromPublicKeyRoot", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "getExitRequestedFromRoot", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGlobalFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + } + ], + "name": "getOperator", + "outputs": [ + { + "internalType": "address", + "name": "operatorAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "feeRecipientAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "keys", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "funded", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "available", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "deactivated", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getOperatorFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "pubKeyRoot", + "type": "bytes32" + } + ], + "name": "getOperatorFeeRecipient", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPendingAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTreasury", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_validatorIndex", + "type": "uint256" + } + ], + "name": "getValidator", + "outputs": [ + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "address", + "name": "withdrawer", + "type": "address" + }, + { + "internalType": "bool", + "name": "funded", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKey", + "type": "bytes" + } + ], + "name": "getWithdrawer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "getWithdrawerFromPublicKeyRoot", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "getWithdrawnFromPublicKeyRoot", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_admin", + "type": "address" + }, + { + "internalType": "address", + "name": "_treasury", + "type": "address" + }, + { + "internalType": "address", + "name": "_depositContract", + "type": "address" + }, + { + "internalType": "address", + "name": "_elDispatcher", + "type": "address" + }, + { + "internalType": "address", + "name": "_clDispatcher", + "type": "address" + }, + { + "internalType": "address", + "name": "_feeRecipientImplementation", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_globalFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_operatorFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "globalCommissionLimitBPS", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "operatorCommissionLimitBPS", + "type": "uint256" + } + ], + "name": "initialize_1", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "globalCommissionLimitBPS", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "operatorCommissionLimitBPS", + "type": "uint256" + } + ], + "name": "initialize_2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "_indexes", + "type": "uint256[]" + } + ], + "name": "removeValidators", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKeys", + "type": "bytes" + } + ], + "name": "requestValidatorsExit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "val", + "type": "bool" + } + ], + "name": "setDepositsStopped", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_globalFee", + "type": "uint256" + } + ], + "name": "setGlobalFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_operatorAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_feeRecipientAddress", + "type": "address" + } + ], + "name": "setOperatorAddresses", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorFee", + "type": "uint256" + } + ], + "name": "setOperatorFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_operatorIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_limit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_snapshot", + "type": "uint256" + } + ], + "name": "setOperatorLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newTreasury", + "type": "address" + } + ], + "name": "setTreasury", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKey", + "type": "bytes" + }, + { + "internalType": "address", + "name": "_newWithdrawer", + "type": "address" + } + ], + "name": "setWithdrawer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_enabled", + "type": "bool" + } + ], + "name": "setWithdrawerCustomizationEnabled", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_publicKeyRoot", + "type": "bytes32" + } + ], + "name": "toggleWithdrawnFromPublicKeyRoot", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newAdmin", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKey", + "type": "bytes" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKey", + "type": "bytes" + } + ], + "name": "withdrawCLFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_publicKey", + "type": "bytes" + } + ], + "name": "withdrawELFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0x3a3027a00645d018f077551226ac72eed989c7a213a9d1b988177fe4cca0ecae", + "receipt": { + "to": null, + "from": "0x6C74FDa18Ea90E1C61e800Dd9B4508Ac782e0Dc8", + "contractAddress": "0xC9db9bf0c9c8Fa30B09c45233E6A2D5eEfF9aBF3", + "transactionIndex": 127, + "gasUsed": "5075909", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x686e7e99fc7045dbe3c1258f9003a2f7c0330cab7d03b617757d08d47d6709e3", + "transactionHash": "0x3a3027a00645d018f077551226ac72eed989c7a213a9d1b988177fe4cca0ecae", + "logs": [], + "blockNumber": 20583414, + "cumulativeGasUsed": "15825422", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "e20c74109a1c61285ef1a2afbbbc70eb", + "metadata": "{\"compiler\":{\"version\":\"0.8.13+commit.abaa5c0e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"AlreadyInitialized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Deactivated\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DepositFailure\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DepositsStopped\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"DuplicateValidatorKey\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Forbidden\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FundedValidatorDeletionAttempt\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidArgument\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDepositValue\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidFee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPublicKeys\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidValidatorCount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidWithdrawer\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidZeroAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LastEditAfterSnapshot\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaximumOperatorCountAlreadyReached\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoOperators\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotEnoughValidators\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"limit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"keyCount\",\"type\":\"uint256\"}],\"name\":\"OperatorLimitTooHigh\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PublicKeyNotInContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsortedIndexes\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"}],\"name\":\"ActivatedOperator\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"ChangedAdmin\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isStopped\",\"type\":\"bool\"}],\"name\":\"ChangedDepositsStopped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newGlobalFee\",\"type\":\"uint256\"}],\"name\":\"ChangedGlobalFee\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"operatorIndex\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"operatorAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeRecipientAddress\",\"type\":\"address\"}],\"name\":\"ChangedOperatorAddresses\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newOperatorFee\",\"type\":\"uint256\"}],\"name\":\"ChangedOperatorFee\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"operatorIndex\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"limit\",\"type\":\"uint256\"}],\"name\":\"ChangedOperatorLimit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newTreasury\",\"type\":\"address\"}],\"name\":\"ChangedTreasury\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"publicKey\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newWithdrawer\",\"type\":\"address\"}],\"name\":\"ChangedWithdrawer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"}],\"name\":\"DeactivatedOperator\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"withdrawer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"publicKey\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"pubkey\",\"type\":\"bytes\"}],\"name\":\"ExitRequest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"operatorAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeRecipientAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"NewOperator\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"_status\",\"type\":\"bool\"}],\"name\":\"SetWithdrawerCustomizationStatus\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"operatorIndex\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"publicKey\",\"type\":\"bytes\"}],\"name\":\"ValidatorKeyRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"operatorIndex\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"publicKeys\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"}],\"name\":\"ValidatorKeysAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"ValidatorsEdited\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"DEPOSIT_SIZE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PUBLIC_KEY_LENGTH\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SIGNATURE_LENGTH\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_newFeeRecipient\",\"type\":\"address\"}],\"name\":\"activateOperator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_operatorAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_feeRecipientAddress\",\"type\":\"address\"}],\"name\":\"addOperator\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_keyCount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"_publicKeys\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"_signatures\",\"type\":\"bytes\"}],\"name\":\"addValidators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKeys\",\"type\":\"bytes\"}],\"name\":\"batchWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKeys\",\"type\":\"bytes\"}],\"name\":\"batchWithdrawCLFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKeys\",\"type\":\"bytes\"}],\"name\":\"batchWithdrawELFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_temporaryFeeRecipient\",\"type\":\"address\"}],\"name\":\"deactivateOperator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAvailableValidatorCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKey\",\"type\":\"bytes\"}],\"name\":\"getCLFeeRecipient\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDepositsStopped\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKey\",\"type\":\"bytes\"}],\"name\":\"getELFeeRecipient\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"getEnabledFromPublicKeyRoot\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"getExitRequestedFromRoot\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getGlobalFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"}],\"name\":\"getOperator\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"operatorAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeRecipientAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"limit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"keys\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"funded\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"deactivated\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getOperatorFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"pubKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"getOperatorFeeRecipient\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPendingAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTreasury\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_validatorIndex\",\"type\":\"uint256\"}],\"name\":\"getValidator\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"publicKey\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"withdrawer\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"funded\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKey\",\"type\":\"bytes\"}],\"name\":\"getWithdrawer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"getWithdrawerFromPublicKeyRoot\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"getWithdrawnFromPublicKeyRoot\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_admin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_treasury\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_depositContract\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_elDispatcher\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_clDispatcher\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_feeRecipientImplementation\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_globalFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_operatorFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"globalCommissionLimitBPS\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"operatorCommissionLimitBPS\",\"type\":\"uint256\"}],\"name\":\"initialize_1\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"globalCommissionLimitBPS\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"operatorCommissionLimitBPS\",\"type\":\"uint256\"}],\"name\":\"initialize_2\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"_indexes\",\"type\":\"uint256[]\"}],\"name\":\"removeValidators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKeys\",\"type\":\"bytes\"}],\"name\":\"requestValidatorsExit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"val\",\"type\":\"bool\"}],\"name\":\"setDepositsStopped\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_globalFee\",\"type\":\"uint256\"}],\"name\":\"setGlobalFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_operatorAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_feeRecipientAddress\",\"type\":\"address\"}],\"name\":\"setOperatorAddresses\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorFee\",\"type\":\"uint256\"}],\"name\":\"setOperatorFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_operatorIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_limit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_snapshot\",\"type\":\"uint256\"}],\"name\":\"setOperatorLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newTreasury\",\"type\":\"address\"}],\"name\":\"setTreasury\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKey\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"_newWithdrawer\",\"type\":\"address\"}],\"name\":\"setWithdrawer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"_enabled\",\"type\":\"bool\"}],\"name\":\"setWithdrawerCustomizationEnabled\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_publicKeyRoot\",\"type\":\"bytes32\"}],\"name\":\"toggleWithdrawnFromPublicKeyRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newAdmin\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKey\",\"type\":\"bytes\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKey\",\"type\":\"bytes\"}],\"name\":\"withdrawCLFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_publicKey\",\"type\":\"bytes\"}],\"name\":\"withdrawELFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"author\":\"Kiln\",\"kind\":\"dev\",\"methods\":{\"acceptOwnership()\":{\"details\":\"Only callable by new admin\"},\"activateOperator(uint256,address)\":{\"params\":{\"_newFeeRecipient\":\"Sets the fee recipient address\",\"_operatorIndex\":\"Operator Index\"}},\"addOperator(address,address)\":{\"details\":\"Only callable by admin\",\"params\":{\"_feeRecipientAddress\":\"Privileged operator address used to manage rewards and operator addresses\",\"_operatorAddress\":\"Operator address allowed to add / remove validators\"}},\"addValidators(uint256,uint256,bytes,bytes)\":{\"details\":\"Only callable by operator\",\"params\":{\"_keyCount\":\"Number of keys added\",\"_operatorIndex\":\"Operator Index\",\"_publicKeys\":\"Concatenated _keyCount public keys\",\"_signatures\":\"Concatenated _keyCount signatures\"}},\"batchWithdraw(bytes)\":{\"details\":\"Funds are sent to the withdrawer account\",\"params\":{\"_publicKeys\":\"Validators to withdraw fees from\"}},\"batchWithdrawCLFee(bytes)\":{\"details\":\"Funds are sent to the withdrawer accountThis method is public on purpose\",\"params\":{\"_publicKeys\":\"Validators to withdraw Consensus Layer Fees from\"}},\"batchWithdrawELFee(bytes)\":{\"details\":\"Funds are sent to the withdrawer accountThis method is public on purpose\",\"params\":{\"_publicKeys\":\"Validators to withdraw Execution Layer Fees from\"}},\"deactivateOperator(uint256,address)\":{\"params\":{\"_operatorIndex\":\"Operator Index\",\"_temporaryFeeRecipient\":\"Temporary address to receive funds decided by the system admin\"}},\"deposit()\":{\"details\":\"A multiple of 32 ETH should be sent\"},\"getCLFeeRecipient(bytes)\":{\"params\":{\"_publicKey\":\"Validator to get the recipient\"}},\"getELFeeRecipient(bytes)\":{\"params\":{\"_publicKey\":\"Validator to get the recipient\"}},\"getEnabledFromPublicKeyRoot(bytes32)\":{\"params\":{\"_publicKeyRoot\":\"Hash of the public key\"}},\"getExitRequestedFromRoot(bytes32)\":{\"params\":{\"_publicKeyRoot\":\"Public Key Root to check\"}},\"getOperator(uint256)\":{\"params\":{\"_operatorIndex\":\"Operator index\"}},\"getValidator(uint256,uint256)\":{\"params\":{\"_operatorIndex\":\"Index of the operator running the validator\",\"_validatorIndex\":\"Index of the validator\"}},\"getWithdrawer(bytes)\":{\"params\":{\"_publicKey\":\"Public Key to check\"}},\"getWithdrawerFromPublicKeyRoot(bytes32)\":{\"params\":{\"_publicKeyRoot\":\"Hash of the public key\"}},\"getWithdrawnFromPublicKeyRoot(bytes32)\":{\"params\":{\"_publicKeyRoot\":\"Public Key Root of the validator\"}},\"removeValidators(uint256,uint256[])\":{\"details\":\"Only callable by operatorIndexes should be provided in decreasing orderThe limit will be set to the lowest removed operator index to ensure all changes above the lowest removed validator key are verified by the system administrator\",\"params\":{\"_indexes\":\"List of indexes to delete, in decreasing order\",\"_operatorIndex\":\"Operator Index\"}},\"setGlobalFee(uint256)\":{\"params\":{\"_globalFee\":\"Fee in Basis Point\"}},\"setOperatorAddresses(uint256,address,address)\":{\"details\":\"Only callable by fee recipient address manager\",\"params\":{\"_feeRecipientAddress\":\"New operator address for reward management\",\"_operatorAddress\":\"New operator address for operations management\",\"_operatorIndex\":\"Index of the operator to update\"}},\"setOperatorFee(uint256)\":{\"params\":{\"_operatorFee\":\"Fee in Basis Point\"}},\"setOperatorLimit(uint256,uint256,uint256)\":{\"details\":\"Only callable by adminLimit should not exceed the validator key count of the operatorKeys should be registered before limit is increasedAllows all keys to be verified by the system admin before limit is increased\",\"params\":{\"_limit\":\"New staking limit\",\"_operatorIndex\":\"Operator Index\",\"_snapshot\":\"Block number at which verification was done\"}},\"setTreasury(address)\":{\"details\":\"Only callable by admin\",\"params\":{\"_newTreasury\":\"New Treasury address\"}},\"setWithdrawer(bytes,address)\":{\"details\":\"Only callable by current public key withdrawer\",\"params\":{\"_newWithdrawer\":\"New withdrawer address\",\"_publicKey\":\"Public key to change withdrawer\"}},\"setWithdrawerCustomizationEnabled(bool)\":{\"params\":{\"_enabled\":\"True to allow users to customize the withdrawer\"}},\"toggleWithdrawnFromPublicKeyRoot(bytes32)\":{\"params\":{\"_publicKeyRoot\":\"Public Key Root of the validator\"}},\"transferOwnership(address)\":{\"details\":\"Only callable by admin\",\"params\":{\"_newAdmin\":\"New Administrator address\"}},\"withdraw(bytes)\":{\"details\":\"Reverts if any is null\",\"params\":{\"_publicKey\":\"Validator to withdraw Execution and Consensus Layer Fees from\"}},\"withdrawCLFee(bytes)\":{\"details\":\"Funds are sent to the withdrawer account\",\"params\":{\"_publicKey\":\"Validator to withdraw Consensus Layer Fees from\"}},\"withdrawELFee(bytes)\":{\"details\":\"Funds are sent to the withdrawer account\",\"params\":{\"_publicKey\":\"Validator to withdraw Execution Layer Fees from\"}}},\"title\":\"Ethereum Staking Contract\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"acceptOwnership()\":{\"notice\":\"New admin must accept its role by calling this method\"},\"activateOperator(uint256,address)\":{\"notice\":\"Activates an operator, without changing its 0 staking limit\"},\"addOperator(address,address)\":{\"notice\":\"Add new operator\"},\"addValidators(uint256,uint256,bytes,bytes)\":{\"notice\":\"Add new validator public keys and signatures\"},\"batchWithdraw(bytes)\":{\"notice\":\"Withdraw both Consensus and Execution Layer Fees for given validators public keys\"},\"batchWithdrawCLFee(bytes)\":{\"notice\":\"Withdraw the Consensus Layer Fee for given validators public keys\"},\"batchWithdrawELFee(bytes)\":{\"notice\":\"Withdraw the Execution Layer Fee for given validators public keys\"},\"deactivateOperator(uint256,address)\":{\"notice\":\"Deactivates an operator and changes the fee recipient address and the staking limit\"},\"deposit()\":{\"notice\":\"Explicit deposit method using msg.sender\"},\"getAdmin()\":{\"notice\":\"Retrieve system admin\"},\"getAvailableValidatorCount()\":{\"notice\":\"Get the total available keys that are ready to be used for deposits\"},\"getCLFeeRecipient(bytes)\":{\"notice\":\"Compute the Consensus Layer Fee recipient address for a given validator public key\"},\"getDepositsStopped()\":{\"notice\":\"Returns false if the users can deposit, true if deposits are stopped\"},\"getELFeeRecipient(bytes)\":{\"notice\":\"Compute the Execution Layer Fee recipient address for a given validator public key\"},\"getEnabledFromPublicKeyRoot(bytes32)\":{\"notice\":\"Retrieve the enabled status of public key root, true if the key is in the contract\"},\"getExitRequestedFromRoot(bytes32)\":{\"notice\":\"Retrieve whether the validator exit has been requestedIn case the validator is not enabled, it will return false\"},\"getGlobalFee()\":{\"notice\":\"Retrieve the global fee\"},\"getOperator(uint256)\":{\"notice\":\"Retrieve operator details\"},\"getOperatorFee()\":{\"notice\":\"Retrieve the operator fee\"},\"getOperatorFeeRecipient(bytes32)\":{\"notice\":\"Retrieve the Execution & Consensus Layer Fee operator recipient for a given public key\"},\"getPendingAdmin()\":{\"notice\":\"Get the new admin's address previously set for an ownership transfer\"},\"getTreasury()\":{\"notice\":\"Retrieve system treasury\"},\"getValidator(uint256,uint256)\":{\"notice\":\"Get details about a validator\"},\"getWithdrawer(bytes)\":{\"notice\":\"Retrieve withdrawer of public keyIn case the validator is not enabled, it will return address(0)\"},\"getWithdrawerFromPublicKeyRoot(bytes32)\":{\"notice\":\"Retrieve withdrawer of public key rootIn case the validator is not enabled, it will return address(0)\"},\"getWithdrawnFromPublicKeyRoot(bytes32)\":{\"notice\":\"Return true if the validator already went through the exit logicIn case the validator is not enabled, it will return false\"},\"removeValidators(uint256,uint256[])\":{\"notice\":\"Remove unfunded validators\"},\"setDepositsStopped(bool)\":{\"notice\":\"Utility to stop or allow deposits\"},\"setGlobalFee(uint256)\":{\"notice\":\"Change the Global fee\"},\"setOperatorAddresses(uint256,address,address)\":{\"notice\":\"Set new operator addresses (operations and reward management)\"},\"setOperatorFee(uint256)\":{\"notice\":\"Change the Operator fee\"},\"setOperatorLimit(uint256,uint256,uint256)\":{\"notice\":\"Set operator staking limits\"},\"setTreasury(address)\":{\"notice\":\"Set new treasury\"},\"setWithdrawer(bytes,address)\":{\"notice\":\"Set withdrawer for public key\"},\"setWithdrawerCustomizationEnabled(bool)\":{\"notice\":\"Changes the behavior of the withdrawer customization logic\"},\"toggleWithdrawnFromPublicKeyRoot(bytes32)\":{\"notice\":\"Allows the CLDispatcher to signal a validator went through the exit logic\"},\"transferOwnership(address)\":{\"notice\":\"Set new admin\"},\"withdraw(bytes)\":{\"notice\":\"Withdraw both Consensus and Execution Layer Fee for a given validator public key\"},\"withdrawCLFee(bytes)\":{\"notice\":\"Withdraw the Consensus Layer Fee for a given validator public key\"},\"withdrawELFee(bytes)\":{\"notice\":\"Withdraw the Execution Layer Fee for a given validator public key\"}},\"notice\":\"You can use this contract to store validator keys and have users fund them and trigger deposits.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/contracts/StakingContract.sol\":\"StakingContract\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"@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\",\"keccak256\":\"0x1cc0efb01cbf008b768fd7b334786a6e358809198bb7e67f1c530af4957c6a21\",\"license\":\"MIT\"},\"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 /// \\u2588\\u2588 \\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588\\n /// \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\n /// \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588\\n /// \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\n /// \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588 \\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\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\",\"keccak256\":\"0x48b93da8defc2f12c821b6490cce69bede2dacdd813fe205bbdbeb6107a6bd96\",\"license\":\"BUSL-1.1\"},\"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\",\"keccak256\":\"0x10ced526f2842c879ff63bf37a47d121d56898609456f98df1f3cff0a768b2c9\",\"license\":\"MIT\"},\"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\",\"keccak256\":\"0x2448a6378aa26099508ce00bf1eff7ea293ae97eece97d931a6f500256a2c475\",\"license\":\"MIT\"},\"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\",\"keccak256\":\"0xf98a3b0b668d8850c79a255e0567fd85bf586503b276e95aa604add0b1033dbc\",\"license\":\"MIT\"},\"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\",\"keccak256\":\"0xafac6beeeb9d8b004d643056f2a0d860c586759a2e4464b057066f4d6792b7e9\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x608060405234801561001057600080fd5b50615b2e80620000216000396000f3fe6080604052600436106103015760003560e01c8063a74008011161018f578063d0e30db0116100e1578063e99454f51161008a578063f2fde38b11610064578063f2fde38b146108b1578063fe0e4191146108d1578063fe38231c146108f157610310565b8063e99454f514610851578063ef5e468214610871578063f0f442601461089157610310565b8063dfbe50dd116100bb578063dfbe50dd146107f1578063e00cb6ca14610811578063e8a0c1211461083157610310565b8063d0e30db0146107a9578063d243d69d146107b1578063d2a42747146107d157610310565b8063beee4bbf11610143578063c13d03501161011d578063c13d035014610744578063cac594df14610774578063d04681561461079457610310565b8063beee4bbf146106ef578063bf15af561461070f578063bf509bd41461072457610310565b8063b6b06dec11610174578063b6b06dec1461068f578063b747e7dd146106af578063b86bcaf7146106cf57610310565b8063a74008011461065a578063b4336b841461067a57610310565b806336bf3325116102535780637680fdf5116101fc5780638df4e474116101d65780638df4e474146105fa5780639adf91ee1461061a578063a4d8d2c41461063a57610310565b80637680fdf5146105a557806379ba5097146105c55780638a1af4c4146105da57610310565b806363b4118f1161022d57806363b4118f146105405780636d336fe0146105705780636e9960c31461059057610310565b806336bf3325146104f95780633b19e84a14610516578063540bc5ea1461052b57610310565b80631d095805116102b5578063286966081161028f57806328696608146104a4578063291206f6146104b95780632ba03a79146104d957610310565b80631d0958051461042c5780631ee133431461044c578063227e80fa1461048457610310565b80630ffab6c2116102e65780630ffab6c2146103c95780631864636c146103e95780631bcbfaba1461040957610310565b806305f63c8a146103425780630968f264146103a957610310565b366103105761030e610906565b005b6040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34801561034e57600080fd5b5061036261035d366004615249565b610a62565b604080516001600160a01b039889168152979096166020880152948601939093526060850191909152608084015260a0830152151560c082015260e0015b60405180910390f35b3480156103b557600080fd5b5061030e6103c43660046152ab565b610b78565b3480156103d557600080fd5b5061030e6103e43660046152ab565b610c49565b3480156103f557600080fd5b5061030e6104043660046152ed565b610cfe565b34801561041557600080fd5b5061041e611c77565b6040519081526020016103a0565b34801561043857600080fd5b5061030e610447366004615249565b611c86565b34801561045857600080fd5b5061046c6104673660046152ab565b611d3f565b6040516001600160a01b0390911681526020016103a0565b34801561049057600080fd5b5061030e61049f366004615388565b611d8c565b3480156104b057600080fd5b5061041e611f15565b3480156104c557600080fd5b5061030e6104d4366004615249565b611f1f565b3480156104e557600080fd5b5061030e6104f43660046152ab565b611fd1565b34801561050557600080fd5b5061041e6801bc16d674ec80000081565b34801561052257600080fd5b5061046c612010565b34801561053757600080fd5b5061041e606081565b34801561054c57600080fd5b5061056061055b3660046153b4565b61201a565b6040516103a0949392919061542e565b34801561057c57600080fd5b5061030e61058b3660046153b4565b61229b565b34801561059c57600080fd5b5061046c61237b565b3480156105b157600080fd5b5061030e6105c0366004615475565b612385565b3480156105d157600080fd5b5061030e6123fe565b3480156105e657600080fd5b5061041e6105f536600461549e565b61247e565b34801561060657600080fd5b5061030e610615366004615475565b61267a565b34801561062657600080fd5b5061046c610635366004615249565b6126ed565b34801561064657600080fd5b5061030e6106553660046154c8565b6127e8565b34801561066657600080fd5b5061046c610675366004615249565b6129c4565b34801561068657600080fd5b5061041e612a00565b34801561069b57600080fd5b5061030e6106aa3660046152ab565b612a0a565b3480156106bb57600080fd5b5061030e6106ca366004615504565b612b52565b3480156106db57600080fd5b5061030e6106ea366004615249565b612f1d565b3480156106fb57600080fd5b5061030e61070a366004615587565b612f7d565b34801561071b57600080fd5b5061041e603081565b34801561073057600080fd5b5061030e61073f3660046152ab565b613190565b34801561075057600080fd5b5061076461075f366004615249565b613212565b60405190151581526020016103a0565b34801561078057600080fd5b5061076461078f366004615249565b61321d565b3480156107a057600080fd5b5061046c61323b565b61030e613245565b3480156107bd57600080fd5b5061046c6107cc3660046152ab565b61324f565b3480156107dd57600080fd5b5061030e6107ec366004615388565b613290565b3480156107fd57600080fd5b5061030e61080c3660046155b3565b61348c565b34801561081d57600080fd5b5061046c61082c3660046152ab565b6135ef565b34801561083d57600080fd5b5061030e61084c3660046152ab565b613633565b34801561085d57600080fd5b5061030e61086c366004615651565b6136f6565b34801561087d57600080fd5b5061030e61088c3660046152ab565b61385d565b34801561089d57600080fd5b5061030e6108ac36600461569c565b613912565b3480156108bd57600080fd5b5061030e6108cc36600461569c565b61398f565b3480156108dd57600080fd5b506107646108ec366004615249565b6139d0565b3480156108fd57600080fd5b506107646139f8565b61090e6139fe565b15610945576040517f753ed9db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34158061096357506109606801bc16d674ec800000346156e6565b15155b1561099a576040517f428243e200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006109a4613a32565b905060006109bb6801bc16d674ec80000034615729565b9050818111156109f7576040517fae575a8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054600003610a53576040517fddf9d24500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5d8284613a5c565b505050565b6000808080808080807fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054909150891015610b6c576040805180820182526000808252602080830182815260028e901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f082529382205460068e901b60c0161c63ffffffff8181168552911c1690925290506000826000018b81548110610b0e57610b0e61573d565b600091825260209182902060069091020180546001820154600283015460038401546005909401549487015196516001600160a01b039384169f50919092169c50909a5090985063ffffffff93841697509290921694505060ff1691505b50919395979092949650565b610bb782828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613a7992505050565b610bff82828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509250610bfa9150613aed9050565b613b17565b610c4582828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250610bfa9150613ca29050565b5050565b610c546030826156e6565b15610c8b576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d576000610cdc84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b9050610ce781613a79565b610cf5816001610bfa613ca2565b50603001610c8e565b82610d07613e28565b6001600160a01b0316330361152a576000829003610d51576040517fa9cb9e0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051808201825260008082526020808301828152600289901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f082529390912054600688901b60c0161c63ffffffff8181168452911c1691829052907fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b39907fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec93902908686610e0260018261576c565b818110610e1157610e1161573d565b905060200201351015610e50576040517f34947ea100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8581101561140d57600081118015610ea557508686610e7360018461576c565b818110610e8257610e8261573d565b90506020020135878783818110610e9b57610e9b61573d565b9050602002013510155b15610edc576040517f35061dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610fc8846000018a81548110610ef657610ef661573d565b9060005260206000209060060201600301898985818110610f1957610f1961573d565b9050602002013581548110610f3057610f3061573d565b906000526020600020018054610f4590615783565b80601f0160208091040260200160405190810160405280929190818152602001828054610f7190615783565b8015610fbe5780601f10610f9357610100808354040283529160200191610fbe565b820191906000526020600020905b815481529060010190602001808311610fa157829003601f168201915b5050505050613e52565b600081815260208590526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000169055845490915089907f794aacb42d1ea2e7f72809b74e3ce124325a51c3715b873c36807d3ca37e4fd0908690839081106110375761103761573d565b90600052602060002090600602016003018a8a8681811061105a5761105a61573d565b90506020020135815481106110715761107161573d565b9060005260206000200160405161108891906157d6565b60405180910390a26001846000018a815481106110a7576110a761573d565b9060005260206000209060060201600301805490506110c6919061576c565b8888848181106110d8576110d861573d565b905060200201350361118f578360000189815481106110f9576110f961573d565b906000526020600020906006020160030180548061111957611119615896565b6001900381819060005260206000200160006111359190615085565b905583600001898154811061114c5761114c61573d565b906000526020600020906006020160040180548061116c5761116c615896565b6001900381819060005260206000200160006111889190615085565b9055611404565b8360000189815481106111a4576111a461573d565b90600052602060002090600602016003016001856000018b815481106111cc576111cc61573d565b9060005260206000209060060201600301805490506111eb919061576c565b815481106111fb576111fb61573d565b90600052602060002001846000018a8154811061121a5761121a61573d565b906000526020600020906006020160030189898581811061123d5761123d61573d565b90506020020135815481106112545761125461573d565b9060005260206000200190805461126a90615783565b6112759291906150bf565b5083600001898154811061128b5761128b61573d565b90600052602060002090600602016003018054806112ab576112ab615896565b6001900381819060005260206000200160006112c79190615085565b90558360000189815481106112de576112de61573d565b90600052602060002090600602016004016001856000018b815481106113065761130661573d565b906000526020600020906006020160040180549050611325919061576c565b815481106113355761133561573d565b90600052602060002001846000018a815481106113545761135461573d565b90600052602060002090600602016004018989858181106113775761137761573d565b905060200201358154811061138e5761138e61573d565b906000526020600020019080546113a490615783565b6113af9291906150bf565b508360000189815481106113c5576113c561573d565b90600052602060002090600602016004018054806113e5576113e5615896565b6001900381819060005260206000200160006114019190615085565b90555b50600101610e53565b508160000187815481106114235761142361573d565b6000918252602090912060026006909202010154868661144460018261576c565b8181106114535761145361573d565b90506020020135101561151157858561146d60018261576c565b81811061147c5761147c61573d565b905060200201358260000188815481106114985761149861573d565b60009182526020909120600260069092020101557fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e038787876114db60018261576c565b8181106114ea576114ea61573d565b90506020020135604051611508929190918252602082015260400190565b60405180910390a15b611519613ec7565b61152287613f05565b505050611c71565b611533816140fe565b600082900361156e576040517fa9cb9e0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051808201825260008082526020808301828152600289901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f082529390912054600688901b60c0161c63ffffffff8181168452911c1691829052907fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b39907fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec9390290868661161f60018261576c565b81811061162e5761162e61573d565b90506020020135101561166d576040517f34947ea100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b85811015611b58576000811180156116c25750868661169060018461576c565b81811061169f5761169f61573d565b905060200201358787838181106116b8576116b861573d565b9050602002013510155b156116f9576040517f35061dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611713846000018a81548110610ef657610ef661573d565b600081815260208590526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000169055845490915089907f794aacb42d1ea2e7f72809b74e3ce124325a51c3715b873c36807d3ca37e4fd0908690839081106117825761178261573d565b90600052602060002090600602016003018a8a868181106117a5576117a561573d565b90506020020135815481106117bc576117bc61573d565b906000526020600020016040516117d391906157d6565b60405180910390a26001846000018a815481106117f2576117f261573d565b906000526020600020906006020160030180549050611811919061576c565b8888848181106118235761182361573d565b90506020020135036118da578360000189815481106118445761184461573d565b906000526020600020906006020160030180548061186457611864615896565b6001900381819060005260206000200160006118809190615085565b90558360000189815481106118975761189761573d565b90600052602060002090600602016004018054806118b7576118b7615896565b6001900381819060005260206000200160006118d39190615085565b9055611b4f565b8360000189815481106118ef576118ef61573d565b90600052602060002090600602016003016001856000018b815481106119175761191761573d565b906000526020600020906006020160030180549050611936919061576c565b815481106119465761194661573d565b90600052602060002001846000018a815481106119655761196561573d565b90600052602060002090600602016003018989858181106119885761198861573d565b905060200201358154811061199f5761199f61573d565b906000526020600020019080546119b590615783565b6119c09291906150bf565b508360000189815481106119d6576119d661573d565b90600052602060002090600602016003018054806119f6576119f6615896565b600190038181906000526020600020016000611a129190615085565b9055836000018981548110611a2957611a2961573d565b90600052602060002090600602016004016001856000018b81548110611a5157611a5161573d565b906000526020600020906006020160040180549050611a70919061576c565b81548110611a8057611a8061573d565b90600052602060002001846000018a81548110611a9f57611a9f61573d565b9060005260206000209060060201600401898985818110611ac257611ac261573d565b9050602002013581548110611ad957611ad961573d565b90600052602060002001908054611aef90615783565b611afa9291906150bf565b50836000018981548110611b1057611b1061573d565b9060005260206000209060060201600401805480611b3057611b30615896565b600190038181906000526020600020016000611b4c9190615085565b90555b50600101611670565b50816000018781548110611b6e57611b6e61573d565b60009182526020909120600260069092020101548686611b8f60018261576c565b818110611b9e57611b9e61573d565b905060200201351015611c5c578585611bb860018261576c565b818110611bc757611bc761573d565b90506020020135826000018881548110611be357611be361573d565b60009182526020909120600260069092020101557fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e03878787611c2660018261576c565b818110611c3557611c3561573d565b90506020020135604051611c53929190918252602082015260400190565b60405180910390a15b611c64613ec7565b611c6d87613f05565b5050505b50505050565b6000611c816141ae565b905090565b611c8e613e28565b6001600160a01b0316336001600160a01b031614611cbe576040516282b42960e81b815260040160405180910390fd5b611cc66141d8565b811115611cff576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d0881614208565b6040518181527fd894096cd1f7e89d9b748c7c2358cb699a790a05e97dcde228fe5949b4e80743906020015b60405180910390a150565b6000611d8383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250614231915050565b90505b92915050565b611d94613e28565b6001600160a01b0316336001600160a01b031614611dc4576040516282b42960e81b815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054600090829085908110611dfc57611dfc61573d565b600091825260209182902060069190910201600501805460ff1916921515929092179091556040518481527f8d0ba049731b0417f85546bc7503de241522c61e7464e87cceff56d3f2586dbc910160405180910390a181816000018481548110611e6857611e6861573d565b906000526020600020906006020160010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f97568892aa9e69db7c72b9f32e6d4fe3c0e53b9728a8c0a0f1c2a7eb47c48ad383826000018581548110611ed457611ed461573d565b600091825260209182902060069091020154604080519384526001600160a01b039182169284019290925285169082015260600160405180910390a1505050565b6000611c816142db565b611f27613e28565b6001600160a01b0316336001600160a01b031614611f57576040516282b42960e81b815260040160405180910390fd5b611f5f614305565b811115611f98576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611fa181614335565b6040518181527fecd6fc650620aa722cafc3c3e871fa813947eae6f856982d7510a8ff101a507890602001611d34565b610bff82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613a7992505050565b6000611c8161435e565b606080600080807fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3990508060000187815481106120595761205961573d565b9060005260206000209060060201600301868154811061207b5761207b61573d565b90600052602060002001805461209090615783565b80601f01602080910402602001604051908101604052809291908181526020018280546120bc90615783565b80156121095780601f106120de57610100808354040283529160200191612109565b820191906000526020600020905b8154815290600101906020018083116120ec57829003601f168201915b505050505094508060000187815481106121255761212561573d565b906000526020600020906006020160040186815481106121475761214761573d565b90600052602060002001805461215c90615783565b80601f016020809104026020016040519081016040528092919081815260200182805461218890615783565b80156121d55780601f106121aa576101008083540402835291602001916121d5565b820191906000526020600020905b8154815290600101906020018083116121b857829003601f168201915b505050505093506122226121e886613e52565b60009081527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260409020546001600160a01b031690565b6040805180820182526000808252602080830182815260028d901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f08252939091205460068c901b60c0161c63ffffffff8181168452911c169091529093506020015163ffffffff16861091505092959194509250565b60026122a5614388565b6122b09060016158c5565b81146122e8576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6122f1816143b2565b61271083111561232d576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612336836143db565b612710821115612372576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5d8261440e565b6000611c81613e28565b61238d613e28565b6001600160a01b0316336001600160a01b0316146123bd576040516282b42960e81b815260040160405180910390fd5b60405181151581527f8caf502b0bfa5915a541f14f8b0d091d4837c201b74aa2a7a7f52487a5fe8a819060200160405180910390a16123fb8161443c565b50565b600061240861446a565b9050336001600160a01b03821614612432576040516282b42960e81b815260040160405180910390fd5b61243b81614493565b61244560006144bc565b6040516001600160a01b03821681527ff29c1089a9594c030706593e345bd8f70a26125bfb7bf4c54e757e20f456fd1c90602001611d34565b6000612488613e28565b6001600160a01b0316336001600160a01b0316146124b8576040516282b42960e81b815260040160405180910390fd5b6040805160c0810182526000808252602082018190529181018290526060808201819052608082015260a08101919091527fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054909190600103612548576040517fa20c741300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03808616825284811660208084019182528454600181810187556000878152839020865160069093020180549286167fffffffffffffffffffffffff00000000000000000000000000000000000000009384161781559351908401805491909516911617909255604083015160028201556060830151805184936125da92600385019291019061514a565b50608082015180516125f691600484019160209091019061514a565b5060a091909101516005909101805460ff191691151591909117905581546000906126239060019061576c565b604080516001600160a01b03808a168252881660208201529081018290529091507f2b3c4db2c0f4f51da09c2510a63e1d90235e486e8f075a609103a5c7a07422179060600160405180910390a195945050505050565b612682613e28565b6001600160a01b0316336001600160a01b0316146126b2576040516282b42960e81b815260040160405180910390fd5b6126bb816144e4565b60405181151581527f96639403db35a7ef32b25543bec54f73278d8c5ca82883ef1385ddcb27afa7dc90602001611d34565b60008181527fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec93902602052604081205460ff1615158103612758576040517f9e44bfc500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281527fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec9390260205260409020547fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980549091610100900463ffffffff169081106127c5576127c561573d565b60009182526020909120600160069092020101546001600160a01b031692915050565b8260007fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980548390811061281e5761281e61573d565b60009182526020909120600690910201600581015490915060ff1615612870576040517f1142a68c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018101546001600160a01b0316331461289c576040516282b42960e81b815260040160405180910390fd5b6128a58461450d565b6128ae8361450d565b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b39805485908290889081106128e5576128e561573d565b906000526020600020906006020160000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550838160000187815481106129305761293061573d565b60009182526020918290206006919091020160010180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0393841617905560408051898152888416928101929092529186168183015290517f97568892aa9e69db7c72b9f32e6d4fe3c0e53b9728a8c0a0f1c2a7eb47c48ad3916060908290030190a1505050505050565b60008181527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260408120546001600160a01b0316611d86565b6000611c81613a32565b612a156030826156e6565b15612a4c576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d576000612a9d84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b90506000612aaa82613e52565b60008181527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260409020549091506001600160a01b0316338114612b03576040516282b42960e81b815260040160405180910390fd5b612b0e82600161454d565b7fcb773ebd49049a1f68200f0814f547dec2c54293ff2c6a6a4f4d98d6d9cb12298184604051612b3f9291906158dd565b60405180910390a1505050603001612a4f565b85612b5c816140fe565b85600003612b96576040517fa9cb9e0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612ba16030856156e6565b151580612bb8575085612bb5603086615729565b14155b15612bef576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612bfa6060836156e6565b151580612c11575085612c0e606084615729565b14155b15612c48576040517f274cf40100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b397fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec9390260005b88811015612ec2576000612ce589898080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612cde925060309150869050615907565b6030613ccc565b90506000612d3788888080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612d30925060609150879050615907565b6060613ccc565b9050846000018c81548110612d4e57612d4e61573d565b600091825260208083206003600690930201919091018054600181018255908352918190208451612d869391909101918501906151a3565b50846000018c81548110612d9c57612d9c61573d565b600091825260208083206004600690930201919091018054600181018255908352918190208351612dd49391909101918401906151a3565b506000612de083613e52565b60008181526020879052604090205490915060ff1615612e3757826040517f5a303adb000000000000000000000000000000000000000000000000000000008152600401612e2e9190615944565b60405180910390fd5b60405180604001604052806001151581526020018e63ffffffff1681525085600001600083815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548163ffffffff021916908363ffffffff160217905550905050836001019350505050612c8d565b50887fb82c87b84f76f39d5f61ed59b411352603805dda6080a2c44b4a86eab141ff1b88888888604051612ef99493929190615982565b60405180910390a2612f09613ec7565b612f1289613f05565b505050505050505050565b612f25613ca2565b6001600160a01b0316336001600160a01b031614612f55576040516282b42960e81b815260040160405180910390fd5b6001612f5f614576565b60009283526020526040909120805460ff1916911515919091179055565b612f85613e28565b6001600160a01b0316336001600160a01b031614612fb5576040516282b42960e81b815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054819085908110612fea57612fea61573d565b600091825260209091206005600690920201015460ff1615613038576040517f1142a68c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081600001858154811061304f5761304f61573d565b9060005260206000209060060201600301805490509050838110156130aa576040517f62106cb30000000000000000000000000000000000000000000000000000000081526004810185905260248101829052604401612e2e565b838260000186815481106130c0576130c061573d565b9060005260206000209060060201600201541080156130e55750826130e36145a4565b115b1561311c576040517f474c62e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b838260000186815481106131325761313261573d565b90600052602060002090600602016002018190555061315085613f05565b60408051868152602081018690527fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e03910160405180910390a15050505050565b6131cf82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613a7992505050565b610c4582828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509250610bfa9150613aed9050565b6000611d86826145d4565b6000613227614576565b600092835260205250604090205460ff1690565b6000611c8161446a565b61324d610906565b565b6000611d8383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509250614231915050565b613298613e28565b6001600160a01b0316336001600160a01b0316146132c8576040516282b42960e81b815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980546000908290859081106133005761330061573d565b9060005260206000209060060201600201819055507fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e03836000604051613350929190918252602082015260400190565b60405180910390a1600181600001848154811061336f5761336f61573d565b600091825260209182902060069190910201600501805460ff1916921515929092179091556040518481527f4c644bb0e171ba9e5cf08f5d66836528c3947c3512b34dd8e27da30e803527b3910160405180910390a1818160000184815481106133db576133db61573d565b906000526020600020906006020160010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f97568892aa9e69db7c72b9f32e6d4fe3c0e53b9728a8c0a0f1c2a7eb47c48ad3838260000185815481106134475761344761573d565b600091825260209182902060069091020154604080519384526001600160a01b039182169284019290925285169082015260600160405180910390a1610a5d83613f05565b6001613496614388565b6134a19060016158c5565b81146134d9576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6134e2816143b2565b6134eb8b61450d565b6134f48b614493565b6134fd8a61450d565b6135068a6145de565b612710851115613542576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61354b85614335565b612710841115613587576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61359084614208565b6135998861450d565b6135a288614607565b6135ab8761450d565b6135b487614630565b6135bd8961450d565b6135c689614659565b6135cf8661450d565b6135d886614682565b6135e2838361229b565b5050505050505050505050565b6000611d836121e884848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613e5292505050565b61363e6030826156e6565b15613675576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d5760006136c684848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b90506136d181613a79565b6136df816000610bfa613aed565b6136ed816001610bfa613ca2565b50603001613678565b6136fe6146ab565b613734576040517fee90c46800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61373d8161450d565b600061377e84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613e5292505050565b905060007f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e2656000838152602082905260409020549091506001600160a01b031633146137dc576040516282b42960e81b815260040160405180910390fd5b7f833a0d0704fbdc6a0f748ebec819c31dde0b532ca9e3e29f56a5c4e8f3cbc0c985858560405161380f939291906159b4565b60405180910390a160009182526020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03929092169190911790555050565b6138686030826156e6565b1561389f576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d5760006138f084848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b90506138fb81613a79565b613909816000610bfa613aed565b506030016138a2565b61391a613e28565b6001600160a01b0316336001600160a01b03161461394a576040516282b42960e81b815260040160405180910390fd5b6040516001600160a01b03821681527f63cc689e9d3377465b51fb094ea4ca5e0a1436b21f1ad30d707c696111c665009060200160405180910390a16123fb816145de565b613997613e28565b6001600160a01b0316336001600160a01b0316146139c7576040516282b42960e81b815260040160405180910390fd5b6123fb816144bc565b60007fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec93902613227565b6000611c815b6000611c81613a2e60017fb2e94356976bf59104fa32f6a0cf50947281732def9f18014aacae55ac60c5dd61576c565b5490565b6000611c817f559ad51499ae00ca2e9d9d95aab46737c8904ab7da276613fefda282b2c2ac065490565b613a6e613a69838361576c565b6146d5565b610c456000836146fe565b613a856121e882613e52565b6001600160a01b0316336001600160a01b031614158015613ab6575033613aaa613e28565b6001600160a01b031614155b156123fb576040517fca455fa500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611c817faa81344d5857c875349bc4a95d531a580c46bdd94c41b35b1e072e4d627079f85490565b6000613b2284613e52565b9050600060028483604051602001613b44929190918252602082015260400190565b60408051601f1981840301815290829052613b5e916159e1565b602060405180830381855afa158015613b7b573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190613b9e91906159fd565b90506000613baa614aa3565b90506000613bb88284614acd565b9050806001600160a01b03163b600003613c5357613bd68284614b49565b506040517f2cc0b2540000000000000000000000000000000000000000000000000000000081526001600160a01b03868116600483015260248201869052821690632cc0b25490604401600060405180830381600087803b158015613c3a57600080fd5b505af1158015613c4e573d6000803e3d6000fd5b505050505b806001600160a01b0316633ccfd60b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015613c8e57600080fd5b505af11580156135e2573d6000803e3d6000fd5b6000611c817fb6d5e19fdd6cde5f03ed4f17c2670deffa47975541fd270b0b17e803297d76085490565b606081613cda81601f6158c5565b1015613d42576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401612e2e565b613d4c82846158c5565b84511015613db6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401612e2e565b606082158015613dd55760405191506000825260208201604052613e1f565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015613e0e578051835260209283019201613df6565b5050858452601f01601f1916604052505b50949350505050565b6000611c817ffbeda9bc03875013b12a1ec161efb8e5bf7e58e3cec96a1ea9efd3e264d26e645490565b6000600282600060801b604051602001613e6d929190615a16565b60408051601f1981840301815290829052613e87916159e1565b602060405180830381855afa158015613ea4573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190611d8691906159fd565b613ed043614c1a565b6040514381527f55c5aabfc91da783d60f72c41f352ac7f9d507fcd884f41e27e6852305f948ce9060200160405180910390a1565b60408051808201825260008082526020808301828152600286901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f0825293822054600686901b60c0161c63ffffffff8181168552911c1690925280517fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054929350916000908190849087908110613fa257613fa261573d565b9060005260206000209060060201600201549050846020015163ffffffff16811161403157602080860151600288901c60009081527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f083526040902080549190921b67ffffffff0000000016600689901b60c01690811b67ffffffffffffffff90911b199091161790556140b6565b60208501516140469063ffffffff168261576c565b602080870151600289901c60009081527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f083526040902080549190921b67ffffffff000000001663ffffffff84161760068a901b60c01690811b67ffffffffffffffff90911b1990911617905591505b8163ffffffff168363ffffffff16146140f6576140f68263ffffffff168463ffffffff166140e2613a32565b6140ec919061576c565b613a6991906158c5565b505050505050565b60007fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054839081106141335761413361573d565b60009182526020909120600690910201600581015490915060ff1615614185576040517f1142a68c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80546001600160a01b03163314610c45576040516282b42960e81b815260040160405180910390fd5b6000611c817fb88142a0318e2b174876bceb4db9dc318011849c83fb8a8bb2997386d562324a5490565b6000611c81613a2e60017f05896f012c7ab472c495a6810b73fc6183d339203dd1c74344bbfd2e1f5038fe61576c565b6123fb7f41118591c19026bdc7a484e34f80a8e7e632600aff1c72460e9c7dfe94a2dda6829055565b60008061423d84613e52565b905060006002848360405160200161425f929190918252602082015260400190565b60408051601f1981840301815290829052614279916159e1565b602060405180830381855afa158015614296573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906142b991906159fd565b905060006142c5614aa3565b90506142d18183614acd565b9695505050505050565b6000611c817f41118591c19026bdc7a484e34f80a8e7e632600aff1c72460e9c7dfe94a2dda65490565b6000611c81613a2e60017f2ad86e81c1a15749eb0399a4f2566324ade0cc7d7f8592a3db8896a29004bf8161576c565b6123fb7fb88142a0318e2b174876bceb4db9dc318011849c83fb8a8bb2997386d562324a829055565b6000611c817f10c92bb459c0223bf996150f2fb702a8288fb8354a33d5b0212e8e6b8273f55e5490565b6000611c817fd5c553085b8382c47128ae7612257fd5dc3b4fc4d3a108925604d3c8700c025b5490565b6123fb7fd5c553085b8382c47128ae7612257fd5dc3b4fc4d3a108925604d3c8700c025b829055565b6123fb61440960017f2ad86e81c1a15749eb0399a4f2566324ade0cc7d7f8592a3db8896a29004bf8161576c565b829055565b6123fb61440960017f05896f012c7ab472c495a6810b73fc6183d339203dd1c74344bbfd2e1f5038fe61576c565b6123fb61440960017fb2e94356976bf59104fa32f6a0cf50947281732def9f18014aacae55ac60c5dd61576c565b6000611c817e595eca1f8b39945ff4c404827bfa5fd1e295ef3f7d59d120a8ce3bae4e37a05490565b6123fb7ffbeda9bc03875013b12a1ec161efb8e5bf7e58e3cec96a1ea9efd3e264d26e64829055565b6123fb7e595eca1f8b39945ff4c404827bfa5fd1e295ef3f7d59d120a8ce3bae4e37a0829055565b6123fb7fce3b5bd42ef5aae9d7b01cd806ce2a8b66f0a48f2e8b5a15f77dfbbdbbf5144f829055565b6001600160a01b0381166123fb576040517ff6b2911f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80614556614c48565b60009384526020526040909220805460ff19169215159290921790915550565b600080611d8660017fd9ed3684910fa17a5f2ad83fd15b2ccb0abf740b7041631a9fe7daffda0cb9ee61576c565b6000611c81613a2e60017f7d3541d2c0079305636180d42008f210385563932c50e421bd8654ed4a6017eb61576c565b6000613227614c48565b6123fb7f10c92bb459c0223bf996150f2fb702a8288fb8354a33d5b0212e8e6b8273f55e829055565b6123fb7faa81344d5857c875349bc4a95d531a580c46bdd94c41b35b1e072e4d627079f8829055565b6123fb7fb6d5e19fdd6cde5f03ed4f17c2670deffa47975541fd270b0b17e803297d7608829055565b6123fb7fbc8b9852d17d50256bb221fdf6ee12d78dd493d807e907f7d223c40d65abd6b9829055565b6123fb7fd1c64973da70267569571a091966834c1a36bdba47f2a112b6a95bf41fc9c24e829055565b6000611c817fce3b5bd42ef5aae9d7b01cd806ce2a8b66f0a48f2e8b5a15f77dfbbdbbf5144f5490565b6123fb7f559ad51499ae00ca2e9d9d95aab46737c8904ab7da276613fefda282b2c2ac06829055565b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980546000908290859081106147365761473661573d565b6000918252602080832060408051808201825285815280840186815260028b901c87527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f08552919095205460068a811b60c0169190911c63ffffffff8181168852941c90931690819052929091020192505b84826020015163ffffffff166147be91906158c5565b8110156149f75760008360030182815481106147dc576147dc61573d565b9060005260206000200180546147f190615783565b80601f016020809104026020016040519081016040528092919081815260200182805461481d90615783565b801561486a5780601f1061483f5761010080835404028352916020019161486a565b820191906000526020600020905b81548152906001019060200180831161484d57829003601f168201915b5050505050905060008460040183815481106148885761488861573d565b90600052602060002001805461489d90615783565b80601f01602080910402602001604051908101604052809291908181526020018280546148c990615783565b80156149165780601f106148eb57610100808354040283529160200191614916565b820191906000526020600020905b8154815290600101906020018083116148f957829003601f168201915b50505050509050600061492a836001614231565b9050600061493782614c76565b9050600061494485613e52565b905061495285828685614cab565b60008181527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260409081902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001633908117909155905181907fac1020908b5f7134d59c1580838eba6fc42dd8c28bae65bf345676bba1913f8e906149df9089908990615a5d565b60405180910390a385600101955050505050506147a8565b50614a9c8585836000015163ffffffff16614a12919061576c565b86846020015163ffffffff16614a2891906158c5565b600283901c60009081527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f060209081526040909120805467ffffffffffffffff60c060069790971b9690961695861b191663ffffffff9490941667ffffffff000000009390921b929092161790921b179055565b5050505050565b6000611c817fd1c64973da70267569571a091966834c1a36bdba47f2a112b6a95bf41fc9c24e5490565b6000611d838383306040517f3d602d80600a3d3981f3363d3d373d3d3d363d730000000000000000000000008152606093841b60148201527f5af43d82803e903d91602b57fd5bf3ff000000000000000000000000000000006028820152921b6038830152604c8201526037808220606c830152605591012090565b60006040517f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000081528360601b60148201527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006028820152826037826000f59150506001600160a01b038116611d86576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f455243313136373a2063726561746532206661696c65640000000000000000006044820152606401612e2e565b6123fb61440960017f7d3541d2c0079305636180d42008f210385563932c50e421bd8654ed4a6017eb61576c565b600080611d8660017f68ed1186fe6f93f91e23b08dcba07651b0153d3ab058aab96212b33aaaf3a89961576c565b6000611d867f01000000000000000000000000000000000000000000000000000000000000006001600160a01b0384166158c5565b6000600280614cbd8560006040613ccc565b604051614cca91906159e1565b602060405180830381855afa158015614ce7573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614d0a91906159fd565b6002614d22866040614d1d81606061576c565b613ccc565b604051614d359190600090602001615a8b565b60408051601f1981840301815290829052614d4f916159e1565b602060405180830381855afa158015614d6c573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614d8f91906159fd565b60408051602081019390935282015260600160408051601f1981840301815290829052614dbb916159e1565b602060405180830381855afa158015614dd8573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614dfb91906159fd565b905060006002808685604051602001614e1e929190918252602082015260400190565b60408051601f1981840301815290829052614e38916159e1565b602060405180830381855afa158015614e55573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614e7891906159fd565b604080517e40597307000000000000000000000000000000000000000000000000000000602082015290810185905260029060600160408051601f1981840301815290829052614ec7916159e1565b602060405180830381855afa158015614ee4573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614f0791906159fd565b60408051602081019390935282015260600160408051601f1981840301815290829052614f33916159e1565b602060405180830381855afa158015614f50573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614f7391906159fd565b90506000614f8a6801bc16d674ec8000004761576c565b9050614f9461505b565b6001600160a01b031663228951186801bc16d674ec8000008987604051602001614fc091815260200190565b60405160208183030381529060405289876040518663ffffffff1660e01b8152600401614ff09493929190615aad565b6000604051808303818588803b15801561500957600080fd5b505af115801561501d573d6000803e3d6000fd5b5050505050804714611c6d576040517f6596d2b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611c817fbc8b9852d17d50256bb221fdf6ee12d78dd493d807e907f7d223c40d65abd6b95490565b50805461509190615783565b6000825580601f106150a1575050565b601f0160209004906000526020600020908101906123fb9190615217565b8280546150cb90615783565b90600052602060002090601f0160209004810192826150ed576000855561513a565b82601f106150fe578054855561513a565b8280016001018555821561513a57600052602060002091601f016020900482015b8281111561513a57825482559160010191906001019061511f565b50615146929150615217565b5090565b828054828255906000526020600020908101928215615197579160200282015b8281111561519757825180516151879184916020909101906151a3565b509160200191906001019061516a565b5061514692915061522c565b8280546151af90615783565b90600052602060002090601f0160209004810192826151d1576000855561513a565b82601f106151ea57805160ff191683800117855561513a565b8280016001018555821561513a579182015b8281111561513a5782518255916020019190600101906151fc565b5b808211156151465760008155600101615218565b808211156151465760006152408282615085565b5060010161522c565b60006020828403121561525b57600080fd5b5035919050565b60008083601f84011261527457600080fd5b50813567ffffffffffffffff81111561528c57600080fd5b6020830191508360208285010111156152a457600080fd5b9250929050565b600080602083850312156152be57600080fd5b823567ffffffffffffffff8111156152d557600080fd5b6152e185828601615262565b90969095509350505050565b60008060006040848603121561530257600080fd5b83359250602084013567ffffffffffffffff8082111561532157600080fd5b818601915086601f83011261533557600080fd5b81358181111561534457600080fd5b8760208260051b850101111561535957600080fd5b6020830194508093505050509250925092565b80356001600160a01b038116811461538357600080fd5b919050565b6000806040838503121561539b57600080fd5b823591506153ab6020840161536c565b90509250929050565b600080604083850312156153c757600080fd5b50508035926020909101359150565b60005b838110156153f15781810151838201526020016153d9565b83811115611c715750506000910152565b6000815180845261541a8160208601602086016153d6565b601f01601f19169290920160200192915050565b6080815260006154416080830187615402565b82810360208401526154538187615402565b6001600160a01b03959095166040840152505090151560609091015292915050565b60006020828403121561548757600080fd5b8135801515811461549757600080fd5b9392505050565b600080604083850312156154b157600080fd5b6154ba8361536c565b91506153ab6020840161536c565b6000806000606084860312156154dd57600080fd5b833592506154ed6020850161536c565b91506154fb6040850161536c565b90509250925092565b6000806000806000806080878903121561551d57600080fd5b8635955060208701359450604087013567ffffffffffffffff8082111561554357600080fd5b61554f8a838b01615262565b9096509450606089013591508082111561556857600080fd5b5061557589828a01615262565b979a9699509497509295939492505050565b60008060006060848603121561559c57600080fd5b505081359360208301359350604090920135919050565b6000806000806000806000806000806101408b8d0312156155d357600080fd5b6155dc8b61536c565b99506155ea60208c0161536c565b98506155f860408c0161536c565b975061560660608c0161536c565b965061561460808c0161536c565b955061562260a08c0161536c565b945060c08b0135935060e08b013592506101008b013591506101208b013590509295989b9194979a5092959850565b60008060006040848603121561566657600080fd5b833567ffffffffffffffff81111561567d57600080fd5b61568986828701615262565b90945092506154fb90506020850161536c565b6000602082840312156156ae57600080fd5b611d838261536c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826156f5576156f56156b7565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082615738576157386156b7565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008282101561577e5761577e6156fa565b500390565b600181811c9082168061579757607f821691505b6020821081036157d0577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600060208083526000845481600182811c9150808316806157f857607f831692505b858310810361582e577f4e487b710000000000000000000000000000000000000000000000000000000085526022600452602485fd5b87860183815260200181801561584b576001811461585c57615887565b60ff19861682528782019650615887565b60008b81526020902060005b8681101561588157815484820152908501908901615868565b83019750505b50949998505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600082198211156158d8576158d86156fa565b500190565b6001600160a01b03831681526040602082015260006158ff6040830184615402565b949350505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561593f5761593f6156fa565b500290565b602081526000611d836020830184615402565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b604081526000615996604083018688615957565b82810360208401526159a9818587615957565b979650505050505050565b6040815260006159c8604083018587615957565b90506001600160a01b0383166020830152949350505050565b600082516159f38184602087016153d6565b9190910192915050565b600060208284031215615a0f57600080fd5b5051919050565b60008351615a288184602088016153d6565b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000939093169190920190815260100192915050565b604081526000615a706040830185615402565b8281036020840152615a828185615402565b95945050505050565b60008351615a9d8184602088016153d6565b9190910191825250602001919050565b608081526000615ac06080830187615402565b8281036020840152615ad28187615402565b90508281036040840152615ae68186615402565b9150508260608301529594505050505056fea264697066735822122082f275dcc95ed77ce7470cc6de8ad96480198f43db9aa8fe8aa8314355ecbd8364736f6c634300080d0033", + "deployedBytecode": "0x6080604052600436106103015760003560e01c8063a74008011161018f578063d0e30db0116100e1578063e99454f51161008a578063f2fde38b11610064578063f2fde38b146108b1578063fe0e4191146108d1578063fe38231c146108f157610310565b8063e99454f514610851578063ef5e468214610871578063f0f442601461089157610310565b8063dfbe50dd116100bb578063dfbe50dd146107f1578063e00cb6ca14610811578063e8a0c1211461083157610310565b8063d0e30db0146107a9578063d243d69d146107b1578063d2a42747146107d157610310565b8063beee4bbf11610143578063c13d03501161011d578063c13d035014610744578063cac594df14610774578063d04681561461079457610310565b8063beee4bbf146106ef578063bf15af561461070f578063bf509bd41461072457610310565b8063b6b06dec11610174578063b6b06dec1461068f578063b747e7dd146106af578063b86bcaf7146106cf57610310565b8063a74008011461065a578063b4336b841461067a57610310565b806336bf3325116102535780637680fdf5116101fc5780638df4e474116101d65780638df4e474146105fa5780639adf91ee1461061a578063a4d8d2c41461063a57610310565b80637680fdf5146105a557806379ba5097146105c55780638a1af4c4146105da57610310565b806363b4118f1161022d57806363b4118f146105405780636d336fe0146105705780636e9960c31461059057610310565b806336bf3325146104f95780633b19e84a14610516578063540bc5ea1461052b57610310565b80631d095805116102b5578063286966081161028f57806328696608146104a4578063291206f6146104b95780632ba03a79146104d957610310565b80631d0958051461042c5780631ee133431461044c578063227e80fa1461048457610310565b80630ffab6c2116102e65780630ffab6c2146103c95780631864636c146103e95780631bcbfaba1461040957610310565b806305f63c8a146103425780630968f264146103a957610310565b366103105761030e610906565b005b6040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34801561034e57600080fd5b5061036261035d366004615249565b610a62565b604080516001600160a01b039889168152979096166020880152948601939093526060850191909152608084015260a0830152151560c082015260e0015b60405180910390f35b3480156103b557600080fd5b5061030e6103c43660046152ab565b610b78565b3480156103d557600080fd5b5061030e6103e43660046152ab565b610c49565b3480156103f557600080fd5b5061030e6104043660046152ed565b610cfe565b34801561041557600080fd5b5061041e611c77565b6040519081526020016103a0565b34801561043857600080fd5b5061030e610447366004615249565b611c86565b34801561045857600080fd5b5061046c6104673660046152ab565b611d3f565b6040516001600160a01b0390911681526020016103a0565b34801561049057600080fd5b5061030e61049f366004615388565b611d8c565b3480156104b057600080fd5b5061041e611f15565b3480156104c557600080fd5b5061030e6104d4366004615249565b611f1f565b3480156104e557600080fd5b5061030e6104f43660046152ab565b611fd1565b34801561050557600080fd5b5061041e6801bc16d674ec80000081565b34801561052257600080fd5b5061046c612010565b34801561053757600080fd5b5061041e606081565b34801561054c57600080fd5b5061056061055b3660046153b4565b61201a565b6040516103a0949392919061542e565b34801561057c57600080fd5b5061030e61058b3660046153b4565b61229b565b34801561059c57600080fd5b5061046c61237b565b3480156105b157600080fd5b5061030e6105c0366004615475565b612385565b3480156105d157600080fd5b5061030e6123fe565b3480156105e657600080fd5b5061041e6105f536600461549e565b61247e565b34801561060657600080fd5b5061030e610615366004615475565b61267a565b34801561062657600080fd5b5061046c610635366004615249565b6126ed565b34801561064657600080fd5b5061030e6106553660046154c8565b6127e8565b34801561066657600080fd5b5061046c610675366004615249565b6129c4565b34801561068657600080fd5b5061041e612a00565b34801561069b57600080fd5b5061030e6106aa3660046152ab565b612a0a565b3480156106bb57600080fd5b5061030e6106ca366004615504565b612b52565b3480156106db57600080fd5b5061030e6106ea366004615249565b612f1d565b3480156106fb57600080fd5b5061030e61070a366004615587565b612f7d565b34801561071b57600080fd5b5061041e603081565b34801561073057600080fd5b5061030e61073f3660046152ab565b613190565b34801561075057600080fd5b5061076461075f366004615249565b613212565b60405190151581526020016103a0565b34801561078057600080fd5b5061076461078f366004615249565b61321d565b3480156107a057600080fd5b5061046c61323b565b61030e613245565b3480156107bd57600080fd5b5061046c6107cc3660046152ab565b61324f565b3480156107dd57600080fd5b5061030e6107ec366004615388565b613290565b3480156107fd57600080fd5b5061030e61080c3660046155b3565b61348c565b34801561081d57600080fd5b5061046c61082c3660046152ab565b6135ef565b34801561083d57600080fd5b5061030e61084c3660046152ab565b613633565b34801561085d57600080fd5b5061030e61086c366004615651565b6136f6565b34801561087d57600080fd5b5061030e61088c3660046152ab565b61385d565b34801561089d57600080fd5b5061030e6108ac36600461569c565b613912565b3480156108bd57600080fd5b5061030e6108cc36600461569c565b61398f565b3480156108dd57600080fd5b506107646108ec366004615249565b6139d0565b3480156108fd57600080fd5b506107646139f8565b61090e6139fe565b15610945576040517f753ed9db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34158061096357506109606801bc16d674ec800000346156e6565b15155b1561099a576040517f428243e200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006109a4613a32565b905060006109bb6801bc16d674ec80000034615729565b9050818111156109f7576040517fae575a8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054600003610a53576040517fddf9d24500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5d8284613a5c565b505050565b6000808080808080807fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054909150891015610b6c576040805180820182526000808252602080830182815260028e901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f082529382205460068e901b60c0161c63ffffffff8181168552911c1690925290506000826000018b81548110610b0e57610b0e61573d565b600091825260209182902060069091020180546001820154600283015460038401546005909401549487015196516001600160a01b039384169f50919092169c50909a5090985063ffffffff93841697509290921694505060ff1691505b50919395979092949650565b610bb782828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613a7992505050565b610bff82828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509250610bfa9150613aed9050565b613b17565b610c4582828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250610bfa9150613ca29050565b5050565b610c546030826156e6565b15610c8b576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d576000610cdc84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b9050610ce781613a79565b610cf5816001610bfa613ca2565b50603001610c8e565b82610d07613e28565b6001600160a01b0316330361152a576000829003610d51576040517fa9cb9e0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051808201825260008082526020808301828152600289901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f082529390912054600688901b60c0161c63ffffffff8181168452911c1691829052907fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b39907fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec93902908686610e0260018261576c565b818110610e1157610e1161573d565b905060200201351015610e50576040517f34947ea100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8581101561140d57600081118015610ea557508686610e7360018461576c565b818110610e8257610e8261573d565b90506020020135878783818110610e9b57610e9b61573d565b9050602002013510155b15610edc576040517f35061dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610fc8846000018a81548110610ef657610ef661573d565b9060005260206000209060060201600301898985818110610f1957610f1961573d565b9050602002013581548110610f3057610f3061573d565b906000526020600020018054610f4590615783565b80601f0160208091040260200160405190810160405280929190818152602001828054610f7190615783565b8015610fbe5780601f10610f9357610100808354040283529160200191610fbe565b820191906000526020600020905b815481529060010190602001808311610fa157829003601f168201915b5050505050613e52565b600081815260208590526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000169055845490915089907f794aacb42d1ea2e7f72809b74e3ce124325a51c3715b873c36807d3ca37e4fd0908690839081106110375761103761573d565b90600052602060002090600602016003018a8a8681811061105a5761105a61573d565b90506020020135815481106110715761107161573d565b9060005260206000200160405161108891906157d6565b60405180910390a26001846000018a815481106110a7576110a761573d565b9060005260206000209060060201600301805490506110c6919061576c565b8888848181106110d8576110d861573d565b905060200201350361118f578360000189815481106110f9576110f961573d565b906000526020600020906006020160030180548061111957611119615896565b6001900381819060005260206000200160006111359190615085565b905583600001898154811061114c5761114c61573d565b906000526020600020906006020160040180548061116c5761116c615896565b6001900381819060005260206000200160006111889190615085565b9055611404565b8360000189815481106111a4576111a461573d565b90600052602060002090600602016003016001856000018b815481106111cc576111cc61573d565b9060005260206000209060060201600301805490506111eb919061576c565b815481106111fb576111fb61573d565b90600052602060002001846000018a8154811061121a5761121a61573d565b906000526020600020906006020160030189898581811061123d5761123d61573d565b90506020020135815481106112545761125461573d565b9060005260206000200190805461126a90615783565b6112759291906150bf565b5083600001898154811061128b5761128b61573d565b90600052602060002090600602016003018054806112ab576112ab615896565b6001900381819060005260206000200160006112c79190615085565b90558360000189815481106112de576112de61573d565b90600052602060002090600602016004016001856000018b815481106113065761130661573d565b906000526020600020906006020160040180549050611325919061576c565b815481106113355761133561573d565b90600052602060002001846000018a815481106113545761135461573d565b90600052602060002090600602016004018989858181106113775761137761573d565b905060200201358154811061138e5761138e61573d565b906000526020600020019080546113a490615783565b6113af9291906150bf565b508360000189815481106113c5576113c561573d565b90600052602060002090600602016004018054806113e5576113e5615896565b6001900381819060005260206000200160006114019190615085565b90555b50600101610e53565b508160000187815481106114235761142361573d565b6000918252602090912060026006909202010154868661144460018261576c565b8181106114535761145361573d565b90506020020135101561151157858561146d60018261576c565b81811061147c5761147c61573d565b905060200201358260000188815481106114985761149861573d565b60009182526020909120600260069092020101557fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e038787876114db60018261576c565b8181106114ea576114ea61573d565b90506020020135604051611508929190918252602082015260400190565b60405180910390a15b611519613ec7565b61152287613f05565b505050611c71565b611533816140fe565b600082900361156e576040517fa9cb9e0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051808201825260008082526020808301828152600289901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f082529390912054600688901b60c0161c63ffffffff8181168452911c1691829052907fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b39907fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec9390290868661161f60018261576c565b81811061162e5761162e61573d565b90506020020135101561166d576040517f34947ea100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b85811015611b58576000811180156116c25750868661169060018461576c565b81811061169f5761169f61573d565b905060200201358787838181106116b8576116b861573d565b9050602002013510155b156116f9576040517f35061dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611713846000018a81548110610ef657610ef661573d565b600081815260208590526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000169055845490915089907f794aacb42d1ea2e7f72809b74e3ce124325a51c3715b873c36807d3ca37e4fd0908690839081106117825761178261573d565b90600052602060002090600602016003018a8a868181106117a5576117a561573d565b90506020020135815481106117bc576117bc61573d565b906000526020600020016040516117d391906157d6565b60405180910390a26001846000018a815481106117f2576117f261573d565b906000526020600020906006020160030180549050611811919061576c565b8888848181106118235761182361573d565b90506020020135036118da578360000189815481106118445761184461573d565b906000526020600020906006020160030180548061186457611864615896565b6001900381819060005260206000200160006118809190615085565b90558360000189815481106118975761189761573d565b90600052602060002090600602016004018054806118b7576118b7615896565b6001900381819060005260206000200160006118d39190615085565b9055611b4f565b8360000189815481106118ef576118ef61573d565b90600052602060002090600602016003016001856000018b815481106119175761191761573d565b906000526020600020906006020160030180549050611936919061576c565b815481106119465761194661573d565b90600052602060002001846000018a815481106119655761196561573d565b90600052602060002090600602016003018989858181106119885761198861573d565b905060200201358154811061199f5761199f61573d565b906000526020600020019080546119b590615783565b6119c09291906150bf565b508360000189815481106119d6576119d661573d565b90600052602060002090600602016003018054806119f6576119f6615896565b600190038181906000526020600020016000611a129190615085565b9055836000018981548110611a2957611a2961573d565b90600052602060002090600602016004016001856000018b81548110611a5157611a5161573d565b906000526020600020906006020160040180549050611a70919061576c565b81548110611a8057611a8061573d565b90600052602060002001846000018a81548110611a9f57611a9f61573d565b9060005260206000209060060201600401898985818110611ac257611ac261573d565b9050602002013581548110611ad957611ad961573d565b90600052602060002001908054611aef90615783565b611afa9291906150bf565b50836000018981548110611b1057611b1061573d565b9060005260206000209060060201600401805480611b3057611b30615896565b600190038181906000526020600020016000611b4c9190615085565b90555b50600101611670565b50816000018781548110611b6e57611b6e61573d565b60009182526020909120600260069092020101548686611b8f60018261576c565b818110611b9e57611b9e61573d565b905060200201351015611c5c578585611bb860018261576c565b818110611bc757611bc761573d565b90506020020135826000018881548110611be357611be361573d565b60009182526020909120600260069092020101557fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e03878787611c2660018261576c565b818110611c3557611c3561573d565b90506020020135604051611c53929190918252602082015260400190565b60405180910390a15b611c64613ec7565b611c6d87613f05565b5050505b50505050565b6000611c816141ae565b905090565b611c8e613e28565b6001600160a01b0316336001600160a01b031614611cbe576040516282b42960e81b815260040160405180910390fd5b611cc66141d8565b811115611cff576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d0881614208565b6040518181527fd894096cd1f7e89d9b748c7c2358cb699a790a05e97dcde228fe5949b4e80743906020015b60405180910390a150565b6000611d8383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250614231915050565b90505b92915050565b611d94613e28565b6001600160a01b0316336001600160a01b031614611dc4576040516282b42960e81b815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054600090829085908110611dfc57611dfc61573d565b600091825260209182902060069190910201600501805460ff1916921515929092179091556040518481527f8d0ba049731b0417f85546bc7503de241522c61e7464e87cceff56d3f2586dbc910160405180910390a181816000018481548110611e6857611e6861573d565b906000526020600020906006020160010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f97568892aa9e69db7c72b9f32e6d4fe3c0e53b9728a8c0a0f1c2a7eb47c48ad383826000018581548110611ed457611ed461573d565b600091825260209182902060069091020154604080519384526001600160a01b039182169284019290925285169082015260600160405180910390a1505050565b6000611c816142db565b611f27613e28565b6001600160a01b0316336001600160a01b031614611f57576040516282b42960e81b815260040160405180910390fd5b611f5f614305565b811115611f98576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611fa181614335565b6040518181527fecd6fc650620aa722cafc3c3e871fa813947eae6f856982d7510a8ff101a507890602001611d34565b610bff82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613a7992505050565b6000611c8161435e565b606080600080807fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3990508060000187815481106120595761205961573d565b9060005260206000209060060201600301868154811061207b5761207b61573d565b90600052602060002001805461209090615783565b80601f01602080910402602001604051908101604052809291908181526020018280546120bc90615783565b80156121095780601f106120de57610100808354040283529160200191612109565b820191906000526020600020905b8154815290600101906020018083116120ec57829003601f168201915b505050505094508060000187815481106121255761212561573d565b906000526020600020906006020160040186815481106121475761214761573d565b90600052602060002001805461215c90615783565b80601f016020809104026020016040519081016040528092919081815260200182805461218890615783565b80156121d55780601f106121aa576101008083540402835291602001916121d5565b820191906000526020600020905b8154815290600101906020018083116121b857829003601f168201915b505050505093506122226121e886613e52565b60009081527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260409020546001600160a01b031690565b6040805180820182526000808252602080830182815260028d901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f08252939091205460068c901b60c0161c63ffffffff8181168452911c169091529093506020015163ffffffff16861091505092959194509250565b60026122a5614388565b6122b09060016158c5565b81146122e8576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6122f1816143b2565b61271083111561232d576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612336836143db565b612710821115612372576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5d8261440e565b6000611c81613e28565b61238d613e28565b6001600160a01b0316336001600160a01b0316146123bd576040516282b42960e81b815260040160405180910390fd5b60405181151581527f8caf502b0bfa5915a541f14f8b0d091d4837c201b74aa2a7a7f52487a5fe8a819060200160405180910390a16123fb8161443c565b50565b600061240861446a565b9050336001600160a01b03821614612432576040516282b42960e81b815260040160405180910390fd5b61243b81614493565b61244560006144bc565b6040516001600160a01b03821681527ff29c1089a9594c030706593e345bd8f70a26125bfb7bf4c54e757e20f456fd1c90602001611d34565b6000612488613e28565b6001600160a01b0316336001600160a01b0316146124b8576040516282b42960e81b815260040160405180910390fd5b6040805160c0810182526000808252602082018190529181018290526060808201819052608082015260a08101919091527fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054909190600103612548576040517fa20c741300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03808616825284811660208084019182528454600181810187556000878152839020865160069093020180549286167fffffffffffffffffffffffff00000000000000000000000000000000000000009384161781559351908401805491909516911617909255604083015160028201556060830151805184936125da92600385019291019061514a565b50608082015180516125f691600484019160209091019061514a565b5060a091909101516005909101805460ff191691151591909117905581546000906126239060019061576c565b604080516001600160a01b03808a168252881660208201529081018290529091507f2b3c4db2c0f4f51da09c2510a63e1d90235e486e8f075a609103a5c7a07422179060600160405180910390a195945050505050565b612682613e28565b6001600160a01b0316336001600160a01b0316146126b2576040516282b42960e81b815260040160405180910390fd5b6126bb816144e4565b60405181151581527f96639403db35a7ef32b25543bec54f73278d8c5ca82883ef1385ddcb27afa7dc90602001611d34565b60008181527fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec93902602052604081205460ff1615158103612758576040517f9e44bfc500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281527fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec9390260205260409020547fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980549091610100900463ffffffff169081106127c5576127c561573d565b60009182526020909120600160069092020101546001600160a01b031692915050565b8260007fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980548390811061281e5761281e61573d565b60009182526020909120600690910201600581015490915060ff1615612870576040517f1142a68c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018101546001600160a01b0316331461289c576040516282b42960e81b815260040160405180910390fd5b6128a58461450d565b6128ae8361450d565b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b39805485908290889081106128e5576128e561573d565b906000526020600020906006020160000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550838160000187815481106129305761293061573d565b60009182526020918290206006919091020160010180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0393841617905560408051898152888416928101929092529186168183015290517f97568892aa9e69db7c72b9f32e6d4fe3c0e53b9728a8c0a0f1c2a7eb47c48ad3916060908290030190a1505050505050565b60008181527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260408120546001600160a01b0316611d86565b6000611c81613a32565b612a156030826156e6565b15612a4c576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d576000612a9d84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b90506000612aaa82613e52565b60008181527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260409020549091506001600160a01b0316338114612b03576040516282b42960e81b815260040160405180910390fd5b612b0e82600161454d565b7fcb773ebd49049a1f68200f0814f547dec2c54293ff2c6a6a4f4d98d6d9cb12298184604051612b3f9291906158dd565b60405180910390a1505050603001612a4f565b85612b5c816140fe565b85600003612b96576040517fa9cb9e0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612ba16030856156e6565b151580612bb8575085612bb5603086615729565b14155b15612bef576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612bfa6060836156e6565b151580612c11575085612c0e606084615729565b14155b15612c48576040517f274cf40100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b397fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec9390260005b88811015612ec2576000612ce589898080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612cde925060309150869050615907565b6030613ccc565b90506000612d3788888080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612d30925060609150879050615907565b6060613ccc565b9050846000018c81548110612d4e57612d4e61573d565b600091825260208083206003600690930201919091018054600181018255908352918190208451612d869391909101918501906151a3565b50846000018c81548110612d9c57612d9c61573d565b600091825260208083206004600690930201919091018054600181018255908352918190208351612dd49391909101918401906151a3565b506000612de083613e52565b60008181526020879052604090205490915060ff1615612e3757826040517f5a303adb000000000000000000000000000000000000000000000000000000008152600401612e2e9190615944565b60405180910390fd5b60405180604001604052806001151581526020018e63ffffffff1681525085600001600083815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548163ffffffff021916908363ffffffff160217905550905050836001019350505050612c8d565b50887fb82c87b84f76f39d5f61ed59b411352603805dda6080a2c44b4a86eab141ff1b88888888604051612ef99493929190615982565b60405180910390a2612f09613ec7565b612f1289613f05565b505050505050505050565b612f25613ca2565b6001600160a01b0316336001600160a01b031614612f55576040516282b42960e81b815260040160405180910390fd5b6001612f5f614576565b60009283526020526040909120805460ff1916911515919091179055565b612f85613e28565b6001600160a01b0316336001600160a01b031614612fb5576040516282b42960e81b815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054819085908110612fea57612fea61573d565b600091825260209091206005600690920201015460ff1615613038576040517f1142a68c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081600001858154811061304f5761304f61573d565b9060005260206000209060060201600301805490509050838110156130aa576040517f62106cb30000000000000000000000000000000000000000000000000000000081526004810185905260248101829052604401612e2e565b838260000186815481106130c0576130c061573d565b9060005260206000209060060201600201541080156130e55750826130e36145a4565b115b1561311c576040517f474c62e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b838260000186815481106131325761313261573d565b90600052602060002090600602016002018190555061315085613f05565b60408051868152602081018690527fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e03910160405180910390a15050505050565b6131cf82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613a7992505050565b610c4582828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509250610bfa9150613aed9050565b6000611d86826145d4565b6000613227614576565b600092835260205250604090205460ff1690565b6000611c8161446a565b61324d610906565b565b6000611d8383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509250614231915050565b613298613e28565b6001600160a01b0316336001600160a01b0316146132c8576040516282b42960e81b815260040160405180910390fd5b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980546000908290859081106133005761330061573d565b9060005260206000209060060201600201819055507fa4dcee8ff224c7cfd53b1f2cd1f5afd98927d079b4152d639c5e523a52bf1e03836000604051613350929190918252602082015260400190565b60405180910390a1600181600001848154811061336f5761336f61573d565b600091825260209182902060069190910201600501805460ff1916921515929092179091556040518481527f4c644bb0e171ba9e5cf08f5d66836528c3947c3512b34dd8e27da30e803527b3910160405180910390a1818160000184815481106133db576133db61573d565b906000526020600020906006020160010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f97568892aa9e69db7c72b9f32e6d4fe3c0e53b9728a8c0a0f1c2a7eb47c48ad3838260000185815481106134475761344761573d565b600091825260209182902060069091020154604080519384526001600160a01b039182169284019290925285169082015260600160405180910390a1610a5d83613f05565b6001613496614388565b6134a19060016158c5565b81146134d9576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6134e2816143b2565b6134eb8b61450d565b6134f48b614493565b6134fd8a61450d565b6135068a6145de565b612710851115613542576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61354b85614335565b612710841115613587576040517f58d620b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61359084614208565b6135998861450d565b6135a288614607565b6135ab8761450d565b6135b487614630565b6135bd8961450d565b6135c689614659565b6135cf8661450d565b6135d886614682565b6135e2838361229b565b5050505050505050505050565b6000611d836121e884848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613e5292505050565b61363e6030826156e6565b15613675576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d5760006136c684848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b90506136d181613a79565b6136df816000610bfa613aed565b6136ed816001610bfa613ca2565b50603001613678565b6136fe6146ab565b613734576040517fee90c46800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61373d8161450d565b600061377e84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613e5292505050565b905060007f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e2656000838152602082905260409020549091506001600160a01b031633146137dc576040516282b42960e81b815260040160405180910390fd5b7f833a0d0704fbdc6a0f748ebec819c31dde0b532ca9e3e29f56a5c4e8f3cbc0c985858560405161380f939291906159b4565b60405180910390a160009182526020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03929092169190911790555050565b6138686030826156e6565b1561389f576040517f337d0f4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610a5d5760006138f084848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925060309150613ccc9050565b90506138fb81613a79565b613909816000610bfa613aed565b506030016138a2565b61391a613e28565b6001600160a01b0316336001600160a01b03161461394a576040516282b42960e81b815260040160405180910390fd5b6040516001600160a01b03821681527f63cc689e9d3377465b51fb094ea4ca5e0a1436b21f1ad30d707c696111c665009060200160405180910390a16123fb816145de565b613997613e28565b6001600160a01b0316336001600160a01b0316146139c7576040516282b42960e81b815260040160405180910390fd5b6123fb816144bc565b60007fc58a51931c529c2a8796a8fad2ae789ee504643b4b567f2c0c97e809cec93902613227565b6000611c815b6000611c81613a2e60017fb2e94356976bf59104fa32f6a0cf50947281732def9f18014aacae55ac60c5dd61576c565b5490565b6000611c817f559ad51499ae00ca2e9d9d95aab46737c8904ab7da276613fefda282b2c2ac065490565b613a6e613a69838361576c565b6146d5565b610c456000836146fe565b613a856121e882613e52565b6001600160a01b0316336001600160a01b031614158015613ab6575033613aaa613e28565b6001600160a01b031614155b156123fb576040517fca455fa500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611c817faa81344d5857c875349bc4a95d531a580c46bdd94c41b35b1e072e4d627079f85490565b6000613b2284613e52565b9050600060028483604051602001613b44929190918252602082015260400190565b60408051601f1981840301815290829052613b5e916159e1565b602060405180830381855afa158015613b7b573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190613b9e91906159fd565b90506000613baa614aa3565b90506000613bb88284614acd565b9050806001600160a01b03163b600003613c5357613bd68284614b49565b506040517f2cc0b2540000000000000000000000000000000000000000000000000000000081526001600160a01b03868116600483015260248201869052821690632cc0b25490604401600060405180830381600087803b158015613c3a57600080fd5b505af1158015613c4e573d6000803e3d6000fd5b505050505b806001600160a01b0316633ccfd60b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015613c8e57600080fd5b505af11580156135e2573d6000803e3d6000fd5b6000611c817fb6d5e19fdd6cde5f03ed4f17c2670deffa47975541fd270b0b17e803297d76085490565b606081613cda81601f6158c5565b1015613d42576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401612e2e565b613d4c82846158c5565b84511015613db6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401612e2e565b606082158015613dd55760405191506000825260208201604052613e1f565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015613e0e578051835260209283019201613df6565b5050858452601f01601f1916604052505b50949350505050565b6000611c817ffbeda9bc03875013b12a1ec161efb8e5bf7e58e3cec96a1ea9efd3e264d26e645490565b6000600282600060801b604051602001613e6d929190615a16565b60408051601f1981840301815290829052613e87916159e1565b602060405180830381855afa158015613ea4573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190611d8691906159fd565b613ed043614c1a565b6040514381527f55c5aabfc91da783d60f72c41f352ac7f9d507fcd884f41e27e6852305f948ce9060200160405180910390a1565b60408051808201825260008082526020808301828152600286901c83527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f0825293822054600686901b60c0161c63ffffffff8181168552911c1690925280517fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054929350916000908190849087908110613fa257613fa261573d565b9060005260206000209060060201600201549050846020015163ffffffff16811161403157602080860151600288901c60009081527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f083526040902080549190921b67ffffffff0000000016600689901b60c01690811b67ffffffffffffffff90911b199091161790556140b6565b60208501516140469063ffffffff168261576c565b602080870151600289901c60009081527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f083526040902080549190921b67ffffffff000000001663ffffffff84161760068a901b60c01690811b67ffffffffffffffff90911b1990911617905591505b8163ffffffff168363ffffffff16146140f6576140f68263ffffffff168463ffffffff166140e2613a32565b6140ec919061576c565b613a6991906158c5565b505050505050565b60007fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b398054839081106141335761413361573d565b60009182526020909120600690910201600581015490915060ff1615614185576040517f1142a68c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80546001600160a01b03163314610c45576040516282b42960e81b815260040160405180910390fd5b6000611c817fb88142a0318e2b174876bceb4db9dc318011849c83fb8a8bb2997386d562324a5490565b6000611c81613a2e60017f05896f012c7ab472c495a6810b73fc6183d339203dd1c74344bbfd2e1f5038fe61576c565b6123fb7f41118591c19026bdc7a484e34f80a8e7e632600aff1c72460e9c7dfe94a2dda6829055565b60008061423d84613e52565b905060006002848360405160200161425f929190918252602082015260400190565b60408051601f1981840301815290829052614279916159e1565b602060405180830381855afa158015614296573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906142b991906159fd565b905060006142c5614aa3565b90506142d18183614acd565b9695505050505050565b6000611c817f41118591c19026bdc7a484e34f80a8e7e632600aff1c72460e9c7dfe94a2dda65490565b6000611c81613a2e60017f2ad86e81c1a15749eb0399a4f2566324ade0cc7d7f8592a3db8896a29004bf8161576c565b6123fb7fb88142a0318e2b174876bceb4db9dc318011849c83fb8a8bb2997386d562324a829055565b6000611c817f10c92bb459c0223bf996150f2fb702a8288fb8354a33d5b0212e8e6b8273f55e5490565b6000611c817fd5c553085b8382c47128ae7612257fd5dc3b4fc4d3a108925604d3c8700c025b5490565b6123fb7fd5c553085b8382c47128ae7612257fd5dc3b4fc4d3a108925604d3c8700c025b829055565b6123fb61440960017f2ad86e81c1a15749eb0399a4f2566324ade0cc7d7f8592a3db8896a29004bf8161576c565b829055565b6123fb61440960017f05896f012c7ab472c495a6810b73fc6183d339203dd1c74344bbfd2e1f5038fe61576c565b6123fb61440960017fb2e94356976bf59104fa32f6a0cf50947281732def9f18014aacae55ac60c5dd61576c565b6000611c817e595eca1f8b39945ff4c404827bfa5fd1e295ef3f7d59d120a8ce3bae4e37a05490565b6123fb7ffbeda9bc03875013b12a1ec161efb8e5bf7e58e3cec96a1ea9efd3e264d26e64829055565b6123fb7e595eca1f8b39945ff4c404827bfa5fd1e295ef3f7d59d120a8ce3bae4e37a0829055565b6123fb7fce3b5bd42ef5aae9d7b01cd806ce2a8b66f0a48f2e8b5a15f77dfbbdbbf5144f829055565b6001600160a01b0381166123fb576040517ff6b2911f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80614556614c48565b60009384526020526040909220805460ff19169215159290921790915550565b600080611d8660017fd9ed3684910fa17a5f2ad83fd15b2ccb0abf740b7041631a9fe7daffda0cb9ee61576c565b6000611c81613a2e60017f7d3541d2c0079305636180d42008f210385563932c50e421bd8654ed4a6017eb61576c565b6000613227614c48565b6123fb7f10c92bb459c0223bf996150f2fb702a8288fb8354a33d5b0212e8e6b8273f55e829055565b6123fb7faa81344d5857c875349bc4a95d531a580c46bdd94c41b35b1e072e4d627079f8829055565b6123fb7fb6d5e19fdd6cde5f03ed4f17c2670deffa47975541fd270b0b17e803297d7608829055565b6123fb7fbc8b9852d17d50256bb221fdf6ee12d78dd493d807e907f7d223c40d65abd6b9829055565b6123fb7fd1c64973da70267569571a091966834c1a36bdba47f2a112b6a95bf41fc9c24e829055565b6000611c817fce3b5bd42ef5aae9d7b01cd806ce2a8b66f0a48f2e8b5a15f77dfbbdbbf5144f5490565b6123fb7f559ad51499ae00ca2e9d9d95aab46737c8904ab7da276613fefda282b2c2ac06829055565b7fd2a2f1f08ad325daf72af0169949ae210065d6916750ff03abd83510331b7b3980546000908290859081106147365761473661573d565b6000918252602080832060408051808201825285815280840186815260028b901c87527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f08552919095205460068a811b60c0169190911c63ffffffff8181168852941c90931690819052929091020192505b84826020015163ffffffff166147be91906158c5565b8110156149f75760008360030182815481106147dc576147dc61573d565b9060005260206000200180546147f190615783565b80601f016020809104026020016040519081016040528092919081815260200182805461481d90615783565b801561486a5780601f1061483f5761010080835404028352916020019161486a565b820191906000526020600020905b81548152906001019060200180831161484d57829003601f168201915b5050505050905060008460040183815481106148885761488861573d565b90600052602060002001805461489d90615783565b80601f01602080910402602001604051908101604052809291908181526020018280546148c990615783565b80156149165780601f106148eb57610100808354040283529160200191614916565b820191906000526020600020905b8154815290600101906020018083116148f957829003601f168201915b50505050509050600061492a836001614231565b9050600061493782614c76565b9050600061494485613e52565b905061495285828685614cab565b60008181527f86647fdbbdb534026d3e0f93a551cecf651c2b40fcdfef4b9fd9ed826133e26560205260409081902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001633908117909155905181907fac1020908b5f7134d59c1580838eba6fc42dd8c28bae65bf345676bba1913f8e906149df9089908990615a5d565b60405180910390a385600101955050505050506147a8565b50614a9c8585836000015163ffffffff16614a12919061576c565b86846020015163ffffffff16614a2891906158c5565b600283901c60009081527f37e2c371bbf1c7a1326d52e30855e9c8b6cac15eda4475320e427b948813a9f060209081526040909120805467ffffffffffffffff60c060069790971b9690961695861b191663ffffffff9490941667ffffffff000000009390921b929092161790921b179055565b5050505050565b6000611c817fd1c64973da70267569571a091966834c1a36bdba47f2a112b6a95bf41fc9c24e5490565b6000611d838383306040517f3d602d80600a3d3981f3363d3d373d3d3d363d730000000000000000000000008152606093841b60148201527f5af43d82803e903d91602b57fd5bf3ff000000000000000000000000000000006028820152921b6038830152604c8201526037808220606c830152605591012090565b60006040517f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000081528360601b60148201527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006028820152826037826000f59150506001600160a01b038116611d86576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f455243313136373a2063726561746532206661696c65640000000000000000006044820152606401612e2e565b6123fb61440960017f7d3541d2c0079305636180d42008f210385563932c50e421bd8654ed4a6017eb61576c565b600080611d8660017f68ed1186fe6f93f91e23b08dcba07651b0153d3ab058aab96212b33aaaf3a89961576c565b6000611d867f01000000000000000000000000000000000000000000000000000000000000006001600160a01b0384166158c5565b6000600280614cbd8560006040613ccc565b604051614cca91906159e1565b602060405180830381855afa158015614ce7573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614d0a91906159fd565b6002614d22866040614d1d81606061576c565b613ccc565b604051614d359190600090602001615a8b565b60408051601f1981840301815290829052614d4f916159e1565b602060405180830381855afa158015614d6c573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614d8f91906159fd565b60408051602081019390935282015260600160408051601f1981840301815290829052614dbb916159e1565b602060405180830381855afa158015614dd8573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614dfb91906159fd565b905060006002808685604051602001614e1e929190918252602082015260400190565b60408051601f1981840301815290829052614e38916159e1565b602060405180830381855afa158015614e55573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614e7891906159fd565b604080517e40597307000000000000000000000000000000000000000000000000000000602082015290810185905260029060600160408051601f1981840301815290829052614ec7916159e1565b602060405180830381855afa158015614ee4573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614f0791906159fd565b60408051602081019390935282015260600160408051601f1981840301815290829052614f33916159e1565b602060405180830381855afa158015614f50573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614f7391906159fd565b90506000614f8a6801bc16d674ec8000004761576c565b9050614f9461505b565b6001600160a01b031663228951186801bc16d674ec8000008987604051602001614fc091815260200190565b60405160208183030381529060405289876040518663ffffffff1660e01b8152600401614ff09493929190615aad565b6000604051808303818588803b15801561500957600080fd5b505af115801561501d573d6000803e3d6000fd5b5050505050804714611c6d576040517f6596d2b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611c817fbc8b9852d17d50256bb221fdf6ee12d78dd493d807e907f7d223c40d65abd6b95490565b50805461509190615783565b6000825580601f106150a1575050565b601f0160209004906000526020600020908101906123fb9190615217565b8280546150cb90615783565b90600052602060002090601f0160209004810192826150ed576000855561513a565b82601f106150fe578054855561513a565b8280016001018555821561513a57600052602060002091601f016020900482015b8281111561513a57825482559160010191906001019061511f565b50615146929150615217565b5090565b828054828255906000526020600020908101928215615197579160200282015b8281111561519757825180516151879184916020909101906151a3565b509160200191906001019061516a565b5061514692915061522c565b8280546151af90615783565b90600052602060002090601f0160209004810192826151d1576000855561513a565b82601f106151ea57805160ff191683800117855561513a565b8280016001018555821561513a579182015b8281111561513a5782518255916020019190600101906151fc565b5b808211156151465760008155600101615218565b808211156151465760006152408282615085565b5060010161522c565b60006020828403121561525b57600080fd5b5035919050565b60008083601f84011261527457600080fd5b50813567ffffffffffffffff81111561528c57600080fd5b6020830191508360208285010111156152a457600080fd5b9250929050565b600080602083850312156152be57600080fd5b823567ffffffffffffffff8111156152d557600080fd5b6152e185828601615262565b90969095509350505050565b60008060006040848603121561530257600080fd5b83359250602084013567ffffffffffffffff8082111561532157600080fd5b818601915086601f83011261533557600080fd5b81358181111561534457600080fd5b8760208260051b850101111561535957600080fd5b6020830194508093505050509250925092565b80356001600160a01b038116811461538357600080fd5b919050565b6000806040838503121561539b57600080fd5b823591506153ab6020840161536c565b90509250929050565b600080604083850312156153c757600080fd5b50508035926020909101359150565b60005b838110156153f15781810151838201526020016153d9565b83811115611c715750506000910152565b6000815180845261541a8160208601602086016153d6565b601f01601f19169290920160200192915050565b6080815260006154416080830187615402565b82810360208401526154538187615402565b6001600160a01b03959095166040840152505090151560609091015292915050565b60006020828403121561548757600080fd5b8135801515811461549757600080fd5b9392505050565b600080604083850312156154b157600080fd5b6154ba8361536c565b91506153ab6020840161536c565b6000806000606084860312156154dd57600080fd5b833592506154ed6020850161536c565b91506154fb6040850161536c565b90509250925092565b6000806000806000806080878903121561551d57600080fd5b8635955060208701359450604087013567ffffffffffffffff8082111561554357600080fd5b61554f8a838b01615262565b9096509450606089013591508082111561556857600080fd5b5061557589828a01615262565b979a9699509497509295939492505050565b60008060006060848603121561559c57600080fd5b505081359360208301359350604090920135919050565b6000806000806000806000806000806101408b8d0312156155d357600080fd5b6155dc8b61536c565b99506155ea60208c0161536c565b98506155f860408c0161536c565b975061560660608c0161536c565b965061561460808c0161536c565b955061562260a08c0161536c565b945060c08b0135935060e08b013592506101008b013591506101208b013590509295989b9194979a5092959850565b60008060006040848603121561566657600080fd5b833567ffffffffffffffff81111561567d57600080fd5b61568986828701615262565b90945092506154fb90506020850161536c565b6000602082840312156156ae57600080fd5b611d838261536c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826156f5576156f56156b7565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082615738576157386156b7565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008282101561577e5761577e6156fa565b500390565b600181811c9082168061579757607f821691505b6020821081036157d0577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600060208083526000845481600182811c9150808316806157f857607f831692505b858310810361582e577f4e487b710000000000000000000000000000000000000000000000000000000085526022600452602485fd5b87860183815260200181801561584b576001811461585c57615887565b60ff19861682528782019650615887565b60008b81526020902060005b8681101561588157815484820152908501908901615868565b83019750505b50949998505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600082198211156158d8576158d86156fa565b500190565b6001600160a01b03831681526040602082015260006158ff6040830184615402565b949350505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561593f5761593f6156fa565b500290565b602081526000611d836020830184615402565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b604081526000615996604083018688615957565b82810360208401526159a9818587615957565b979650505050505050565b6040815260006159c8604083018587615957565b90506001600160a01b0383166020830152949350505050565b600082516159f38184602087016153d6565b9190910192915050565b600060208284031215615a0f57600080fd5b5051919050565b60008351615a288184602088016153d6565b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000939093169190920190815260100192915050565b604081526000615a706040830185615402565b8281036020840152615a828185615402565b95945050505050565b60008351615a9d8184602088016153d6565b9190910191825250602001919050565b608081526000615ac06080830187615402565b8281036020840152615ad28187615402565b90508281036040840152615ae68186615402565b9150508260608301529594505050505056fea264697066735822122082f275dcc95ed77ce7470cc6de8ad96480198f43db9aa8fe8aa8314355ecbd8364736f6c634300080d0033", + "devdoc": { + "author": "Kiln", + "kind": "dev", + "methods": { + "acceptOwnership()": { + "details": "Only callable by new admin" + }, + "activateOperator(uint256,address)": { + "params": { + "_newFeeRecipient": "Sets the fee recipient address", + "_operatorIndex": "Operator Index" + } + }, + "addOperator(address,address)": { + "details": "Only callable by admin", + "params": { + "_feeRecipientAddress": "Privileged operator address used to manage rewards and operator addresses", + "_operatorAddress": "Operator address allowed to add / remove validators" + } + }, + "addValidators(uint256,uint256,bytes,bytes)": { + "details": "Only callable by operator", + "params": { + "_keyCount": "Number of keys added", + "_operatorIndex": "Operator Index", + "_publicKeys": "Concatenated _keyCount public keys", + "_signatures": "Concatenated _keyCount signatures" + } + }, + "batchWithdraw(bytes)": { + "details": "Funds are sent to the withdrawer account", + "params": { + "_publicKeys": "Validators to withdraw fees from" + } + }, + "batchWithdrawCLFee(bytes)": { + "details": "Funds are sent to the withdrawer accountThis method is public on purpose", + "params": { + "_publicKeys": "Validators to withdraw Consensus Layer Fees from" + } + }, + "batchWithdrawELFee(bytes)": { + "details": "Funds are sent to the withdrawer accountThis method is public on purpose", + "params": { + "_publicKeys": "Validators to withdraw Execution Layer Fees from" + } + }, + "deactivateOperator(uint256,address)": { + "params": { + "_operatorIndex": "Operator Index", + "_temporaryFeeRecipient": "Temporary address to receive funds decided by the system admin" + } + }, + "deposit()": { + "details": "A multiple of 32 ETH should be sent" + }, + "getCLFeeRecipient(bytes)": { + "params": { + "_publicKey": "Validator to get the recipient" + } + }, + "getELFeeRecipient(bytes)": { + "params": { + "_publicKey": "Validator to get the recipient" + } + }, + "getEnabledFromPublicKeyRoot(bytes32)": { + "params": { + "_publicKeyRoot": "Hash of the public key" + } + }, + "getExitRequestedFromRoot(bytes32)": { + "params": { + "_publicKeyRoot": "Public Key Root to check" + } + }, + "getOperator(uint256)": { + "params": { + "_operatorIndex": "Operator index" + } + }, + "getValidator(uint256,uint256)": { + "params": { + "_operatorIndex": "Index of the operator running the validator", + "_validatorIndex": "Index of the validator" + } + }, + "getWithdrawer(bytes)": { + "params": { + "_publicKey": "Public Key to check" + } + }, + "getWithdrawerFromPublicKeyRoot(bytes32)": { + "params": { + "_publicKeyRoot": "Hash of the public key" + } + }, + "getWithdrawnFromPublicKeyRoot(bytes32)": { + "params": { + "_publicKeyRoot": "Public Key Root of the validator" + } + }, + "removeValidators(uint256,uint256[])": { + "details": "Only callable by operatorIndexes should be provided in decreasing orderThe limit will be set to the lowest removed operator index to ensure all changes above the lowest removed validator key are verified by the system administrator", + "params": { + "_indexes": "List of indexes to delete, in decreasing order", + "_operatorIndex": "Operator Index" + } + }, + "setGlobalFee(uint256)": { + "params": { + "_globalFee": "Fee in Basis Point" + } + }, + "setOperatorAddresses(uint256,address,address)": { + "details": "Only callable by fee recipient address manager", + "params": { + "_feeRecipientAddress": "New operator address for reward management", + "_operatorAddress": "New operator address for operations management", + "_operatorIndex": "Index of the operator to update" + } + }, + "setOperatorFee(uint256)": { + "params": { + "_operatorFee": "Fee in Basis Point" + } + }, + "setOperatorLimit(uint256,uint256,uint256)": { + "details": "Only callable by adminLimit should not exceed the validator key count of the operatorKeys should be registered before limit is increasedAllows all keys to be verified by the system admin before limit is increased", + "params": { + "_limit": "New staking limit", + "_operatorIndex": "Operator Index", + "_snapshot": "Block number at which verification was done" + } + }, + "setTreasury(address)": { + "details": "Only callable by admin", + "params": { + "_newTreasury": "New Treasury address" + } + }, + "setWithdrawer(bytes,address)": { + "details": "Only callable by current public key withdrawer", + "params": { + "_newWithdrawer": "New withdrawer address", + "_publicKey": "Public key to change withdrawer" + } + }, + "setWithdrawerCustomizationEnabled(bool)": { + "params": { + "_enabled": "True to allow users to customize the withdrawer" + } + }, + "toggleWithdrawnFromPublicKeyRoot(bytes32)": { + "params": { + "_publicKeyRoot": "Public Key Root of the validator" + } + }, + "transferOwnership(address)": { + "details": "Only callable by admin", + "params": { + "_newAdmin": "New Administrator address" + } + }, + "withdraw(bytes)": { + "details": "Reverts if any is null", + "params": { + "_publicKey": "Validator to withdraw Execution and Consensus Layer Fees from" + } + }, + "withdrawCLFee(bytes)": { + "details": "Funds are sent to the withdrawer account", + "params": { + "_publicKey": "Validator to withdraw Consensus Layer Fees from" + } + }, + "withdrawELFee(bytes)": { + "details": "Funds are sent to the withdrawer account", + "params": { + "_publicKey": "Validator to withdraw Execution Layer Fees from" + } + } + }, + "title": "Ethereum Staking Contract", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "acceptOwnership()": { + "notice": "New admin must accept its role by calling this method" + }, + "activateOperator(uint256,address)": { + "notice": "Activates an operator, without changing its 0 staking limit" + }, + "addOperator(address,address)": { + "notice": "Add new operator" + }, + "addValidators(uint256,uint256,bytes,bytes)": { + "notice": "Add new validator public keys and signatures" + }, + "batchWithdraw(bytes)": { + "notice": "Withdraw both Consensus and Execution Layer Fees for given validators public keys" + }, + "batchWithdrawCLFee(bytes)": { + "notice": "Withdraw the Consensus Layer Fee for given validators public keys" + }, + "batchWithdrawELFee(bytes)": { + "notice": "Withdraw the Execution Layer Fee for given validators public keys" + }, + "deactivateOperator(uint256,address)": { + "notice": "Deactivates an operator and changes the fee recipient address and the staking limit" + }, + "deposit()": { + "notice": "Explicit deposit method using msg.sender" + }, + "getAdmin()": { + "notice": "Retrieve system admin" + }, + "getAvailableValidatorCount()": { + "notice": "Get the total available keys that are ready to be used for deposits" + }, + "getCLFeeRecipient(bytes)": { + "notice": "Compute the Consensus Layer Fee recipient address for a given validator public key" + }, + "getDepositsStopped()": { + "notice": "Returns false if the users can deposit, true if deposits are stopped" + }, + "getELFeeRecipient(bytes)": { + "notice": "Compute the Execution Layer Fee recipient address for a given validator public key" + }, + "getEnabledFromPublicKeyRoot(bytes32)": { + "notice": "Retrieve the enabled status of public key root, true if the key is in the contract" + }, + "getExitRequestedFromRoot(bytes32)": { + "notice": "Retrieve whether the validator exit has been requestedIn case the validator is not enabled, it will return false" + }, + "getGlobalFee()": { + "notice": "Retrieve the global fee" + }, + "getOperator(uint256)": { + "notice": "Retrieve operator details" + }, + "getOperatorFee()": { + "notice": "Retrieve the operator fee" + }, + "getOperatorFeeRecipient(bytes32)": { + "notice": "Retrieve the Execution & Consensus Layer Fee operator recipient for a given public key" + }, + "getPendingAdmin()": { + "notice": "Get the new admin's address previously set for an ownership transfer" + }, + "getTreasury()": { + "notice": "Retrieve system treasury" + }, + "getValidator(uint256,uint256)": { + "notice": "Get details about a validator" + }, + "getWithdrawer(bytes)": { + "notice": "Retrieve withdrawer of public keyIn case the validator is not enabled, it will return address(0)" + }, + "getWithdrawerFromPublicKeyRoot(bytes32)": { + "notice": "Retrieve withdrawer of public key rootIn case the validator is not enabled, it will return address(0)" + }, + "getWithdrawnFromPublicKeyRoot(bytes32)": { + "notice": "Return true if the validator already went through the exit logicIn case the validator is not enabled, it will return false" + }, + "removeValidators(uint256,uint256[])": { + "notice": "Remove unfunded validators" + }, + "setDepositsStopped(bool)": { + "notice": "Utility to stop or allow deposits" + }, + "setGlobalFee(uint256)": { + "notice": "Change the Global fee" + }, + "setOperatorAddresses(uint256,address,address)": { + "notice": "Set new operator addresses (operations and reward management)" + }, + "setOperatorFee(uint256)": { + "notice": "Change the Operator fee" + }, + "setOperatorLimit(uint256,uint256,uint256)": { + "notice": "Set operator staking limits" + }, + "setTreasury(address)": { + "notice": "Set new treasury" + }, + "setWithdrawer(bytes,address)": { + "notice": "Set withdrawer for public key" + }, + "setWithdrawerCustomizationEnabled(bool)": { + "notice": "Changes the behavior of the withdrawer customization logic" + }, + "toggleWithdrawnFromPublicKeyRoot(bytes32)": { + "notice": "Allows the CLDispatcher to signal a validator went through the exit logic" + }, + "transferOwnership(address)": { + "notice": "Set new admin" + }, + "withdraw(bytes)": { + "notice": "Withdraw both Consensus and Execution Layer Fee for a given validator public key" + }, + "withdrawCLFee(bytes)": { + "notice": "Withdraw the Consensus Layer Fee for a given validator public key" + }, + "withdrawELFee(bytes)": { + "notice": "Withdraw the Execution Layer Fee for a given validator public key" + } + }, + "notice": "You can use this contract to store validator keys and have users fund them and trigger deposits.", + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/deployments/mainnet_safe/solcInputs/e20c74109a1c61285ef1a2afbbbc70eb.json b/deployments/mainnet_safe/solcInputs/e20c74109a1c61285ef1a2afbbbc70eb.json new file mode 100644 index 0000000..9c74564 --- /dev/null +++ b/deployments/mainnet_safe/solcInputs/e20c74109a1c61285ef1a2afbbbc70eb.json @@ -0,0 +1,100 @@ +{ + "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": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates", + "devdoc", + "userdoc" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file