ETH Price: $2,696.58 (+2.22%)

Contract

0xC24162aa69C3267Ed5a4900a2976487BDDFC69d2
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Init CLD187429232023-12-08 17:06:35294 days ago1702055195IN
0xC24162aa...BDDFC69d2
0 ETH0.0047294971.57984729
0x60806040187429222023-12-08 17:06:23294 days ago1702055183IN
 Contract Creation
0 ETH0.0563288573.61650983

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
208448452024-09-27 22:07:1111 mins ago1727474831
0xC24162aa...BDDFC69d2
0.00589035 ETH
208448452024-09-27 22:07:1111 mins ago1727474831
0xC24162aa...BDDFC69d2
0.00065448 ETH
208448452024-09-27 22:07:1111 mins ago1727474831
0xC24162aa...BDDFC69d2
0.0589035 ETH
208448452024-09-27 22:07:1111 mins ago1727474831
0xC24162aa...BDDFC69d2
0.06544833 ETH
208413152024-09-27 10:18:4712 hrs ago1727432327
0xC24162aa...BDDFC69d2
0.00709691 ETH
208413152024-09-27 10:18:4712 hrs ago1727432327
0xC24162aa...BDDFC69d2
0.00078854 ETH
208413152024-09-27 10:18:4712 hrs ago1727432327
0xC24162aa...BDDFC69d2
0.07096917 ETH
208413152024-09-27 10:18:4712 hrs ago1727432327
0xC24162aa...BDDFC69d2
0.07885463 ETH
208413152024-09-27 10:18:4712 hrs ago1727432327
0xC24162aa...BDDFC69d2
0.00710081 ETH
208413152024-09-27 10:18:4712 hrs ago1727432327
0xC24162aa...BDDFC69d2
0.00078897 ETH
208413152024-09-27 10:18:4712 hrs ago1727432327
0xC24162aa...BDDFC69d2
0.0710081 ETH
208413152024-09-27 10:18:4712 hrs ago1727432327
0xC24162aa...BDDFC69d2
0.07889789 ETH
208400862024-09-27 6:12:1116 hrs ago1727417531
0xC24162aa...BDDFC69d2
0.04155837 ETH
208400862024-09-27 6:12:1116 hrs ago1727417531
0xC24162aa...BDDFC69d2
0.00461759 ETH
208400862024-09-27 6:12:1116 hrs ago1727417531
0xC24162aa...BDDFC69d2
0.41558373 ETH
208400862024-09-27 6:12:1116 hrs ago1727417531
0xC24162aa...BDDFC69d2
0.46175971 ETH
208333952024-09-26 7:48:4738 hrs ago1727336927
0xC24162aa...BDDFC69d2
0.00512152 ETH
208333952024-09-26 7:48:4738 hrs ago1727336927
0xC24162aa...BDDFC69d2
0.00056905 ETH
208333952024-09-26 7:48:4738 hrs ago1727336927
0xC24162aa...BDDFC69d2
0.05121524 ETH
208333952024-09-26 7:48:4738 hrs ago1727336927
0xC24162aa...BDDFC69d2
0.05690582 ETH
208333792024-09-26 7:45:3538 hrs ago1727336735
0xC24162aa...BDDFC69d2
0.00516103 ETH
208333792024-09-26 7:45:3538 hrs ago1727336735
0xC24162aa...BDDFC69d2
0.00057344 ETH
208333792024-09-26 7:45:3538 hrs ago1727336735
0xC24162aa...BDDFC69d2
0.05161034 ETH
208333792024-09-26 7:45:3538 hrs ago1727336735
0xC24162aa...BDDFC69d2
0.05734482 ETH
208331392024-09-26 6:57:3539 hrs ago1727333855
0xC24162aa...BDDFC69d2
0.00172204 ETH
View All Internal Transactions
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x462Dd07A...88d5d03C3
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
ConsensusLayerFeeDispatcher

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion, BSL 1.1 license
File 1 of 22 : ConsensusLayerFeeDispatcher.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.10;

import "./libs/DispatchersStorageLib.sol";
import "./interfaces/IStakingContractFeeDetails.sol";
import "./interfaces/IFeeDispatcher.sol";

/// @title Consensus Layer Fee Recipient
/// @author Kiln
/// @notice This contract can be used to receive fees from a validator and split them with a node operator
contract ConsensusLayerFeeDispatcher is IFeeDispatcher {
    using DispatchersStorageLib for bytes32;

    event Withdrawal(
        address indexed withdrawer,
        address indexed feeRecipient,
        bytes32 pubKeyRoot,
        uint256 rewards,
        uint256 nodeOperatorFee,
        uint256 treasuryFee
    );

    error TreasuryReceiveError(bytes errorData);
    error FeeRecipientReceiveError(bytes errorData);
    error WithdrawerReceiveError(bytes errorData);
    error ZeroBalanceWithdrawal();
    error AlreadyInitialized();
    error InvalidCall();

    bytes32 internal constant STAKING_CONTRACT_ADDRESS_SLOT =
        keccak256("ConsensusLayerFeeRecipient.stakingContractAddress");
    uint256 internal constant BASIS_POINTS = 10_000;
    bytes32 internal constant VERSION_SLOT = keccak256("ConsensusLayerFeeRecipient.version");

    /// @notice Ensures an initialisation call has been called only once per _version value
    /// @param _version The current initialisation value
    modifier init(uint256 _version) {
        if (_version != VERSION_SLOT.getUint256() + 1) {
            revert AlreadyInitialized();
        }

        VERSION_SLOT.setUint256(_version);

        _;
    }

    /// @notice Constructor method allowing us to prevent calls to initCLFR by setting the appropriate version
    constructor(uint256 _version) {
        VERSION_SLOT.setUint256(_version);
    }

    /// @notice Initialize the contract by storing the staking contract
    /// @param _stakingContract Address of the Staking Contract
    function initCLD(address _stakingContract) external init(1) {
        STAKING_CONTRACT_ADDRESS_SLOT.setAddress(_stakingContract);
    }

    /// @notice Performs a withdrawal on this contract's balance
    function dispatch(bytes32 _publicKeyRoot) external payable {
        IStakingContractFeeDetails stakingContract = IStakingContractFeeDetails(
            STAKING_CONTRACT_ADDRESS_SLOT.getAddress()
        );

        uint256 balance = address(this).balance; // this has taken into account msg.value
        if (balance == 0) {
            revert ZeroBalanceWithdrawal();
        }

        bool exitRequested = stakingContract.getExitRequestedFromRoot(_publicKeyRoot);
        bool withdrawn = stakingContract.getWithdrawnFromPublicKeyRoot(_publicKeyRoot);

        uint256 nonExemptBalance = balance;

        if (exitRequested && balance >= 31 ether && !withdrawn) {
            // If the skimmed rewards were withdrawn and the validator then underperformed
            // an healthy exit can be slightly lower than 32 ETH
            // We exempt the balance up to 32 ETH, happens only once.
            // !withdrawn prevents this logic being reused to not pay the fee on rewards
            uint256 exemption = nonExemptBalance > 32 ether ? 32 ether : nonExemptBalance;
            nonExemptBalance -= exemption;
            stakingContract.toggleWithdrawnFromPublicKeyRoot(_publicKeyRoot);
        }
        // In case of slashing the exit is not requested we don't exempt anything
        // This is in case of slashing, the staker will be rebated manually
        // A slashed validator may have accumulated enough skimmed rewards to still have a balance > 32 ETH
        // All of this will be taken into account and the staker will be compensated for the commission taken
        // on its principal and the loss according to the SLA described in the Terms&Conditions

        uint256 globalFee = (nonExemptBalance * stakingContract.getGlobalFee()) / BASIS_POINTS;
        uint256 operatorFee = (globalFee * stakingContract.getOperatorFee()) / BASIS_POINTS;
        address operator = stakingContract.getOperatorFeeRecipient(_publicKeyRoot);
        address treasury = stakingContract.getTreasury();
        address withdrawer = stakingContract.getWithdrawerFromPublicKeyRoot(_publicKeyRoot);

        (bool status, bytes memory data) = withdrawer.call{value: balance - globalFee}("");
        if (status == false) {
            revert WithdrawerReceiveError(data);
        }
        if (globalFee > 0) {
            (status, data) = treasury.call{value: globalFee - operatorFee}("");
            if (status == false) {
                revert TreasuryReceiveError(data);
            }
        }
        if (operatorFee > 0) {
            (status, data) = operator.call{value: operatorFee}("");
            if (status == false) {
                revert FeeRecipientReceiveError(data);
            }
        }
        emit Withdrawal(
            withdrawer,
            operator,
            _publicKeyRoot,
            balance - globalFee,
            operatorFee,
            globalFee - operatorFee
        );
    }

    /// @notice Retrieve the staking contract address
    function getStakingContract() external view returns (address) {
        return STAKING_CONTRACT_ADDRESS_SLOT.getAddress();
    }

    /// @notice Retrieve the assigned withdrawer for the given public key root
    /// @param _publicKeyRoot Public key root to get the owner
    function getWithdrawer(bytes32 _publicKeyRoot) external view returns (address) {
        IStakingContractFeeDetails stakingContract = IStakingContractFeeDetails(
            STAKING_CONTRACT_ADDRESS_SLOT.getAddress()
        );
        return stakingContract.getWithdrawerFromPublicKeyRoot(_publicKeyRoot);
    }

    receive() external payable {
        revert InvalidCall();
    }

    fallback() external payable {
        revert InvalidCall();
    }
}

File 2 of 22 : draft-IERC1822.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.0;

/**
 * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
 * proxy whose upgrades are fully controlled by the current implementation.
 */
interface IERC1822Proxiable {
    /**
     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
     * address.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy.
     */
    function proxiableUUID() external view returns (bytes32);
}

File 3 of 22 : IBeacon.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.0;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeacon {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {BeaconProxy} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}

File 4 of 22 : Clones.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/Clones.sol)

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create(0, ptr, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create2(0, ptr, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
            mstore(add(ptr, 0x38), shl(0x60, deployer))
            mstore(add(ptr, 0x4c), salt)
            mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
            predicted := keccak256(add(ptr, 0x37), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address implementation, bytes32 salt)
        internal
        view
        returns (address predicted)
    {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

File 5 of 22 : ERC1967Proxy.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol)

pragma solidity ^0.8.0;

import "../Proxy.sol";
import "./ERC1967Upgrade.sol";

/**
 * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
 * implementation address that can be changed. This address is stored in storage in the location specified by
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
 * implementation behind the proxy.
 */
contract ERC1967Proxy is Proxy, ERC1967Upgrade {
    /**
     * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
     *
     * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
     * function call, and allows initializating the storage of the proxy like a Solidity constructor.
     */
    constructor(address _logic, bytes memory _data) payable {
        assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
        _upgradeToAndCall(_logic, _data, false);
    }

    /**
     * @dev Returns the current implementation address.
     */
    function _implementation() internal view virtual override returns (address impl) {
        return ERC1967Upgrade._getImplementation();
    }
}

File 6 of 22 : ERC1967Upgrade.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)

pragma solidity ^0.8.2;

import "../beacon/IBeacon.sol";
import "../../interfaces/draft-IERC1822.sol";
import "../../utils/Address.sol";
import "../../utils/StorageSlot.sol";

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 *
 * _Available since v4.1._
 *
 * @custom:oz-upgrades-unsafe-allow delegatecall
 */
abstract contract ERC1967Upgrade {
    // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
    bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Returns the current implementation address.
     */
    function _getImplementation() internal view returns (address) {
        return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
        StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Perform implementation upgrade
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Perform implementation upgrade with additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCall(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        _upgradeTo(newImplementation);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(newImplementation, data);
        }
    }

    /**
     * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCallUUPS(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        // Upgrades from old implementations will perform a rollback test. This test requires the new
        // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
        // this special case will break upgrade paths from old UUPS implementation to new ones.
        if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
            _setImplementation(newImplementation);
        } else {
            try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
            } catch {
                revert("ERC1967Upgrade: new implementation is not UUPS");
            }
            _upgradeToAndCall(newImplementation, data, forceCall);
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Returns the current admin.
     */
    function _getAdmin() internal view returns (address) {
        return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
        StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     */
    function _changeAdmin(address newAdmin) internal {
        emit AdminChanged(_getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
     */
    bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Emitted when the beacon is upgraded.
     */
    event BeaconUpgraded(address indexed beacon);

    /**
     * @dev Returns the current beacon.
     */
    function _getBeacon() internal view returns (address) {
        return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the EIP1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
        require(
            Address.isContract(IBeacon(newBeacon).implementation()),
            "ERC1967: beacon implementation is not a contract"
        );
        StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
    }

    /**
     * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
     * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
     *
     * Emits a {BeaconUpgraded} event.
     */
    function _upgradeBeaconToAndCall(
        address newBeacon,
        bytes memory data,
        bool forceCall
    ) internal {
        _setBeacon(newBeacon);
        emit BeaconUpgraded(newBeacon);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
        }
    }
}

File 7 of 22 : Proxy.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/Proxy.sol)

pragma solidity ^0.8.0;

/**
 * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
 * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
 * be specified by overriding the virtual {_implementation} function.
 *
 * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
 * different contract through the {_delegate} function.
 *
 * The success and return data of the delegated call will be returned back to the caller of the proxy.
 */
abstract contract Proxy {
    /**
     * @dev Delegates the current call to `implementation`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _delegate(address implementation) internal virtual {
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }

    /**
     * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
     * and {_fallback} should delegate.
     */
    function _implementation() internal view virtual returns (address);

    /**
     * @dev Delegates the current call to the address returned by `_implementation()`.
     *
     * This function does not return to its internall call site, it will return directly to the external caller.
     */
    function _fallback() internal virtual {
        _beforeFallback();
        _delegate(_implementation());
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
     * function in the contract matches the call data.
     */
    fallback() external payable virtual {
        _fallback();
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
     * is empty.
     */
    receive() external payable virtual {
        _fallback();
    }

    /**
     * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
     * call, or as part of the Solidity `fallback` or `receive` functions.
     *
     * If overriden should call `super._beforeFallback()`.
     */
    function _beforeFallback() internal virtual {}
}

File 8 of 22 : TransparentUpgradeableProxy.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol)

pragma solidity ^0.8.0;

import "../ERC1967/ERC1967Proxy.sol";

/**
 * @dev This contract implements a proxy that is upgradeable by an admin.
 *
 * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
 * clashing], which can potentially be used in an attack, this contract uses the
 * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
 * things that go hand in hand:
 *
 * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
 * that call matches one of the admin functions exposed by the proxy itself.
 * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
 * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
 * "admin cannot fallback to proxy target".
 *
 * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
 * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
 * to sudden errors when trying to call a function from the proxy implementation.
 *
 * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
 * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
 */
contract TransparentUpgradeableProxy is ERC1967Proxy {
    /**
     * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
     * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
     */
    constructor(
        address _logic,
        address admin_,
        bytes memory _data
    ) payable ERC1967Proxy(_logic, _data) {
        assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
        _changeAdmin(admin_);
    }

    /**
     * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
     */
    modifier ifAdmin() {
        if (msg.sender == _getAdmin()) {
            _;
        } else {
            _fallback();
        }
    }

    /**
     * @dev Returns the current admin.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
     */
    function admin() external ifAdmin returns (address admin_) {
        admin_ = _getAdmin();
    }

    /**
     * @dev Returns the current implementation.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
     */
    function implementation() external ifAdmin returns (address implementation_) {
        implementation_ = _implementation();
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
     */
    function changeAdmin(address newAdmin) external virtual ifAdmin {
        _changeAdmin(newAdmin);
    }

    /**
     * @dev Upgrade the implementation of the proxy.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
     */
    function upgradeTo(address newImplementation) external ifAdmin {
        _upgradeToAndCall(newImplementation, bytes(""), false);
    }

    /**
     * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
     * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
     * proxied contract.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
     */
    function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
        _upgradeToAndCall(newImplementation, data, true);
    }

    /**
     * @dev Returns the current admin.
     */
    function _admin() internal view virtual returns (address) {
        return _getAdmin();
    }

    /**
     * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
     */
    function _beforeFallback() internal virtual override {
        require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
        super._beforeFallback();
    }
}

File 9 of 22 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 10 of 22 : StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        assembly {
            r.slot := slot
        }
    }
}

File 11 of 22 : AuthorizedFeeRecipient.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.10;

import "./interfaces/IFeeDispatcher.sol";
import "./libs/DispatchersStorageLib.sol";
import "./interfaces/IFeeRecipient.sol";

contract AuthorizedFeeRecipient is IFeeRecipient {
    /// @notice Constructor replay prevention
    bool internal initialized;
    /// @notice Address where funds are sent to be dispatched
    IFeeDispatcher internal dispatcher;
    /// @notice Public Key root assigned to this receiver
    bytes32 internal publicKeyRoot;
    /// @notice Address of the staking contract
    address internal stakingContract;

    error AlreadyInitialized();
    error Unauthorized();

    /// @notice Initializes the receiver
    /// @param _dispatcher Address that will handle the fee dispatching
    /// @param _publicKeyRoot Public Key root assigned to this receiver
    function init(address _dispatcher, bytes32 _publicKeyRoot) external {
        if (initialized) {
            revert AlreadyInitialized();
        }
        initialized = true;
        dispatcher = IFeeDispatcher(_dispatcher);
        publicKeyRoot = _publicKeyRoot;
        stakingContract = msg.sender; // The staking contract always calls init
    }

    /// @notice Empty calldata fallback
    receive() external payable {}

    /// @notice Non-empty calldata fallback
    fallback() external payable {}

    /// @notice Triggers a withdrawal by sending its funds + its public key root to the dispatcher
    /// @dev Can be called only be called through the staking contract
    function withdraw() external {
        if (msg.sender != stakingContract) {
            revert Unauthorized();
        }
        dispatcher.dispatch{value: address(this).balance}(publicKeyRoot);
    }

    /// @notice Retrieve the assigned public key root
    function getPublicKeyRoot() external view returns (bytes32) {
        return publicKeyRoot;
    }

    /// @notice retrieve the assigned withdrawer
    function getWithdrawer() external view returns (address) {
        return dispatcher.getWithdrawer(publicKeyRoot);
    }
}

File 12 of 22 : ExecutionLayerFeeDispatcher.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.10;

import "./libs/DispatchersStorageLib.sol";
import "./interfaces/IStakingContractFeeDetails.sol";
import "./interfaces/IFeeDispatcher.sol";

/// @title Execution Layer Fee Recipient
/// @author Kiln
/// @notice This contract can be used to receive fees from a validator and split them with a node operator
contract ExecutionLayerFeeDispatcher is IFeeDispatcher {
    using DispatchersStorageLib for bytes32;

    event Withdrawal(
        address indexed withdrawer,
        address indexed feeRecipient,
        bytes32 pubKeyRoot,
        uint256 rewards,
        uint256 nodeOperatorFee,
        uint256 treasuryFee
    );

    error TreasuryReceiveError(bytes errorData);
    error FeeRecipientReceiveError(bytes errorData);
    error WithdrawerReceiveError(bytes errorData);
    error ZeroBalanceWithdrawal();
    error AlreadyInitialized();
    error InvalidCall();

    bytes32 internal constant STAKING_CONTRACT_ADDRESS_SLOT =
        keccak256("ExecutionLayerFeeRecipient.stakingContractAddress");
    uint256 internal constant BASIS_POINTS = 10_000;
    bytes32 internal constant VERSION_SLOT = keccak256("ExecutionLayerFeeRecipient.version");

    /// @notice Ensures an initialisation call has been called only once per _version value
    /// @param _version The current initialisation value
    modifier init(uint256 _version) {
        if (_version != VERSION_SLOT.getUint256() + 1) {
            revert AlreadyInitialized();
        }

        VERSION_SLOT.setUint256(_version);

        _;
    }

    /// @notice Constructor method allowing us to prevent calls to initCLFR by setting the appropriate version
    constructor(uint256 _version) {
        VERSION_SLOT.setUint256(_version);
    }

    /// @notice Initialize the contract by storing the staking contract and the public key in storage
    /// @param _stakingContract Address of the Staking Contract
    function initELD(address _stakingContract) external init(1) {
        STAKING_CONTRACT_ADDRESS_SLOT.setAddress(_stakingContract);
    }

    /// @notice Performs a withdrawal on this contract's balance
    function dispatch(bytes32 _publicKeyRoot) external payable {
        uint256 balance = address(this).balance;
        if (balance == 0) {
            revert ZeroBalanceWithdrawal();
        }
        IStakingContractFeeDetails stakingContract = IStakingContractFeeDetails(
            STAKING_CONTRACT_ADDRESS_SLOT.getAddress()
        );
        address withdrawer = stakingContract.getWithdrawerFromPublicKeyRoot(_publicKeyRoot);
        address operator = stakingContract.getOperatorFeeRecipient(_publicKeyRoot);
        address treasury = stakingContract.getTreasury();
        uint256 globalFee = (balance * stakingContract.getGlobalFee()) / BASIS_POINTS;
        uint256 operatorFee = (globalFee * stakingContract.getOperatorFee()) / BASIS_POINTS;

        (bool status, bytes memory data) = withdrawer.call{value: balance - globalFee}("");
        if (status == false) {
            revert WithdrawerReceiveError(data);
        }
        if (globalFee > 0) {
            (status, data) = treasury.call{value: globalFee - operatorFee}("");
            if (status == false) {
                revert TreasuryReceiveError(data);
            }
        }
        if (operatorFee > 0) {
            (status, data) = operator.call{value: operatorFee}("");
            if (status == false) {
                revert FeeRecipientReceiveError(data);
            }
        }
        emit Withdrawal(
            withdrawer,
            operator,
            _publicKeyRoot,
            balance - globalFee,
            operatorFee,
            globalFee - operatorFee
        );
    }

    /// @notice Retrieve the staking contract address
    function getStakingContract() external view returns (address) {
        return STAKING_CONTRACT_ADDRESS_SLOT.getAddress();
    }

    /// @notice Retrieve the assigned withdrawer for the given public key root
    /// @param _publicKeyRoot Public key root to get the owner
    function getWithdrawer(bytes32 _publicKeyRoot) external view returns (address) {
        IStakingContractFeeDetails stakingContract = IStakingContractFeeDetails(
            STAKING_CONTRACT_ADDRESS_SLOT.getAddress()
        );
        return stakingContract.getWithdrawerFromPublicKeyRoot(_publicKeyRoot);
    }

    receive() external payable {
        revert InvalidCall();
    }

    fallback() external payable {
        revert InvalidCall();
    }
}

File 13 of 22 : FeeRecipient.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.10;

import "./interfaces/IFeeDispatcher.sol";

contract FeeRecipient {
    /// @notice Constructor replay prevention
    bool internal initialized;
    /// @notice Address where funds are sent to be dispatched
    IFeeDispatcher internal dispatcher;
    /// @notice Public Key root assigned to this receiver
    bytes32 internal publicKeyRoot;

    error AlreadyInitialized();

    /// @notice Initializes the receiver
    /// @param _dispatcher Address that will handle the fee dispatching
    /// @param _publicKeyRoot Public Key root assigned to this receiver
    function init(address _dispatcher, bytes32 _publicKeyRoot) external {
        if (initialized) {
            revert AlreadyInitialized();
        }
        initialized = true;
        dispatcher = IFeeDispatcher(_dispatcher);
        publicKeyRoot = _publicKeyRoot;
    }

    /// @notice Empty calldata fallback
    receive() external payable {}

    /// @notice Non-empty calldata fallback
    fallback() external payable {}

    /// @notice Triggers a withdrawal by sending its funds + its public key root to the dispatcher
    /// @dev Can be called by any wallet as recipients are not parameters
    function withdraw() external {
        dispatcher.dispatch{value: address(this).balance}(publicKeyRoot);
    }

    /// @notice Retrieve the assigned public key root
    function getPublicKeyRoot() external view returns (bytes32) {
        return publicKeyRoot;
    }

    /// @notice retrieve the assigned withdrawer
    function getWithdrawer() external view returns (address) {
        return dispatcher.getWithdrawer(publicKeyRoot);
    }
}

File 14 of 22 : IDepositContract.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.10;

interface IDepositContract {
    function deposit(
        bytes calldata pubkey,
        bytes calldata withdrawalCredentials,
        bytes calldata signature,
        bytes32 depositDataRoot
    ) external payable;
}

File 15 of 22 : IFeeDispatcher.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.10;

interface IFeeDispatcher {
    function dispatch(bytes32 _publicKeyRoot) external payable;

    function getWithdrawer(bytes32 _publicKeyRoot) external view returns (address);
}

File 16 of 22 : IFeeRecipient.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.10;

interface IFeeRecipient {
    function init(address _dispatcher, bytes32 _publicKeyRoot) external;

    function withdraw() external;
}

File 17 of 22 : IStakingContractFeeDetails.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.10;

interface IStakingContractFeeDetails {
    function getWithdrawerFromPublicKeyRoot(bytes32 _publicKeyRoot) external view returns (address);

    function getTreasury() external view returns (address);

    function getOperatorFeeRecipient(bytes32 pubKeyRoot) external view returns (address);

    function getGlobalFee() external view returns (uint256);

    function getOperatorFee() external view returns (uint256);

    function getExitRequestedFromRoot(bytes32 _publicKeyRoot) external view returns (bool);

    function getWithdrawnFromPublicKeyRoot(bytes32 _publicKeyRoot) external view returns (bool);

    function toggleWithdrawnFromPublicKeyRoot(bytes32 _publicKeyRoot) external;
}

File 18 of 22 : BytesLib.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.10;

/// Based on GNSPS/BytesLib.sol
library BytesLib {
    function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) {
        bytes memory tempBytes;

        assembly {
            // Get a location of some free memory and store it in tempBytes as
            // Solidity does for memory variables.
            tempBytes := mload(0x40)

            // Store the length of the first bytes array at the beginning of
            // the memory for tempBytes.
            let length := mload(_preBytes)
            mstore(tempBytes, length)

            // Maintain a memory counter for the current write location in the
            // temp bytes array by adding the 32 bytes for the array length to
            // the starting location.
            let mc := add(tempBytes, 0x20)
            // Stop copying when the memory counter reaches the length of the
            // first bytes array.
            let end := add(mc, length)

            for {
                // Initialize a copy counter to the start of the _preBytes data,
                // 32 bytes into its memory.
                let cc := add(_preBytes, 0x20)
            } lt(mc, end) {
                // Increase both counters by 32 bytes each iteration.
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                // Write the _preBytes data into the tempBytes memory 32 bytes
                // at a time.
                mstore(mc, mload(cc))
            }

            // Add the length of _postBytes to the current length of tempBytes
            // and store it as the new length in the first 32 bytes of the
            // tempBytes memory.
            length := mload(_postBytes)
            mstore(tempBytes, add(length, mload(tempBytes)))

            // Move the memory counter back from a multiple of 0x20 to the
            // actual end of the _preBytes data.
            mc := end
            // Stop copying when the memory counter reaches the new combined
            // length of the arrays.
            end := add(mc, length)

            for {
                let cc := add(_postBytes, 0x20)
            } lt(mc, end) {
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                mstore(mc, mload(cc))
            }

            // Update the free-memory pointer by padding our last write location
            // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
            // next 32 byte block, then round down to the nearest multiple of
            // 32. If the sum of the length of the two arrays is zero then add
            // one before rounding down to leave a blank 32 bytes (the length block with 0).
            mstore(
                0x40,
                and(
                    add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                    not(31) // Round down to the nearest 32 bytes.
                )
            )
        }

        return tempBytes;
    }

    function slice(
        bytes memory _bytes,
        uint256 _start,
        uint256 _length
    ) internal pure returns (bytes memory) {
        require(_length + 31 >= _length, "slice_overflow");
        require(_bytes.length >= _start + _length, "slice_outOfBounds");

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }
}

File 19 of 22 : DispatchersStorageLib.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.10;

library DispatchersStorageLib {
    function getUint256(bytes32 position) internal view returns (uint256 data) {
        assembly {
            data := sload(position)
        }
    }

    function setUint256(bytes32 position, uint256 data) internal {
        assembly {
            sstore(position, data)
        }
    }

    function getAddress(bytes32 position) internal view returns (address data) {
        assembly {
            data := sload(position)
        }
    }

    function setAddress(bytes32 position, address data) internal {
        assembly {
            sstore(position, data)
        }
    }
}

File 20 of 22 : StakingContractStorageLib.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.10;

library StakingContractStorageLib {
    function getUint256(bytes32 position) internal view returns (uint256 data) {
        assembly {
            data := sload(position)
        }
    }

    function setUint256(bytes32 position, uint256 data) internal {
        assembly {
            sstore(position, data)
        }
    }

    function getAddress(bytes32 position) internal view returns (address data) {
        assembly {
            data := sload(position)
        }
    }

    function setAddress(bytes32 position, address data) internal {
        assembly {
            sstore(position, data)
        }
    }

    function getBool(bytes32 position) internal view returns (bool data) {
        assembly {
            data := sload(position)
        }
    }

    function setBool(bytes32 position, bool data) internal {
        assembly {
            sstore(position, data)
        }
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant VERSION_SLOT = keccak256("StakingContract.version");

    function getVersion() internal view returns (uint256) {
        return getUint256(VERSION_SLOT);
    }

    function setVersion(uint256 _newVersion) internal {
        setUint256(VERSION_SLOT, _newVersion);
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant ADMIN_SLOT = keccak256("StakingContract.admin");
    bytes32 internal constant PENDING_ADMIN_SLOT = keccak256("StakingContract.pendingAdmin");

    function getAdmin() internal view returns (address) {
        return getAddress(ADMIN_SLOT);
    }

    function setAdmin(address _newAdmin) internal {
        setAddress(ADMIN_SLOT, _newAdmin);
    }

    function getPendingAdmin() internal view returns (address) {
        return getAddress(PENDING_ADMIN_SLOT);
    }

    function setPendingAdmin(address _newPendingAdmin) internal {
        setAddress(PENDING_ADMIN_SLOT, _newPendingAdmin);
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant TREASURY_SLOT = keccak256("StakingContract.treasury");

    function getTreasury() internal view returns (address) {
        return getAddress(TREASURY_SLOT);
    }

    function setTreasury(address _newTreasury) internal {
        setAddress(TREASURY_SLOT, _newTreasury);
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant DEPOSIT_CONTRACT_SLOT = keccak256("StakingContract.depositContract");

    function getDepositContract() internal view returns (address) {
        return getAddress(DEPOSIT_CONTRACT_SLOT);
    }

    function setDepositContract(address _newDepositContract) internal {
        setAddress(DEPOSIT_CONTRACT_SLOT, _newDepositContract);
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant OPERATORS_SLOT = keccak256("StakingContract.operators");

    struct OperatorInfo {
        address operator;
        address feeRecipient;
        uint256 limit;
        bytes[] publicKeys;
        bytes[] signatures;
        bool deactivated;
    }

    struct OperatorsSlot {
        OperatorInfo[] value;
    }

    function getOperators() internal pure returns (OperatorsSlot storage p) {
        bytes32 slot = OPERATORS_SLOT;
        assembly {
            p.slot := slot
        }
    }

    /* ========================================
    ===========================================
    =========================================*/

    /// Validator funding information is stored in a packed fashion
    /// We fit 4 vfi per storage slot.
    /// Each vfi is stored in 64 bits, with the following layout:
    /// 32 bits for the number of available keys
    /// 32 bits for the number of funded keys

    uint256 internal constant FUNDED_OFFSET = 32;

    bytes32 internal constant VALIDATORS_FUNDING_INFO_SLOT = keccak256("StakingContract.validatorsFundingInfo");

    struct ValidatorsFundingInfo {
        uint32 availableKeys;
        uint32 funded;
    }

    struct UintToUintMappingSlot {
        mapping(uint256 => uint256) value;
    }

    function getValidatorsFundingInfo(uint256 _index) internal view returns (ValidatorsFundingInfo memory vfi) {
        UintToUintMappingSlot storage p;
        bytes32 slot = VALIDATORS_FUNDING_INFO_SLOT;

        assembly {
            p.slot := slot
        }

        uint256 slotIndex = _index >> 2; // divide by 4
        uint256 innerIndex = (_index & 3) << 6; // modulo 4, multiply by 64
        uint256 value = p.value[slotIndex] >> innerIndex;
        vfi.availableKeys = uint32(value);
        vfi.funded = uint32(value >> FUNDED_OFFSET);
    }

    function setValidatorsFundingInfo(
        uint256 _index,
        uint32 _availableKeys,
        uint32 _funded
    ) internal {
        UintToUintMappingSlot storage p;
        bytes32 slot = VALIDATORS_FUNDING_INFO_SLOT;

        assembly {
            p.slot := slot
        }

        uint256 slotIndex = _index >> 2; // divide by 4
        uint256 innerIndex = (_index & 3) << 6; // modulo 4, multiply by 64
        p.value[slotIndex] =
            (p.value[slotIndex] & (~(uint256(0xFFFFFFFFFFFFFFFF) << innerIndex))) | // clear the bits we want to set
            ((uint256(_availableKeys) | (uint256(_funded) << FUNDED_OFFSET)) << innerIndex);
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant TOTAL_AVAILABLE_VALIDATORS_SLOT = keccak256("StakingContract.totalAvailableValidators");

    function getTotalAvailableValidators() internal view returns (uint256) {
        return getUint256(TOTAL_AVAILABLE_VALIDATORS_SLOT);
    }

    function setTotalAvailableValidators(uint256 _newTotal) internal {
        setUint256(TOTAL_AVAILABLE_VALIDATORS_SLOT, _newTotal);
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant WITHDRAWERS_SLOT = keccak256("StakingContract.withdrawers");

    struct WithdrawersSlot {
        mapping(bytes32 => address) value;
    }

    function getWithdrawers() internal pure returns (WithdrawersSlot storage p) {
        bytes32 slot = WITHDRAWERS_SLOT;
        assembly {
            p.slot := slot
        }
    }

    /* ========================================
    ===========================================
    =========================================*/

    struct OperatorIndex {
        bool enabled;
        uint32 operatorIndex;
    }

    struct OperatorIndexPerValidatorSlot {
        mapping(bytes32 => OperatorIndex) value;
    }

    bytes32 internal constant OPERATOR_INDEX_PER_VALIDATOR_SLOT =
        keccak256("StakingContract.operatorIndexPerValidator");

    function getOperatorIndexPerValidator() internal pure returns (OperatorIndexPerValidatorSlot storage p) {
        bytes32 slot = OPERATOR_INDEX_PER_VALIDATOR_SLOT;
        assembly {
            p.slot := slot
        }
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant GLOBAL_FEE_SLOT = keccak256("StakingContract.globalFee");

    function getGlobalFee() internal view returns (uint256) {
        return getUint256(GLOBAL_FEE_SLOT);
    }

    function setGlobalFee(uint256 _newTreasuryFee) internal {
        setUint256(GLOBAL_FEE_SLOT, _newTreasuryFee);
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant OPERATOR_FEE_SLOT = keccak256("StakingContract.operatorFee");

    function getOperatorFee() internal view returns (uint256) {
        return getUint256(OPERATOR_FEE_SLOT);
    }

    function setOperatorFee(uint256 _newOperatorFee) internal {
        setUint256(OPERATOR_FEE_SLOT, _newOperatorFee);
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant EL_DISPATCHER_SLOT = keccak256("StakingContract.executionLayerDispatcher");

    function getELDispatcher() internal view returns (address) {
        return getAddress(EL_DISPATCHER_SLOT);
    }

    function setELDispatcher(address _newElDispatcher) internal {
        setAddress(EL_DISPATCHER_SLOT, _newElDispatcher);
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant CL_DISPATCHER_SLOT = keccak256("StakingContract.consensusLayerDispatcher");

    function getCLDispatcher() internal view returns (address) {
        return getAddress(CL_DISPATCHER_SLOT);
    }

    function setCLDispatcher(address _newClDispatcher) internal {
        setAddress(CL_DISPATCHER_SLOT, _newClDispatcher);
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant FEE_RECIPIENT_IMPLEMENTATION_SLOT =
        keccak256("StakingContract.feeRecipientImplementation");

    function getFeeRecipientImplementation() internal view returns (address) {
        return getAddress(FEE_RECIPIENT_IMPLEMENTATION_SLOT);
    }

    function setFeeRecipientImplementation(address _newFeeRecipientImplementation) internal {
        setAddress(FEE_RECIPIENT_IMPLEMENTATION_SLOT, _newFeeRecipientImplementation);
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant WITHDRAWER_CUSTOMIZATION_ENABLED_SLOT =
        keccak256("StakingContract.withdrawerCustomizationEnabled");

    function getWithdrawerCustomizationEnabled() internal view returns (bool) {
        return getBool(WITHDRAWER_CUSTOMIZATION_ENABLED_SLOT);
    }

    function setWithdrawerCustomizationEnabled(bool _enabled) internal {
        setBool(WITHDRAWER_CUSTOMIZATION_ENABLED_SLOT, _enabled);
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant EXIT_REQUEST_MAPPING_SLOT =
        bytes32(uint256(keccak256("StakingContract.exitRequest")) - 1);

    struct ExitRequestMap {
        mapping(bytes32 => bool) value;
    }

    function getExitRequestMap() internal pure returns (ExitRequestMap storage p) {
        bytes32 slot = EXIT_REQUEST_MAPPING_SLOT;
        assembly {
            p.slot := slot
        }
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant WITHDRAWN_MAPPING_SLOT = bytes32(uint256(keccak256("StakingContract.withdrawn")) - 1);

    struct WithdrawnMap {
        mapping(bytes32 => bool) value;
    }

    function getWithdrawnMap() internal pure returns (WithdrawnMap storage p) {
        bytes32 slot = WITHDRAWN_MAPPING_SLOT;
        assembly {
            p.slot := slot
        }
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant GLOBAL_COMMISSION_LIMIT_SLOT =
        bytes32(uint256(keccak256("StakingContract.globalCommissionLimit")) - 1);

    function getGlobalCommissionLimit() internal view returns (uint256) {
        return getUint256(GLOBAL_COMMISSION_LIMIT_SLOT);
    }

    function setGlobalCommissionLimit(uint256 value) internal {
        setUint256(GLOBAL_COMMISSION_LIMIT_SLOT, value);
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant OPERATOR_COMMISSION_LIMIT_SLOT =
        bytes32(uint256(keccak256("StakingContract.operatorCommissionLimit")) - 1);

    function getOperatorCommissionLimit() internal view returns (uint256) {
        return getUint256(OPERATOR_COMMISSION_LIMIT_SLOT);
    }

    function setOperatorCommissionLimit(uint256 value) internal {
        setUint256(OPERATOR_COMMISSION_LIMIT_SLOT, value);
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant DEPOSIT_STOPPED_SLOT = bytes32(uint256(keccak256("StakingContract.depositStopped")) - 1);

    function getDepositStopped() internal view returns (bool) {
        return getBool(DEPOSIT_STOPPED_SLOT);
    }

    function setDepositStopped(bool val) internal {
        setBool(DEPOSIT_STOPPED_SLOT, val);
    }

    /* ========================================
    ===========================================
    =========================================*/

    bytes32 internal constant LAST_VALIDATOR_EDIT_SLOT =
        bytes32(uint256(keccak256("StakingContract.lastValidatorsEdit")) - 1);

    function getLastValidatorEdit() internal view returns (uint256) {
        return getUint256(LAST_VALIDATOR_EDIT_SLOT);
    }

    function setLastValidatorEdit(uint256 value) internal {
        setUint256(LAST_VALIDATOR_EDIT_SLOT, value);
    }
}

File 21 of 22 : StakingContract.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.10;

import "./libs/BytesLib.sol";
import "./interfaces/IFeeRecipient.sol";
import "./interfaces/IDepositContract.sol";
import "./libs/StakingContractStorageLib.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";

/// @title Ethereum Staking Contract
/// @author Kiln
/// @notice You can use this contract to store validator keys and have users fund them and trigger deposits.
contract StakingContract {
    using StakingContractStorageLib for bytes32;

    uint256 internal constant EXECUTION_LAYER_SALT_PREFIX = 0;
    uint256 internal constant CONSENSUS_LAYER_SALT_PREFIX = 1;
    uint256 public constant SIGNATURE_LENGTH = 96;
    uint256 public constant PUBLIC_KEY_LENGTH = 48;
    uint256 public constant DEPOSIT_SIZE = 32 ether;
    // this is the equivalent of Uint256Lib.toLittleEndian64(DEPOSIT_SIZE / 1000000000 wei);
    uint256 constant DEPOSIT_SIZE_AMOUNT_LITTLEENDIAN64 =
        0x0040597307000000000000000000000000000000000000000000000000000000;
    uint256 internal constant BASIS_POINTS = 10_000;
    uint256 internal constant WITHDRAWAL_CREDENTIAL_PREFIX_01 =
        0x0100000000000000000000000000000000000000000000000000000000000000;

    error Forbidden();
    error InvalidFee();
    error Deactivated();
    error NoOperators();
    error InvalidCall();
    error Unauthorized();
    error DepositFailure();
    error DepositsStopped();
    error InvalidArgument();
    error UnsortedIndexes();
    error InvalidPublicKeys();
    error InvalidSignatures();
    error InvalidWithdrawer();
    error InvalidZeroAddress();
    error AlreadyInitialized();
    error InvalidDepositValue();
    error NotEnoughValidators();
    error InvalidValidatorCount();
    error DuplicateValidatorKey(bytes);
    error FundedValidatorDeletionAttempt();
    error OperatorLimitTooHigh(uint256 limit, uint256 keyCount);
    error MaximumOperatorCountAlreadyReached();
    error LastEditAfterSnapshot();
    error PublicKeyNotInContract();

    struct ValidatorAllocationCache {
        bool used;
        uint8 operatorIndex;
        uint32 funded;
        uint32 toDeposit;
        uint32 available;
    }

    event Deposit(address indexed caller, address indexed withdrawer, bytes publicKey, bytes signature);
    event ValidatorKeysAdded(uint256 indexed operatorIndex, bytes publicKeys, bytes signatures);
    event ValidatorKeyRemoved(uint256 indexed operatorIndex, bytes publicKey);
    event ChangedWithdrawer(bytes publicKey, address newWithdrawer);
    event ChangedOperatorLimit(uint256 operatorIndex, uint256 limit);
    event ChangedTreasury(address newTreasury);
    event ChangedGlobalFee(uint256 newGlobalFee);
    event ChangedOperatorFee(uint256 newOperatorFee);
    event ChangedAdmin(address newAdmin);
    event ChangedDepositsStopped(bool isStopped);
    event NewOperator(address operatorAddress, address feeRecipientAddress, uint256 index);
    event ChangedOperatorAddresses(uint256 operatorIndex, address operatorAddress, address feeRecipientAddress);
    event DeactivatedOperator(uint256 _operatorIndex);
    event ActivatedOperator(uint256 _operatorIndex);
    event SetWithdrawerCustomizationStatus(bool _status);
    event ExitRequest(address caller, bytes pubkey);
    event ValidatorsEdited(uint256 blockNumber);

    /// @notice Ensures an initialisation call has been called only once per _version value
    /// @param _version The current initialisation value
    modifier init(uint256 _version) {
        if (_version != StakingContractStorageLib.getVersion() + 1) {
            revert AlreadyInitialized();
        }

        StakingContractStorageLib.setVersion(_version);
        _;
    }

    /// @notice Ensures that the caller is the admin
    modifier onlyAdmin() {
        if (msg.sender != StakingContractStorageLib.getAdmin()) {
            revert Unauthorized();
        }

        _;
    }

    /// @notice Ensures that the caller is the admin or the operator
    modifier onlyActiveOperatorOrAdmin(uint256 _operatorIndex) {
        if (msg.sender == StakingContractStorageLib.getAdmin()) {
            _;
        } else {
            _onlyActiveOperator(_operatorIndex);
            _;
        }
    }

    /// @notice Ensures that the caller is the admin
    modifier onlyActiveOperator(uint256 _operatorIndex) {
        _onlyActiveOperator(_operatorIndex);
        _;
    }

    /// @notice Ensures that the caller is the operator fee recipient
    modifier onlyActiveOperatorFeeRecipient(uint256 _operatorIndex) {
        StakingContractStorageLib.OperatorInfo storage operatorInfo = StakingContractStorageLib.getOperators().value[
            _operatorIndex
        ];

        if (operatorInfo.deactivated) {
            revert Deactivated();
        }

        if (msg.sender != operatorInfo.feeRecipient) {
            revert Unauthorized();
        }

        _;
    }

    /// @notice Explicit deposit method using msg.sender
    /// @dev A multiple of 32 ETH should be sent
    function deposit() external payable {
        _deposit();
    }

    /// @notice Implicit deposit method
    /// @dev A multiple of 32 ETH should be sent
    /// @dev The withdrawer is set to the message sender address
    receive() external payable {
        _deposit();
    }

    /// @notice Fallback detection
    /// @dev Fails on any call that fallbacks
    fallback() external payable {
        revert InvalidCall();
    }

    function initialize_1(
        address _admin,
        address _treasury,
        address _depositContract,
        address _elDispatcher,
        address _clDispatcher,
        address _feeRecipientImplementation,
        uint256 _globalFee,
        uint256 _operatorFee,
        uint256 globalCommissionLimitBPS,
        uint256 operatorCommissionLimitBPS
    ) external init(1) {
        _checkAddress(_admin);
        StakingContractStorageLib.setAdmin(_admin);
        _checkAddress(_treasury);
        StakingContractStorageLib.setTreasury(_treasury);

        if (_globalFee > BASIS_POINTS) {
            revert InvalidFee();
        }
        StakingContractStorageLib.setGlobalFee(_globalFee);
        if (_operatorFee > BASIS_POINTS) {
            revert InvalidFee();
        }
        StakingContractStorageLib.setOperatorFee(_operatorFee);

        _checkAddress(_elDispatcher);
        StakingContractStorageLib.setELDispatcher(_elDispatcher);
        _checkAddress(_clDispatcher);
        StakingContractStorageLib.setCLDispatcher(_clDispatcher);
        _checkAddress(_depositContract);
        StakingContractStorageLib.setDepositContract(_depositContract);
        _checkAddress(_feeRecipientImplementation);
        StakingContractStorageLib.setFeeRecipientImplementation(_feeRecipientImplementation);
        initialize_2(globalCommissionLimitBPS, operatorCommissionLimitBPS);
    }

    function initialize_2(uint256 globalCommissionLimitBPS, uint256 operatorCommissionLimitBPS) public init(2) {
        if (globalCommissionLimitBPS > BASIS_POINTS) {
            revert InvalidFee();
        }
        StakingContractStorageLib.setGlobalCommissionLimit(globalCommissionLimitBPS);
        if (operatorCommissionLimitBPS > BASIS_POINTS) {
            revert InvalidFee();
        }
        StakingContractStorageLib.setOperatorCommissionLimit(operatorCommissionLimitBPS);
    }

    /// @notice Changes the behavior of the withdrawer customization logic
    /// @param _enabled True to allow users to customize the withdrawer
    function setWithdrawerCustomizationEnabled(bool _enabled) external onlyAdmin {
        StakingContractStorageLib.setWithdrawerCustomizationEnabled(_enabled);
        emit SetWithdrawerCustomizationStatus(_enabled);
    }

    /// @notice Retrieve system admin
    function getAdmin() external view returns (address) {
        return StakingContractStorageLib.getAdmin();
    }

    /// @notice Set new treasury
    /// @dev Only callable by admin
    /// @param _newTreasury New Treasury address
    function setTreasury(address _newTreasury) external onlyAdmin {
        emit ChangedTreasury(_newTreasury);
        StakingContractStorageLib.setTreasury(_newTreasury);
    }

    /// @notice Retrieve system treasury
    function getTreasury() external view returns (address) {
        return StakingContractStorageLib.getTreasury();
    }

    /// @notice Retrieve the global fee
    function getGlobalFee() external view returns (uint256) {
        return StakingContractStorageLib.getGlobalFee();
    }

    /// @notice Retrieve the operator fee
    function getOperatorFee() external view returns (uint256) {
        return StakingContractStorageLib.getOperatorFee();
    }

    /// @notice Compute the Execution Layer Fee recipient address for a given validator public key
    /// @param _publicKey Validator to get the recipient
    function getELFeeRecipient(bytes calldata _publicKey) external view returns (address) {
        return _getDeterministicReceiver(_publicKey, EXECUTION_LAYER_SALT_PREFIX);
    }

    /// @notice Compute the Consensus Layer Fee recipient address for a given validator public key
    /// @param _publicKey Validator to get the recipient
    function getCLFeeRecipient(bytes calldata _publicKey) external view returns (address) {
        return _getDeterministicReceiver(_publicKey, CONSENSUS_LAYER_SALT_PREFIX);
    }

    /// @notice Retrieve the Execution & Consensus Layer Fee operator recipient for a given public key
    function getOperatorFeeRecipient(bytes32 pubKeyRoot) external view returns (address) {
        if (StakingContractStorageLib.getOperatorIndexPerValidator().value[pubKeyRoot].enabled == false) {
            revert PublicKeyNotInContract();
        }
        return
            StakingContractStorageLib
                .getOperators()
                .value[StakingContractStorageLib.getOperatorIndexPerValidator().value[pubKeyRoot].operatorIndex]
                .feeRecipient;
    }

    /// @notice Retrieve withdrawer of public key
    /// @notice In case the validator is not enabled, it will return address(0)
    /// @param _publicKey Public Key to check
    function getWithdrawer(bytes calldata _publicKey) external view returns (address) {
        return _getWithdrawer(_getPubKeyRoot(_publicKey));
    }

    /// @notice Retrieve withdrawer of public key root
    /// @notice In case the validator is not enabled, it will return address(0)
    /// @param _publicKeyRoot Hash of the public key
    function getWithdrawerFromPublicKeyRoot(bytes32 _publicKeyRoot) external view returns (address) {
        return _getWithdrawer(_publicKeyRoot);
    }

    /// @notice Retrieve whether the validator exit has been requested
    /// @notice In case the validator is not enabled, it will return false
    /// @param _publicKeyRoot Public Key Root to check
    function getExitRequestedFromRoot(bytes32 _publicKeyRoot) external view returns (bool) {
        return _getExitRequest(_publicKeyRoot);
    }

    /// @notice Return true if the validator already went through the exit logic
    /// @notice In case the validator is not enabled, it will return false
    /// @param _publicKeyRoot Public Key Root of the validator
    function getWithdrawnFromPublicKeyRoot(bytes32 _publicKeyRoot) external view returns (bool) {
        return StakingContractStorageLib.getWithdrawnMap().value[_publicKeyRoot];
    }

    /// @notice Retrieve the enabled status of public key root, true if the key is in the contract
    /// @param _publicKeyRoot Hash of the public key
    function getEnabledFromPublicKeyRoot(bytes32 _publicKeyRoot) external view returns (bool) {
        return StakingContractStorageLib.getOperatorIndexPerValidator().value[_publicKeyRoot].enabled;
    }

    /// @notice Allows the CLDispatcher to signal a validator went through the exit logic
    /// @param _publicKeyRoot Public Key Root of the validator
    function toggleWithdrawnFromPublicKeyRoot(bytes32 _publicKeyRoot) external {
        if (msg.sender != StakingContractStorageLib.getCLDispatcher()) {
            revert Unauthorized();
        }
        StakingContractStorageLib.getWithdrawnMap().value[_publicKeyRoot] = true;
    }

    /// @notice Returns false if the users can deposit, true if deposits are stopped
    function getDepositsStopped() external view returns (bool) {
        return StakingContractStorageLib.getDepositStopped();
    }

    /// @notice Retrieve operator details
    /// @param _operatorIndex Operator index
    function getOperator(uint256 _operatorIndex)
        external
        view
        returns (
            address operatorAddress,
            address feeRecipientAddress,
            uint256 limit,
            uint256 keys,
            uint256 funded,
            uint256 available,
            bool deactivated
        )
    {
        StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();
        if (_operatorIndex < operators.value.length) {
            StakingContractStorageLib.ValidatorsFundingInfo memory _operatorInfo = StakingContractStorageLib
                .getValidatorsFundingInfo(_operatorIndex);
            StakingContractStorageLib.OperatorInfo storage _operator = operators.value[_operatorIndex];

            (operatorAddress, feeRecipientAddress, limit, keys, deactivated) = (
                _operator.operator,
                _operator.feeRecipient,
                _operator.limit,
                _operator.publicKeys.length,
                _operator.deactivated
            );
            (funded, available) = (_operatorInfo.funded, _operatorInfo.availableKeys);
        }
    }

    /// @notice Get details about a validator
    /// @param _operatorIndex Index of the operator running the validator
    /// @param _validatorIndex Index of the validator
    function getValidator(uint256 _operatorIndex, uint256 _validatorIndex)
        external
        view
        returns (
            bytes memory publicKey,
            bytes memory signature,
            address withdrawer,
            bool funded
        )
    {
        StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();
        publicKey = operators.value[_operatorIndex].publicKeys[_validatorIndex];
        signature = operators.value[_operatorIndex].signatures[_validatorIndex];
        withdrawer = _getWithdrawer(_getPubKeyRoot(publicKey));
        funded = _validatorIndex < StakingContractStorageLib.getValidatorsFundingInfo(_operatorIndex).funded;
    }

    /// @notice Get the total available keys that are ready to be used for deposits
    function getAvailableValidatorCount() external view returns (uint256) {
        return StakingContractStorageLib.getTotalAvailableValidators();
    }

    /// @notice Set new admin
    /// @dev Only callable by admin
    /// @param _newAdmin New Administrator address
    function transferOwnership(address _newAdmin) external onlyAdmin {
        StakingContractStorageLib.setPendingAdmin(_newAdmin);
    }

    /// @notice New admin must accept its role by calling this method
    /// @dev Only callable by new admin
    function acceptOwnership() external {
        address newAdmin = StakingContractStorageLib.getPendingAdmin();

        if (msg.sender != newAdmin) {
            revert Unauthorized();
        }
        StakingContractStorageLib.setAdmin(newAdmin);
        StakingContractStorageLib.setPendingAdmin(address(0));
        emit ChangedAdmin(newAdmin);
    }

    /// @notice Get the new admin's address previously set for an ownership transfer
    function getPendingAdmin() external view returns (address) {
        return StakingContractStorageLib.getPendingAdmin();
    }

    /// @notice Add new operator
    /// @dev Only callable by admin
    /// @param _operatorAddress Operator address allowed to add / remove validators
    /// @param _feeRecipientAddress Privileged operator address used to manage rewards and operator addresses
    function addOperator(address _operatorAddress, address _feeRecipientAddress) external onlyAdmin returns (uint256) {
        StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();
        StakingContractStorageLib.OperatorInfo memory newOperator;

        if (operators.value.length == 1) {
            revert MaximumOperatorCountAlreadyReached();
        }
        newOperator.operator = _operatorAddress;
        newOperator.feeRecipient = _feeRecipientAddress;
        operators.value.push(newOperator);
        uint256 operatorIndex = operators.value.length - 1;
        emit NewOperator(_operatorAddress, _feeRecipientAddress, operatorIndex);
        return operatorIndex;
    }

    /// @notice Set new operator addresses (operations and reward management)
    /// @dev Only callable by fee recipient address manager
    /// @param _operatorIndex Index of the operator to update
    /// @param _operatorAddress New operator address for operations management
    /// @param _feeRecipientAddress New operator address for reward management
    function setOperatorAddresses(
        uint256 _operatorIndex,
        address _operatorAddress,
        address _feeRecipientAddress
    ) external onlyActiveOperatorFeeRecipient(_operatorIndex) {
        _checkAddress(_operatorAddress);
        _checkAddress(_feeRecipientAddress);
        StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();

        operators.value[_operatorIndex].operator = _operatorAddress;
        operators.value[_operatorIndex].feeRecipient = _feeRecipientAddress;
        emit ChangedOperatorAddresses(_operatorIndex, _operatorAddress, _feeRecipientAddress);
    }

    /// @notice Set withdrawer for public key
    /// @dev Only callable by current public key withdrawer
    /// @param _publicKey Public key to change withdrawer
    /// @param _newWithdrawer New withdrawer address
    function setWithdrawer(bytes calldata _publicKey, address _newWithdrawer) external {
        if (!StakingContractStorageLib.getWithdrawerCustomizationEnabled()) {
            revert Forbidden();
        }
        _checkAddress(_newWithdrawer);
        bytes32 pubkeyRoot = _getPubKeyRoot(_publicKey);
        StakingContractStorageLib.WithdrawersSlot storage withdrawers = StakingContractStorageLib.getWithdrawers();

        if (withdrawers.value[pubkeyRoot] != msg.sender) {
            revert Unauthorized();
        }

        emit ChangedWithdrawer(_publicKey, _newWithdrawer);

        withdrawers.value[pubkeyRoot] = _newWithdrawer;
    }

    /// @notice Set operator staking limits
    /// @dev Only callable by admin
    /// @dev Limit should not exceed the validator key count of the operator
    /// @dev Keys should be registered before limit is increased
    /// @dev Allows all keys to be verified by the system admin before limit is increased
    /// @param _operatorIndex Operator Index
    /// @param _limit New staking limit
    /// @param _snapshot Block number at which verification was done
    function setOperatorLimit(
        uint256 _operatorIndex,
        uint256 _limit,
        uint256 _snapshot
    ) external onlyAdmin {
        StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();
        if (operators.value[_operatorIndex].deactivated) {
            revert Deactivated();
        }
        uint256 publicKeyCount = operators.value[_operatorIndex].publicKeys.length;
        if (publicKeyCount < _limit) {
            revert OperatorLimitTooHigh(_limit, publicKeyCount);
        }
        if (
            operators.value[_operatorIndex].limit < _limit &&
            StakingContractStorageLib.getLastValidatorEdit() > _snapshot
        ) {
            revert LastEditAfterSnapshot();
        }
        operators.value[_operatorIndex].limit = _limit;
        _updateAvailableValidatorCount(_operatorIndex);
        emit ChangedOperatorLimit(_operatorIndex, _limit);
    }

    /// @notice Deactivates an operator and changes the fee recipient address and the staking limit
    /// @param _operatorIndex Operator Index
    /// @param _temporaryFeeRecipient Temporary address to receive funds decided by the system admin
    function deactivateOperator(uint256 _operatorIndex, address _temporaryFeeRecipient) external onlyAdmin {
        StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();
        operators.value[_operatorIndex].limit = 0;
        emit ChangedOperatorLimit(_operatorIndex, 0);
        operators.value[_operatorIndex].deactivated = true;
        emit DeactivatedOperator(_operatorIndex);
        operators.value[_operatorIndex].feeRecipient = _temporaryFeeRecipient;
        emit ChangedOperatorAddresses(_operatorIndex, operators.value[_operatorIndex].operator, _temporaryFeeRecipient);
        _updateAvailableValidatorCount(_operatorIndex);
    }

    /// @notice Activates an operator, without changing its 0 staking limit
    /// @param _operatorIndex Operator Index
    /// @param _newFeeRecipient Sets the fee recipient address
    function activateOperator(uint256 _operatorIndex, address _newFeeRecipient) external onlyAdmin {
        StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();
        operators.value[_operatorIndex].deactivated = false;
        emit ActivatedOperator(_operatorIndex);
        operators.value[_operatorIndex].feeRecipient = _newFeeRecipient;
        emit ChangedOperatorAddresses(_operatorIndex, operators.value[_operatorIndex].operator, _newFeeRecipient);
    }

    /// @notice Change the Operator fee
    /// @param _operatorFee Fee in Basis Point
    function setOperatorFee(uint256 _operatorFee) external onlyAdmin {
        if (_operatorFee > StakingContractStorageLib.getOperatorCommissionLimit()) {
            revert InvalidFee();
        }
        StakingContractStorageLib.setOperatorFee(_operatorFee);
        emit ChangedOperatorFee(_operatorFee);
    }

    /// @notice Change the Global fee
    /// @param _globalFee Fee in Basis Point
    function setGlobalFee(uint256 _globalFee) external onlyAdmin {
        if (_globalFee > StakingContractStorageLib.getGlobalCommissionLimit()) {
            revert InvalidFee();
        }
        StakingContractStorageLib.setGlobalFee(_globalFee);
        emit ChangedGlobalFee(_globalFee);
    }

    /// @notice Add new validator public keys and signatures
    /// @dev Only callable by operator
    /// @param _operatorIndex Operator Index
    /// @param _keyCount Number of keys added
    /// @param _publicKeys Concatenated _keyCount public keys
    /// @param _signatures Concatenated _keyCount signatures
    function addValidators(
        uint256 _operatorIndex,
        uint256 _keyCount,
        bytes calldata _publicKeys,
        bytes calldata _signatures
    ) external onlyActiveOperator(_operatorIndex) {
        if (_keyCount == 0) {
            revert InvalidArgument();
        }

        if (_publicKeys.length % PUBLIC_KEY_LENGTH != 0 || _publicKeys.length / PUBLIC_KEY_LENGTH != _keyCount) {
            revert InvalidPublicKeys();
        }

        if (_signatures.length % SIGNATURE_LENGTH != 0 || _signatures.length / SIGNATURE_LENGTH != _keyCount) {
            revert InvalidSignatures();
        }

        StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();
        StakingContractStorageLib.OperatorIndexPerValidatorSlot
            storage operatorIndexPerValidator = StakingContractStorageLib.getOperatorIndexPerValidator();

        for (uint256 i; i < _keyCount; ) {
            bytes memory publicKey = BytesLib.slice(_publicKeys, i * PUBLIC_KEY_LENGTH, PUBLIC_KEY_LENGTH);
            bytes memory signature = BytesLib.slice(_signatures, i * SIGNATURE_LENGTH, SIGNATURE_LENGTH);

            operators.value[_operatorIndex].publicKeys.push(publicKey);
            operators.value[_operatorIndex].signatures.push(signature);

            bytes32 pubKeyRoot = _getPubKeyRoot(publicKey);

            if (operatorIndexPerValidator.value[pubKeyRoot].enabled) {
                revert DuplicateValidatorKey(publicKey);
            }

            operatorIndexPerValidator.value[pubKeyRoot] = StakingContractStorageLib.OperatorIndex({
                enabled: true,
                operatorIndex: uint32(_operatorIndex)
            });

            unchecked {
                ++i;
            }
        }

        emit ValidatorKeysAdded(_operatorIndex, _publicKeys, _signatures);
        _updateLastValidatorsEdit();
        _updateAvailableValidatorCount(_operatorIndex);
    }

    /// @notice Remove unfunded validators
    /// @dev Only callable by operator
    /// @dev Indexes should be provided in decreasing order
    /// @dev The 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
    /// @param _operatorIndex Operator Index
    /// @param _indexes List of indexes to delete, in decreasing order
    function removeValidators(uint256 _operatorIndex, uint256[] calldata _indexes)
        external
        onlyActiveOperatorOrAdmin(_operatorIndex)
    {
        if (_indexes.length == 0) {
            revert InvalidArgument();
        }

        StakingContractStorageLib.ValidatorsFundingInfo memory operatorInfo = StakingContractStorageLib
            .getValidatorsFundingInfo(_operatorIndex);
        StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();
        StakingContractStorageLib.OperatorIndexPerValidatorSlot
            storage operatorIndexPerValidator = StakingContractStorageLib.getOperatorIndexPerValidator();

        if (_indexes[_indexes.length - 1] < operatorInfo.funded) {
            revert FundedValidatorDeletionAttempt();
        }
        for (uint256 i; i < _indexes.length; ) {
            if (i > 0 && _indexes[i] >= _indexes[i - 1]) {
                revert UnsortedIndexes();
            }

            bytes32 pubKeyRoot = _getPubKeyRoot(operators.value[_operatorIndex].publicKeys[_indexes[i]]);
            operatorIndexPerValidator.value[pubKeyRoot].enabled = false;
            operatorIndexPerValidator.value[pubKeyRoot].operatorIndex = 0;

            emit ValidatorKeyRemoved(_operatorIndex, operators.value[_operatorIndex].publicKeys[_indexes[i]]);
            if (_indexes[i] == operators.value[_operatorIndex].publicKeys.length - 1) {
                operators.value[_operatorIndex].publicKeys.pop();
                operators.value[_operatorIndex].signatures.pop();
            } else {
                operators.value[_operatorIndex].publicKeys[_indexes[i]] = operators.value[_operatorIndex].publicKeys[
                    operators.value[_operatorIndex].publicKeys.length - 1
                ];
                operators.value[_operatorIndex].publicKeys.pop();
                operators.value[_operatorIndex].signatures[_indexes[i]] = operators.value[_operatorIndex].signatures[
                    operators.value[_operatorIndex].signatures.length - 1
                ];
                operators.value[_operatorIndex].signatures.pop();
            }

            unchecked {
                ++i;
            }
        }

        if (_indexes[_indexes.length - 1] < operators.value[_operatorIndex].limit) {
            operators.value[_operatorIndex].limit = _indexes[_indexes.length - 1];
            emit ChangedOperatorLimit(_operatorIndex, _indexes[_indexes.length - 1]);
        }

        _updateLastValidatorsEdit();
        _updateAvailableValidatorCount(_operatorIndex);
    }

    /// @notice Withdraw the Execution Layer Fee for given validators public keys
    /// @dev Funds are sent to the withdrawer account
    /// @dev This method is public on purpose
    /// @param _publicKeys Validators to withdraw Execution Layer Fees from
    function batchWithdrawELFee(bytes calldata _publicKeys) external {
        if (_publicKeys.length % PUBLIC_KEY_LENGTH != 0) {
            revert InvalidPublicKeys();
        }
        for (uint256 i = 0; i < _publicKeys.length; ) {
            bytes memory publicKey = BytesLib.slice(_publicKeys, i, PUBLIC_KEY_LENGTH);
            _onlyWithdrawerOrAdmin(publicKey);
            _deployAndWithdraw(publicKey, EXECUTION_LAYER_SALT_PREFIX, StakingContractStorageLib.getELDispatcher());
            unchecked {
                i += PUBLIC_KEY_LENGTH;
            }
        }
    }

    /// @notice Withdraw the Consensus Layer Fee for given validators public keys
    /// @dev Funds are sent to the withdrawer account
    /// @dev This method is public on purpose
    /// @param _publicKeys Validators to withdraw Consensus Layer Fees from
    function batchWithdrawCLFee(bytes calldata _publicKeys) external {
        if (_publicKeys.length % PUBLIC_KEY_LENGTH != 0) {
            revert InvalidPublicKeys();
        }
        for (uint256 i = 0; i < _publicKeys.length; ) {
            bytes memory publicKey = BytesLib.slice(_publicKeys, i, PUBLIC_KEY_LENGTH);
            _onlyWithdrawerOrAdmin(publicKey);
            _deployAndWithdraw(publicKey, CONSENSUS_LAYER_SALT_PREFIX, StakingContractStorageLib.getCLDispatcher());
            unchecked {
                i += PUBLIC_KEY_LENGTH;
            }
        }
    }

    /// @notice Withdraw both Consensus and Execution Layer Fees for given validators public keys
    /// @dev Funds are sent to the withdrawer account
    /// @param _publicKeys Validators to withdraw fees from
    function batchWithdraw(bytes calldata _publicKeys) external {
        if (_publicKeys.length % PUBLIC_KEY_LENGTH != 0) {
            revert InvalidPublicKeys();
        }
        for (uint256 i = 0; i < _publicKeys.length; ) {
            bytes memory publicKey = BytesLib.slice(_publicKeys, i, PUBLIC_KEY_LENGTH);
            _onlyWithdrawerOrAdmin(publicKey);
            _deployAndWithdraw(publicKey, EXECUTION_LAYER_SALT_PREFIX, StakingContractStorageLib.getELDispatcher());
            _deployAndWithdraw(publicKey, CONSENSUS_LAYER_SALT_PREFIX, StakingContractStorageLib.getCLDispatcher());
            unchecked {
                i += PUBLIC_KEY_LENGTH;
            }
        }
    }

    /// @notice Withdraw the Execution Layer Fee for a given validator public key
    /// @dev Funds are sent to the withdrawer account
    /// @param _publicKey Validator to withdraw Execution Layer Fees from
    function withdrawELFee(bytes calldata _publicKey) external {
        _onlyWithdrawerOrAdmin(_publicKey);
        _deployAndWithdraw(_publicKey, EXECUTION_LAYER_SALT_PREFIX, StakingContractStorageLib.getELDispatcher());
    }

    /// @notice Withdraw the Consensus Layer Fee for a given validator public key
    /// @dev Funds are sent to the withdrawer account
    /// @param _publicKey Validator to withdraw Consensus Layer Fees from
    function withdrawCLFee(bytes calldata _publicKey) external {
        _onlyWithdrawerOrAdmin(_publicKey);
        _deployAndWithdraw(_publicKey, CONSENSUS_LAYER_SALT_PREFIX, StakingContractStorageLib.getCLDispatcher());
    }

    /// @notice Withdraw both Consensus and Execution Layer Fee for a given validator public key
    /// @dev Reverts if any is null
    /// @param _publicKey Validator to withdraw Execution and Consensus Layer Fees from
    function withdraw(bytes calldata _publicKey) external {
        _onlyWithdrawerOrAdmin(_publicKey);
        _deployAndWithdraw(_publicKey, EXECUTION_LAYER_SALT_PREFIX, StakingContractStorageLib.getELDispatcher());
        _deployAndWithdraw(_publicKey, CONSENSUS_LAYER_SALT_PREFIX, StakingContractStorageLib.getCLDispatcher());
    }

    function requestValidatorsExit(bytes calldata _publicKeys) external {
        if (_publicKeys.length % PUBLIC_KEY_LENGTH != 0) {
            revert InvalidPublicKeys();
        }
        for (uint256 i = 0; i < _publicKeys.length; ) {
            bytes memory publicKey = BytesLib.slice(_publicKeys, i, PUBLIC_KEY_LENGTH);
            bytes32 pubKeyRoot = _getPubKeyRoot(publicKey);
            address withdrawer = _getWithdrawer(pubKeyRoot);
            if (msg.sender != withdrawer) {
                revert Unauthorized();
            }
            _setExitRequest(pubKeyRoot, true);
            emit ExitRequest(withdrawer, publicKey);
            unchecked {
                i += PUBLIC_KEY_LENGTH;
            }
        }
    }

    /// @notice Utility to stop or allow deposits
    function setDepositsStopped(bool val) external onlyAdmin {
        emit ChangedDepositsStopped(val);
        StakingContractStorageLib.setDepositStopped(val);
    }

    /// ██ ███    ██ ████████ ███████ ██████  ███    ██  █████  ██
    /// ██ ████   ██    ██    ██      ██   ██ ████   ██ ██   ██ ██
    /// ██ ██ ██  ██    ██    █████   ██████  ██ ██  ██ ███████ ██
    /// ██ ██  ██ ██    ██    ██      ██   ██ ██  ██ ██ ██   ██ ██
    /// ██ ██   ████    ██    ███████ ██   ██ ██   ████ ██   ██ ███████

    function _onlyWithdrawerOrAdmin(bytes memory _publicKey) internal view {
        if (
            msg.sender != _getWithdrawer(_getPubKeyRoot(_publicKey)) &&
            StakingContractStorageLib.getAdmin() != msg.sender
        ) {
            revert InvalidWithdrawer();
        }
    }

    function _onlyActiveOperator(uint256 _operatorIndex) internal view {
        StakingContractStorageLib.OperatorInfo storage operatorInfo = StakingContractStorageLib.getOperators().value[
            _operatorIndex
        ];

        if (operatorInfo.deactivated) {
            revert Deactivated();
        }

        if (msg.sender != operatorInfo.operator) {
            revert Unauthorized();
        }
    }

    function _getPubKeyRoot(bytes memory _publicKey) internal pure returns (bytes32) {
        return sha256(abi.encodePacked(_publicKey, bytes16(0)));
    }

    function _getWithdrawer(bytes32 _publicKeyRoot) internal view returns (address) {
        return StakingContractStorageLib.getWithdrawers().value[_publicKeyRoot];
    }

    function _getExitRequest(bytes32 _publicKeyRoot) internal view returns (bool) {
        return StakingContractStorageLib.getExitRequestMap().value[_publicKeyRoot];
    }

    function _setExitRequest(bytes32 _publicKeyRoot, bool _value) internal {
        StakingContractStorageLib.getExitRequestMap().value[_publicKeyRoot] = _value;
    }

    function _updateAvailableValidatorCount(uint256 _operatorIndex) internal {
        StakingContractStorageLib.ValidatorsFundingInfo memory validatorFundingInfo = StakingContractStorageLib
            .getValidatorsFundingInfo(_operatorIndex);
        StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();

        uint32 oldAvailableCount = validatorFundingInfo.availableKeys;
        uint32 newAvailableCount = 0;
        uint256 cap = operators.value[_operatorIndex].limit;

        if (cap <= validatorFundingInfo.funded) {
            StakingContractStorageLib.setValidatorsFundingInfo(_operatorIndex, 0, validatorFundingInfo.funded);
        } else {
            newAvailableCount = uint32(cap - validatorFundingInfo.funded);
            StakingContractStorageLib.setValidatorsFundingInfo(
                _operatorIndex,
                newAvailableCount,
                validatorFundingInfo.funded
            );
        }

        if (oldAvailableCount != newAvailableCount) {
            StakingContractStorageLib.setTotalAvailableValidators(
                (StakingContractStorageLib.getTotalAvailableValidators() - oldAvailableCount) + newAvailableCount
            );
        }
    }

    function _updateLastValidatorsEdit() internal {
        StakingContractStorageLib.setLastValidatorEdit(block.number);
        emit ValidatorsEdited(block.number);
    }

    function _addressToWithdrawalCredentials(address _recipient) internal pure returns (bytes32) {
        return bytes32(uint256(uint160(_recipient)) + WITHDRAWAL_CREDENTIAL_PREFIX_01);
    }

    function _depositValidatorsOfOperator(uint256 _operatorIndex, uint256 _validatorCount) internal {
        StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();
        StakingContractStorageLib.OperatorInfo storage operator = operators.value[_operatorIndex];
        StakingContractStorageLib.ValidatorsFundingInfo memory vfi = StakingContractStorageLib.getValidatorsFundingInfo(
            _operatorIndex
        );

        for (uint256 i = vfi.funded; i < vfi.funded + _validatorCount; ) {
            bytes memory publicKey = operator.publicKeys[i];
            bytes memory signature = operator.signatures[i];
            address consensusLayerRecipient = _getDeterministicReceiver(publicKey, CONSENSUS_LAYER_SALT_PREFIX);
            bytes32 withdrawalCredentials = _addressToWithdrawalCredentials(consensusLayerRecipient);
            bytes32 pubkeyRoot = _getPubKeyRoot(publicKey);
            _depositValidator(publicKey, pubkeyRoot, signature, withdrawalCredentials);
            StakingContractStorageLib.getWithdrawers().value[pubkeyRoot] = msg.sender;
            emit Deposit(msg.sender, msg.sender, publicKey, signature);
            unchecked {
                ++i;
            }
        }

        StakingContractStorageLib.setValidatorsFundingInfo(
            _operatorIndex,
            uint32(vfi.availableKeys - _validatorCount),
            uint32(vfi.funded + _validatorCount)
        );
    }

    /// @notice Internal utility to deposit a public key, its signature and 32 ETH to the consensus layer
    /// @param _publicKey The Public Key to deposit
    /// @param _signature The Signature to deposit
    /// @param _withdrawalCredentials The Withdrawal Credentials to deposit
    function _depositValidator(
        bytes memory _publicKey,
        bytes32 _pubkeyRoot,
        bytes memory _signature,
        bytes32 _withdrawalCredentials
    ) internal {
        bytes32 signatureRoot = sha256(
            abi.encodePacked(
                sha256(BytesLib.slice(_signature, 0, 64)),
                sha256(abi.encodePacked(BytesLib.slice(_signature, 64, SIGNATURE_LENGTH - 64), bytes32(0)))
            )
        );

        bytes32 depositDataRoot = sha256(
            abi.encodePacked(
                sha256(abi.encodePacked(_pubkeyRoot, _withdrawalCredentials)),
                sha256(abi.encodePacked(DEPOSIT_SIZE_AMOUNT_LITTLEENDIAN64, signatureRoot))
            )
        );

        uint256 targetBalance = address(this).balance - DEPOSIT_SIZE;

        IDepositContract(StakingContractStorageLib.getDepositContract()).deposit{value: DEPOSIT_SIZE}(
            _publicKey,
            abi.encodePacked(_withdrawalCredentials),
            _signature,
            depositDataRoot
        );

        if (address(this).balance != targetBalance) {
            revert DepositFailure();
        }
    }

    function _depositOnOneOperator(uint256 _depositCount, uint256 _totalAvailableValidators) internal {
        StakingContractStorageLib.setTotalAvailableValidators(_totalAvailableValidators - _depositCount);
        _depositValidatorsOfOperator(0, _depositCount);
    }

    function _deposit() internal {
        if (StakingContractStorageLib.getDepositStopped()) {
            revert DepositsStopped();
        }
        if (msg.value == 0 || msg.value % DEPOSIT_SIZE != 0) {
            revert InvalidDepositValue();
        }
        uint256 totalAvailableValidators = StakingContractStorageLib.getTotalAvailableValidators();
        uint256 depositCount = msg.value / DEPOSIT_SIZE;
        if (depositCount > totalAvailableValidators) {
            revert NotEnoughValidators();
        }
        StakingContractStorageLib.OperatorsSlot storage operators = StakingContractStorageLib.getOperators();
        if (operators.value.length == 0) {
            revert NoOperators();
        }
        _depositOnOneOperator(depositCount, totalAvailableValidators);
    }

    function _min(uint256 _a, uint256 _b) internal pure returns (uint256) {
        if (_a < _b) {
            return _a;
        }
        return _b;
    }

    /// @notice Internal utility to compute the receiver deterministic address
    /// @param _publicKey Public Key assigned to the receiver
    /// @param _prefix Prefix used to generate multiple receivers per public key
    function _getDeterministicReceiver(bytes memory _publicKey, uint256 _prefix) internal view returns (address) {
        bytes32 publicKeyRoot = _getPubKeyRoot(_publicKey);
        bytes32 salt = sha256(abi.encodePacked(_prefix, publicKeyRoot));
        address implementation = StakingContractStorageLib.getFeeRecipientImplementation();
        return Clones.predictDeterministicAddress(implementation, salt);
    }

    /// @notice Internal utility to deploy and withdraw the fees from a receiver
    /// @param _publicKey Public Key assigned to the receiver
    /// @param _prefix Prefix used to generate multiple receivers per public key
    /// @param _dispatcher Address of the dispatcher contract
    function _deployAndWithdraw(
        bytes memory _publicKey,
        uint256 _prefix,
        address _dispatcher
    ) internal {
        bytes32 publicKeyRoot = _getPubKeyRoot(_publicKey);
        bytes32 feeRecipientSalt = sha256(abi.encodePacked(_prefix, publicKeyRoot));
        address implementation = StakingContractStorageLib.getFeeRecipientImplementation();
        address feeRecipientAddress = Clones.predictDeterministicAddress(implementation, feeRecipientSalt);
        if (feeRecipientAddress.code.length == 0) {
            Clones.cloneDeterministic(implementation, feeRecipientSalt);
            IFeeRecipient(feeRecipientAddress).init(_dispatcher, publicKeyRoot);
        }
        IFeeRecipient(feeRecipientAddress).withdraw();
    }

    function _checkAddress(address _address) internal pure {
        if (_address == address(0)) {
            revert InvalidZeroAddress();
        }
    }
}

File 22 of 22 : TUPProxy.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.10;

import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

/// @title TUPProxy (Transparent Upgradeable Pausable Proxy)
/// @author SkillZ
/// @notice This contract extends the Transparent Upgradeable proxy and adds a system wide pause feature.
///         When the system is paused, the fallback will fail no matter what calls are made.
contract TUPProxy is TransparentUpgradeableProxy {
    bytes32 private constant _PAUSE_SLOT = bytes32(uint256(keccak256("eip1967.proxy.pause")) - 1);

    error CallWhenPaused();

    constructor(
        address _logic,
        address admin_,
        bytes memory _data
    ) payable TransparentUpgradeableProxy(_logic, admin_, _data) {}

    /// @dev Retrieves Paused state
    /// @return Paused state
    function isPaused() external ifAdmin returns (bool) {
        return StorageSlot.getBooleanSlot(_PAUSE_SLOT).value;
    }

    /// @dev Pauses system
    function pause() external ifAdmin {
        StorageSlot.getBooleanSlot(_PAUSE_SLOT).value = true;
    }

    /// @dev Unpauses system
    function unpause() external ifAdmin {
        StorageSlot.getBooleanSlot(_PAUSE_SLOT).value = false;
    }

    /// @dev Overrides the fallback method to check if system is not paused before
    /// @dev Address Zero is allowed to perform calls even if system is paused. This allows
    /// view functions to be called when the system is paused as rpc providers can easily
    /// set the sender address to zero.
    function _beforeFallback() internal override {
        if (StorageSlot.getBooleanSlot(_PAUSE_SLOT).value == false || msg.sender == address(0)) {
            super._beforeFallback();
        } else {
            revert CallWhenPaused();
        }
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  }
}

Contract Security Audit

Contract 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"}]

Deployed Bytecode

0x6080604052600436106100435760003560e01c806327de9016146100ac5780635f0f28d9146100f55780638e68dce414610117578063ce1206501461012c5761007a565b3661007a576040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fae962d4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3480156100b857600080fd5b506100cc6100c7366004610ab1565b61013f565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b34801561010157600080fd5b50610115610110366004610aef565b610203565b005b34801561012357600080fd5b506100cc6102c6565b61011561013a366004610ab1565b6102f5565b60008061016a7ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a515490565b6040517fa74008010000000000000000000000000000000000000000000000000000000081526004810185905290915073ffffffffffffffffffffffffffffffffffffffff82169063a740080190602401602060405180830381865afa1580156101d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101fc9190610b0c565b9392505050565b600161022d7f6081a8ce97ad8a3951bac5b885ad6097d6b55cb45a6dd62b5069939f8c95ffef5490565b610238906001610b58565b8114610270576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6102997f6081a8ce97ad8a3951bac5b885ad6097d6b55cb45a6dd62b5069939f8c95ffef829055565b6102c27ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a51839055565b5050565b60006102f07ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a515490565b905090565b600061031f7ff0fe62e71ff1ce44ef40d55534c386cf3d375849a5782af5d4e66df449ae3a515490565b905047600081900361035d576040517fb6562b0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fc13d03500000000000000000000000000000000000000000000000000000000081526004810184905260009073ffffffffffffffffffffffffffffffffffffffff84169063c13d035090602401602060405180830381865afa1580156103cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ef9190610b70565b6040517fcac594df0000000000000000000000000000000000000000000000000000000081526004810186905290915060009073ffffffffffffffffffffffffffffffffffffffff85169063cac594df90602401602060405180830381865afa158015610460573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104849190610b70565b90508282801561049d57506801ae361fc1451c00008410155b80156104a7575081155b156105605760006801bc16d674ec80000082116104c457816104cf565b6801bc16d674ec8000005b90506104db8183610b92565b6040517fb86bcaf70000000000000000000000000000000000000000000000000000000081526004810189905290925073ffffffffffffffffffffffffffffffffffffffff87169063b86bcaf790602401600060405180830381600087803b15801561054657600080fd5b505af115801561055a573d6000803e3d6000fd5b50505050505b60006127108673ffffffffffffffffffffffffffffffffffffffff16631bcbfaba6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105d49190610ba9565b6105de9084610bc2565b6105e89190610bff565b905060006127108773ffffffffffffffffffffffffffffffffffffffff1663286966086040518163ffffffff1660e01b8152600401602060405180830381865afa15801561063a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061065e9190610ba9565b6106689084610bc2565b6106729190610bff565b6040517f9adf91ee000000000000000000000000000000000000000000000000000000008152600481018a905290915060009073ffffffffffffffffffffffffffffffffffffffff891690639adf91ee90602401602060405180830381865afa1580156106e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107079190610b0c565b905060008873ffffffffffffffffffffffffffffffffffffffff16633b19e84a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610756573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061077a9190610b0c565b6040517fa7400801000000000000000000000000000000000000000000000000000000008152600481018c905290915060009073ffffffffffffffffffffffffffffffffffffffff8b169063a740080190602401602060405180830381865afa1580156107eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061080f9190610b0c565b905060008073ffffffffffffffffffffffffffffffffffffffff8316610835888d610b92565b604051600081818185875af1925050503d8060008114610871576040519150601f19603f3d011682016040523d82523d6000602084013e610876565b606091505b5090925090508115156000036108c357806040517fce13343d0000000000000000000000000000000000000000000000000000000081526004016108ba9190610c3a565b60405180910390fd5b861561096f5773ffffffffffffffffffffffffffffffffffffffff84166108ea8789610b92565b604051600081818185875af1925050503d8060008114610926576040519150601f19603f3d011682016040523d82523d6000602084013e61092b565b606091505b50909250905081151560000361096f57806040517fbc98622d0000000000000000000000000000000000000000000000000000000081526004016108ba9190610c3a565b8515610a145760405173ffffffffffffffffffffffffffffffffffffffff8616908790600081818185875af1925050503d80600081146109cb576040519150601f19603f3d011682016040523d82523d6000602084013e6109d0565b606091505b509092509050811515600003610a1457806040517fe5ea83e70000000000000000000000000000000000000000000000000000000081526004016108ba9190610c3a565b8473ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f570610cb78811f3a7f90d272791d0a5e71648fb7368280519fc5866e5184db1d8f8a8f610a709190610b92565b8a610a7b818e610b92565b60408051948552602085019390935291830152606082015260800160405180910390a350505050505050505050505050565b9055565b600060208284031215610ac357600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610aec57600080fd5b50565b600060208284031215610b0157600080fd5b81356101fc81610aca565b600060208284031215610b1e57600080fd5b81516101fc81610aca565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115610b6b57610b6b610b29565b500190565b600060208284031215610b8257600080fd5b815180151581146101fc57600080fd5b600082821015610ba457610ba4610b29565b500390565b600060208284031215610bbb57600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615610bfa57610bfa610b29565b500290565b600082610c35577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600060208083528351808285015260005b81811015610c6757858101830151858201604001528201610c4b565b81811115610c79576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201604001939250505056fea26469706673582212202da726a0ebf275a4ea33e21c3cea1b07a08ff132835baf11a00a6a7218c4d46464736f6c634300080d0033

Deployed Bytecode Sourcemap

368:5468:10:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5743:13;;;;;;;;;;;;;;368:5468;5814:13;;;;;;;;;;;;;;5381:312;;;;;;;;;;-1:-1:-1;5381:312:10;;;;;:::i;:::-;;:::i;:::-;;;375:42:22;363:55;;;345:74;;333:2;318:18;5381:312:10;;;;;;;1912:135;;;;;;;;;;-1:-1:-1;1912:135:10;;;;;:::i;:::-;;:::i;:::-;;5105:128;;;;;;;;;;;;;:::i;2118:2927::-;;;;;;:::i;:::-;;:::i;5381:312::-;5451:7;5470:42;5555;1005:62;501:15:20;;385:147;5555:42:10;5624:62;;;;;;;;987:25:22;;;5470:137:10;;-1:-1:-1;5624:46:10;;;;;;960:18:22;;5624:62:10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;5617:69;5381:312;-1:-1:-1;;;5381:312:10:o;1912:135::-;1969:1;1428:25;1167:47;501:15:20;;385:147;1428:25:10;:29;;1456:1;1428:29;:::i;:::-;1416:8;:41;1412:99;;1480:20;;;;;;;;;;;;;;1412:99;1521:33;1167:47;1545:8;341:22:20;;247:132;1521:33:10;1982:58:::1;1005:62;2023:16:::0;341:22:20;;247:132;1982:58:10::1;1912:135:::0;;:::o;5105:128::-;5158:7;5184:42;1005:62;501:15:20;;385:147;5184:42:10;5177:49;;5105:128;:::o;2118:2927::-;2187:42;2272;1005:62;501:15:20;;385:147;2272:42:10;2187:137;-1:-1:-1;2353:21:10;2335:15;2429:12;;;2425:73;;2464:23;;;;;;;;;;;;;;2425:73;2529:56;;;;;;;;987:25:22;;;2508:18:10;;2529:40;;;;;;960:18:22;;2529:56:10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2612:61;;;;;;;;987:25:22;;;2508:77:10;;-1:-1:-1;2595:14:10;;2612:45;;;;;;960:18:22;;2612:61:10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2595:78;-1:-1:-1;2711:7:10;2733:13;:36;;;;;2761:8;2750:7;:19;;2733:36;:50;;;;;2774:9;2773:10;2733:50;2729:594;;;3114:17;3153:8;3134:16;:27;:57;;3175:16;3134:57;;;3164:8;3134:57;3114:77;-1:-1:-1;3205:29:10;3114:77;3205:29;;:::i;:::-;3248:64;;;;;;;;987:25:22;;;3205:29:10;;-1:-1:-1;3248:48:10;;;;;;960:18:22;;3248:64:10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2785:538;2729:594;3805:17;1114:6;3845:15;:28;;;:30;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3826:49;;:16;:49;:::i;:::-;3825:66;;;;:::i;:::-;3805:86;;3901:19;1114:6;3936:15;:30;;;:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3924:44;;:9;:44;:::i;:::-;3923:61;;;;:::i;:::-;4013:55;;;;;;;;987:25:22;;;3901:83:10;;-1:-1:-1;3994:16:10;;4013:39;;;;;;960:18:22;;4013:55:10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3994:74;;4078:16;4097:15;:27;;;:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4157:62;;;;;;;;987:25:22;;;4078:48:10;;-1:-1:-1;4136:18:10;;4157:46;;;;;;960:18:22;;4157:62:10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4136:83;-1:-1:-1;4231:11:10;;4265:15;;;4288:19;4298:9;4288:7;:19;:::i;:::-;4265:47;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;4230:82:10;;-1:-1:-1;4230:82:10;-1:-1:-1;4326:15:10;;;4336:5;4326:15;4322:81;;4387:4;4364:28;;;;;;;;;;;:::i;:::-;;;;;;;;4322:81;4416:13;;4412:210;;4462:13;;;4483:23;4495:11;4483:9;:23;:::i;:::-;4462:49;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;4445:66:10;;-1:-1:-1;4445:66:10;-1:-1:-1;4529:15:10;;;4539:5;4529:15;4525:87;;4592:4;4571:26;;;;;;;;;;;:::i;4525:87::-;4635:15;;4631:204;;4683:37;;:13;;;;4704:11;;4683:37;;;;4704:11;4683:13;:37;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;4666:54:10;;-1:-1:-1;4666:54:10;-1:-1:-1;4738:15:10;;;4748:5;4738:15;4734:91;;4805:4;4780:30;;;;;;;;;;;:::i;4734:91::-;4897:8;4849:189;;4873:10;4849:189;;;4919:14;4957:9;4947:7;:19;;;;:::i;:::-;4980:11;5005:23;4980:11;5005:9;:23;:::i;:::-;4849:189;;;3814:25:22;;;3870:2;3855:18;;3848:34;;;;3898:18;;;3891:34;3956:2;3941:18;;3934:34;3801:3;3786:19;4849:189:10;;;;;;;2177:2868;;;;;;;;;;;;2118:2927;:::o;247:132:20:-;341:22;;247:132::o;14:180:22:-;73:6;126:2;114:9;105:7;101:23;97:32;94:52;;;142:1;139;132:12;94:52;-1:-1:-1;165:23:22;;14:180;-1:-1:-1;14:180:22:o;430:154::-;516:42;509:5;505:54;498:5;495:65;485:93;;574:1;571;564:12;485:93;430:154;:::o;589:247::-;648:6;701:2;689:9;680:7;676:23;672:32;669:52;;;717:1;714;707:12;669:52;756:9;743:23;775:31;800:5;775:31;:::i;1023:251::-;1093:6;1146:2;1134:9;1125:7;1121:23;1117:32;1114:52;;;1162:1;1159;1152:12;1114:52;1194:9;1188:16;1213:31;1238:5;1213:31;:::i;1279:184::-;1331:77;1328:1;1321:88;1428:4;1425:1;1418:15;1452:4;1449:1;1442:15;1468:128;1508:3;1539:1;1535:6;1532:1;1529:13;1526:39;;;1545:18;;:::i;:::-;-1:-1:-1;1581:9:22;;1468:128::o;1601:277::-;1668:6;1721:2;1709:9;1700:7;1696:23;1692:32;1689:52;;;1737:1;1734;1727:12;1689:52;1769:9;1763:16;1822:5;1815:13;1808:21;1801:5;1798:32;1788:60;;1844:1;1841;1834:12;1883:125;1923:4;1951:1;1948;1945:8;1942:34;;;1956:18;;:::i;:::-;-1:-1:-1;1993:9:22;;1883:125::o;2013:184::-;2083:6;2136:2;2124:9;2115:7;2111:23;2107:32;2104:52;;;2152:1;2149;2142:12;2104:52;-1:-1:-1;2175:16:22;;2013:184;-1:-1:-1;2013:184:22:o;2202:228::-;2242:7;2368:1;2300:66;2296:74;2293:1;2290:81;2285:1;2278:9;2271:17;2267:105;2264:131;;;2375:18;;:::i;:::-;-1:-1:-1;2415:9:22;;2202:228::o;2435:274::-;2475:1;2501;2491:189;;2536:77;2533:1;2526:88;2637:4;2634:1;2627:15;2665:4;2662:1;2655:15;2491:189;-1:-1:-1;2694:9:22;;2435:274::o;2924:654::-;3034:4;3063:2;3092;3081:9;3074:21;3124:6;3118:13;3167:6;3162:2;3151:9;3147:18;3140:34;3192:1;3202:140;3216:6;3213:1;3210:13;3202:140;;;3311:14;;;3307:23;;3301:30;3277:17;;;3296:2;3273:26;3266:66;3231:10;;3202:140;;;3360:6;3357:1;3354:13;3351:91;;;3430:1;3425:2;3416:6;3405:9;3401:22;3397:31;3390:42;3351:91;-1:-1:-1;3494:2:22;3482:15;3499:66;3478:88;3463:104;;;;3569:2;3459:113;;2924:654;-1:-1:-1;;;2924:654:22:o

Swarm Source

ipfs://2da726a0ebf275a4ea33e21c3cea1b07a08ff132835baf11a00a6a7218c4d464

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.