Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Latest 22 from a total of 22 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Create Obol Vali... | 574554 | 9 days ago | IN | 0 ETH | 0.00240149 | ||||
Create Obol Vali... | 534653 | 15 days ago | IN | 0 ETH | 0.00238976 | ||||
Create Obol Vali... | 534650 | 15 days ago | IN | 0 ETH | 0.00225192 | ||||
Create Obol Vali... | 534412 | 15 days ago | IN | 0 ETH | 0.00222962 | ||||
Create Obol Vali... | 534405 | 15 days ago | IN | 0 ETH | 0.00214359 | ||||
Create Obol Vali... | 527650 | 16 days ago | IN | 0 ETH | 0.00212138 | ||||
Create Obol Vali... | 526424 | 16 days ago | IN | 0 ETH | 0.00239813 | ||||
Create Obol Vali... | 500951 | 20 days ago | IN | 0 ETH | 0.00235679 | ||||
Create Obol Vali... | 500948 | 20 days ago | IN | 0 ETH | 0.00220264 | ||||
Create Obol Vali... | 500912 | 20 days ago | IN | 0 ETH | 0.00210931 | ||||
Create Obol Vali... | 500909 | 20 days ago | IN | 0 ETH | 0.00241021 | ||||
Create Obol Vali... | 499604 | 20 days ago | IN | 0 ETH | 0.00235479 | ||||
Create Obol Vali... | 499601 | 20 days ago | IN | 0 ETH | 0.00220201 | ||||
Create Obol Vali... | 446970 | 28 days ago | IN | 0 ETH | 0.00206803 | ||||
Create Obol Vali... | 446967 | 28 days ago | IN | 0 ETH | 0.00236502 | ||||
Create Obol Vali... | 446484 | 28 days ago | IN | 0 ETH | 0.00205 | ||||
Create Obol Vali... | 406001 | 34 days ago | IN | 0 ETH | 0.00375841 | ||||
Create Obol Vali... | 307822 | 48 days ago | IN | 0 ETH | 0.00224537 | ||||
Create Obol Vali... | 253038 | 56 days ago | IN | 0 ETH | 0.0021369 | ||||
Create Obol Vali... | 145167 | 72 days ago | IN | 0 ETH | 0.00227905 | ||||
Create Obol Vali... | 145004 | 72 days ago | IN | 0 ETH | 0.00209461 | ||||
Create Obol Vali... | 138143 | 73 days ago | IN | 0 ETH | 0.00240819 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Method | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|---|
0x61012060 | 574554 | 9 days ago | Contract Creation | 0 ETH | |||
0x61012060 | 541531 | 14 days ago | Contract Creation | 0 ETH | |||
Create Obol Vali... | 541531 | 14 days ago | 0 ETH | ||||
0x61012060 | 541527 | 14 days ago | Contract Creation | 0 ETH | |||
Create Obol Vali... | 541527 | 14 days ago | 0 ETH | ||||
0x61012060 | 541521 | 14 days ago | Contract Creation | 0 ETH | |||
Create Obol Vali... | 541521 | 14 days ago | 0 ETH | ||||
0x61012060 | 541518 | 14 days ago | Contract Creation | 0 ETH | |||
Create Obol Vali... | 541518 | 14 days ago | 0 ETH | ||||
0x61012060 | 541515 | 14 days ago | Contract Creation | 0 ETH | |||
Create Obol Vali... | 541515 | 14 days ago | 0 ETH | ||||
0x61012060 | 541510 | 14 days ago | Contract Creation | 0 ETH | |||
Create Obol Vali... | 541510 | 14 days ago | 0 ETH | ||||
0x61012060 | 541507 | 14 days ago | Contract Creation | 0 ETH | |||
Create Obol Vali... | 541507 | 14 days ago | 0 ETH | ||||
0x61012060 | 541506 | 14 days ago | Contract Creation | 0 ETH | |||
Create Obol Vali... | 541506 | 14 days ago | 0 ETH | ||||
0x61012060 | 541481 | 14 days ago | Contract Creation | 0 ETH | |||
Create Obol Vali... | 541481 | 14 days ago | 0 ETH | ||||
0x61012060 | 541478 | 14 days ago | Contract Creation | 0 ETH | |||
Create Obol Vali... | 541478 | 14 days ago | 0 ETH | ||||
0x61012060 | 541469 | 14 days ago | Contract Creation | 0 ETH | |||
Create Obol Vali... | 541469 | 14 days ago | 0 ETH | ||||
0x61012060 | 540694 | 14 days ago | Contract Creation | 0 ETH | |||
Create Obol Vali... | 540694 | 14 days ago | 0 ETH |
Loading...
Loading
Contract Name:
ObolValidatorManagerFactory
Compiler Version
v0.8.19+commit.7dd6d404
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2025-04-04 */ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity =0.8.19 >=0.8.0 ^0.8.19 ^0.8.4; // lib/solady/src/auth/Ownable.sol /// @notice Simple single owner authorization mixin. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol) /// /// @dev Note: /// This implementation does NOT auto-initialize the owner to `msg.sender`. /// You MUST call the `_initializeOwner` in the constructor / initializer. /// /// While the ownable portion follows /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility, /// the nomenclature for the 2-step ownership handover may be unique to this codebase. abstract contract Ownable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The caller is not authorized to call the function. error Unauthorized(); /// @dev The `newOwner` cannot be the zero address. error NewOwnerIsZeroAddress(); /// @dev The `pendingOwner` does not have a valid handover request. error NoHandoverRequest(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ownership is transferred from `oldOwner` to `newOwner`. /// This event is intentionally kept the same as OpenZeppelin's Ownable to be /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173), /// despite it not being as lightweight as a single argument event. event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); /// @dev An ownership handover to `pendingOwner` has been requested. event OwnershipHandoverRequested(address indexed pendingOwner); /// @dev The ownership handover to `pendingOwner` has been canceled. event OwnershipHandoverCanceled(address indexed pendingOwner); /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`. uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE = 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0; /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE = 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d; /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE = 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`. /// It is intentionally chosen to be a high value /// to avoid collision with lower slots. /// The choice of manual storage layout is to enable compatibility /// with both regular and upgradeable contracts. uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8; /// The ownership handover slot of `newOwner` is given by: /// ``` /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED)) /// let handoverSlot := keccak256(0x00, 0x20) /// ``` /// It stores the expiry timestamp of the two-step ownership handover. uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Initializes the owner directly without authorization guard. /// This function must be called upon initialization, /// regardless of whether the contract is upgradeable or not. /// This is to enable generalization to both regular and upgradeable contracts, /// and to save gas in case the initial owner is not the caller. /// For performance reasons, this function will not check if there /// is an existing owner. function _initializeOwner(address newOwner) internal virtual { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(not(_OWNER_SLOT_NOT), newOwner) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } /// @dev Sets the owner directly without authorization guard. function _setOwner(address newOwner) internal virtual { /// @solidity memory-safe-assembly assembly { let ownerSlot := not(_OWNER_SLOT_NOT) // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, newOwner) } } /// @dev Throws if the sender is not the owner. function _checkOwner() internal view virtual { /// @solidity memory-safe-assembly assembly { // If the caller is not the stored owner, revert. if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } /// @dev Returns how long a two-step ownership handover is valid for in seconds. /// Override to return a different value if needed. /// Made internal to conserve bytecode. Wrap it in a public function if needed. function _ownershipHandoverValidFor() internal view virtual returns (uint64) { return 48 * 3600; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC UPDATE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Allows the owner to transfer the ownership to `newOwner`. function transferOwnership(address newOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { if iszero(shl(96, newOwner)) { mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`. revert(0x1c, 0x04) } } _setOwner(newOwner); } /// @dev Allows the owner to renounce their ownership. function renounceOwnership() public payable virtual onlyOwner { _setOwner(address(0)); } /// @dev Request a two-step ownership handover to the caller. /// The request will automatically expire in 48 hours (172800 seconds) by default. function requestOwnershipHandover() public payable virtual { unchecked { uint256 expires = block.timestamp + _ownershipHandoverValidFor(); /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to `expires`. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), expires) // Emit the {OwnershipHandoverRequested} event. log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller()) } } } /// @dev Cancels the two-step ownership handover to the caller, if any. function cancelOwnershipHandover() public payable virtual { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), 0) // Emit the {OwnershipHandoverCanceled} event. log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller()) } } /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`. /// Reverts if there is no existing ownership handover requested by `pendingOwner`. function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) let handoverSlot := keccak256(0x0c, 0x20) // If the handover does not exist, or has expired. if gt(timestamp(), sload(handoverSlot)) { mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`. revert(0x1c, 0x04) } // Set the handover slot to 0. sstore(handoverSlot, 0) } _setOwner(pendingOwner); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC READ FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the owner of the contract. function owner() public view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { result := sload(not(_OWNER_SLOT_NOT)) } } /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`. function ownershipHandoverExpiresAt(address pendingOwner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Compute the handover slot. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) // Load the handover slot. result := sload(keccak256(0x0c, 0x20)) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MODIFIERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Marks a function as only callable by the owner. modifier onlyOwner() virtual { _checkOwner(); _; } } // lib/solady/src/utils/SafeTransferLib.sol /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// /// @dev Note: /// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection. /// - For ERC20s, this implementation won't check that a token has code, /// responsibility is delegated to the caller. library SafeTransferLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ETH transfer has failed. error ETHTransferFailed(); /// @dev The ERC20 `transferFrom` has failed. error TransferFromFailed(); /// @dev The ERC20 `transfer` has failed. error TransferFailed(); /// @dev The ERC20 `approve` has failed. error ApproveFailed(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes. uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300; /// @dev Suggested gas stipend for contract receiving ETH to perform a few /// storage reads and writes, but low enough to prevent griefing. uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ETH OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants. // // The regular variants: // - Forwards all remaining gas to the target. // - Reverts if the target reverts. // - Reverts if the current contract has insufficient balance. // // The force variants: // - Forwards with an optional gas stipend // (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases). // - If the target reverts, or if the gas stipend is exhausted, // creates a temporary contract to force send the ETH via `SELFDESTRUCT`. // Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758. // - Reverts if the current contract has insufficient balance. // // The try variants: // - Forwards with a mandatory gas stipend. // - Instead of reverting, returns whether the transfer succeeded. /// @dev Sends `amount` (in wei) ETH to `to`. function safeTransferETH(address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { if iszero(call(gas(), to, amount, gas(), 0x00, gas(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } } } /// @dev Sends all the ETH in the current contract to `to`. function safeTransferAllETH(address to) internal { /// @solidity memory-safe-assembly assembly { // Transfer all the ETH and check if it succeeded or not. if iszero(call(gas(), to, selfbalance(), gas(), 0x00, gas(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`. function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal { /// @solidity memory-safe-assembly assembly { if lt(selfbalance(), amount) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } if iszero(call(gasStipend, to, amount, gas(), 0x00, gas(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(amount, 0x0b, 0x16)) { returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation. } } } } /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`. function forceSafeTransferAllETH(address to, uint256 gasStipend) internal { /// @solidity memory-safe-assembly assembly { if iszero(call(gasStipend, to, selfbalance(), gas(), 0x00, gas(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(selfbalance(), 0x0b, 0x16)) { returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation. } } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`. function forceSafeTransferETH(address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { if lt(selfbalance(), amount) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, gas(), 0x00, gas(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(amount, 0x0b, 0x16)) { returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation. } } } } /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`. function forceSafeTransferAllETH(address to) internal { /// @solidity memory-safe-assembly assembly { if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), gas(), 0x00, gas(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(selfbalance(), 0x0b, 0x16)) { returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation. } } } } /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`. function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal returns (bool success) { /// @solidity memory-safe-assembly assembly { success := call(gasStipend, to, amount, gas(), 0x00, gas(), 0x00) } } /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`. function trySafeTransferAllETH(address to, uint256 gasStipend) internal returns (bool success) { /// @solidity memory-safe-assembly assembly { success := call(gasStipend, to, selfbalance(), gas(), 0x00, gas(), 0x00) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Sends `amount` of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for /// the current contract to manage. function safeTransferFrom(address token, address from, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, amount) // Store the `amount` argument. mstore(0x40, to) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`. // Perform the transfer, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { mstore(0x00, 0x7939f424) // `TransferFromFailed()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends all of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have their entire balance approved for /// the current contract to manage. function safeTransferAllFrom(address token, address from, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x40, to) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`. // Read the balance, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20) ) ) { mstore(0x00, 0x7939f424) // `TransferFromFailed()`. revert(0x1c, 0x04) } mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`. amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it. // Perform the transfer, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { mstore(0x00, 0x7939f424) // `TransferFromFailed()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransfer(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`. // Perform the transfer, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x00, 0x90b8ec18) // `TransferFailed()`. revert(0x1c, 0x04) } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Sends all of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransferAll(address token, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. mstore(0x20, address()) // Store the address of the current contract. // Read the balance, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20) ) ) { mstore(0x00, 0x90b8ec18) // `TransferFailed()`. revert(0x1c, 0x04) } mstore(0x14, to) // Store the `to` argument. amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it. mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`. // Perform the transfer, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x00, 0x90b8ec18) // `TransferFailed()`. revert(0x1c, 0x04) } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. /// Reverts upon failure. function safeApprove(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. // Perform the approval, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`. revert(0x1c, 0x04) } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. /// If the initial attempt to approve fails, attempts to reset the approved amount to zero, /// then retries the approval again (some tokens, e.g. USDT, requires this). /// Reverts upon failure. function safeApproveWithRetry(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. // Perform the approval, retrying upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x34, 0) // Store 0 for the `amount`. mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. pop(call(gas(), token, 0, 0x10, 0x44, 0x00, 0x00)) // Reset the approval. mstore(0x34, amount) // Store back the original `amount`. // Retry the approval, reverting upon failure. if iszero( and( or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`. revert(0x1c, 0x04) } } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Returns the amount of ERC20 `token` owned by `account`. /// Returns zero if the `token` does not exist. function balanceOf(address token, address account) internal view returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x14, account) // Store the `account` argument. mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`. amount := mul( mload(0x20), and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20) ) ) } } } // lib/solmate/src/tokens/ERC20.sol /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } } // src/interfaces/IDepositContract.sol // ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━ // ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓ // ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛ // ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━ // ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓ // ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // pragma solidity 0.6.11 was the original version // This interface is designed to be compatible with the Vyper version. /// @notice This is the Ethereum 2.0 deposit contract interface. /// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs interface IDepositContract { /// @notice A processed deposit event. event DepositEvent( bytes pubkey, bytes withdrawal_credentials, bytes amount, bytes signature, bytes index ); /// @notice Submit a Phase 0 DepositData object. /// @param pubkey A BLS12-381 public key. /// @param withdrawal_credentials Commitment to a public key for withdrawals. /// @param signature A BLS12-381 signature. /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object. /// Used as a protection against malformed input. function deposit( bytes calldata pubkey, bytes calldata withdrawal_credentials, bytes calldata signature, bytes32 deposit_data_root ) external payable; /// @notice Query the current deposit root hash. /// @return The deposit root hash. function get_deposit_root() external view returns (bytes32); /// @notice Query the current deposit count. /// @return The deposit count encoded as a little endian 64-bit number. function get_deposit_count() external view returns (bytes memory); } // src/interfaces/IENSReverseRegistrar.sol interface IENSReverseRegistrar { function claim(address owner) external returns (bytes32); function defaultResolver() external view returns (address); function ens() external view returns (address); function node(address addr) external pure returns (bytes32); function setName(string memory name) external returns (bytes32); } // lib/solady/src/auth/OwnableRoles.sol /// @notice Simple single owner and multiroles authorization mixin. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol) /// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173) /// for compatibility, the nomenclature for the 2-step ownership handover and roles /// may be unique to this codebase. abstract contract OwnableRoles is Ownable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The `user`'s roles is updated to `roles`. /// Each bit of `roles` represents whether the role is set. event RolesUpdated(address indexed user, uint256 indexed roles); /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`. uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE = 0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The role slot of `user` is given by: /// ``` /// mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED)) /// let roleSlot := keccak256(0x00, 0x20) /// ``` /// This automatically ignores the upper bits of the `user` in case /// they are not clean, as well as keep the `keccak256` under 32-bytes. /// /// Note: This is equal to `_OWNER_SLOT_NOT` in for gas efficiency. uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Overwrite the roles directly without authorization guard. function _setRoles(address user, uint256 roles) internal virtual { /// @solidity memory-safe-assembly assembly { mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, user) // Store the new value. sstore(keccak256(0x0c, 0x20), roles) // Emit the {RolesUpdated} event. log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles) } } /// @dev Updates the roles directly without authorization guard. /// If `on` is true, each set bit of `roles` will be turned on, /// otherwise, each set bit of `roles` will be turned off. function _updateRoles(address user, uint256 roles, bool on) internal virtual { /// @solidity memory-safe-assembly assembly { mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, user) let roleSlot := keccak256(0x0c, 0x20) // Load the current value. let current := sload(roleSlot) // Compute the updated roles if `on` is true. let updated := or(current, roles) // Compute the updated roles if `on` is false. // Use `and` to compute the intersection of `current` and `roles`, // `xor` it with `current` to flip the bits in the intersection. if iszero(on) { updated := xor(current, and(current, roles)) } // Then, store the new value. sstore(roleSlot, updated) // Emit the {RolesUpdated} event. log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), updated) } } /// @dev Grants the roles directly without authorization guard. /// Each bit of `roles` represents the role to turn on. function _grantRoles(address user, uint256 roles) internal virtual { _updateRoles(user, roles, true); } /// @dev Removes the roles directly without authorization guard. /// Each bit of `roles` represents the role to turn off. function _removeRoles(address user, uint256 roles) internal virtual { _updateRoles(user, roles, false); } /// @dev Throws if the sender does not have any of the `roles`. function _checkRoles(uint256 roles) internal view virtual { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, caller()) // Load the stored value, and if the `and` intersection // of the value and `roles` is zero, revert. if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } /// @dev Throws if the sender is not the owner, /// and does not have any of the `roles`. /// Checks for ownership first, then lazily checks for roles. function _checkOwnerOrRoles(uint256 roles) internal view virtual { /// @solidity memory-safe-assembly assembly { // If the caller is not the stored owner. // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`. if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, caller()) // Load the stored value, and if the `and` intersection // of the value and `roles` is zero, revert. if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } } /// @dev Throws if the sender does not have any of the `roles`, /// and is not the owner. /// Checks for roles first, then lazily checks for ownership. function _checkRolesOrOwner(uint256 roles) internal view virtual { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, caller()) // Load the stored value, and if the `and` intersection // of the value and `roles` is zero, revert. if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { // If the caller is not the stored owner. // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`. if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } } /// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`. /// This is meant for frontends like Etherscan, and is therefore not fully optimized. /// Not recommended to be called on-chain. /// Made internal to conserve bytecode. Wrap it in a public function if needed. function _rolesFromOrdinals(uint8[] memory ordinals) internal pure returns (uint256 roles) { /// @solidity memory-safe-assembly assembly { for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } { // We don't need to mask the values of `ordinals`, as Solidity // cleans dirty upper bits when storing variables into memory. roles := or(shl(mload(add(ordinals, i)), 1), roles) } } } /// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap. /// This is meant for frontends like Etherscan, and is therefore not fully optimized. /// Not recommended to be called on-chain. /// Made internal to conserve bytecode. Wrap it in a public function if needed. function _ordinalsFromRoles(uint256 roles) internal pure returns (uint8[] memory ordinals) { /// @solidity memory-safe-assembly assembly { // Grab the pointer to the free memory. ordinals := mload(0x40) let ptr := add(ordinals, 0x20) let o := 0 // The absence of lookup tables, De Bruijn, etc., here is intentional for // smaller bytecode, as this function is not meant to be called on-chain. for { let t := roles } 1 {} { mstore(ptr, o) // `shr` 5 is equivalent to multiplying by 0x20. // Push back into the ordinals array if the bit is set. ptr := add(ptr, shl(5, and(t, 1))) o := add(o, 1) t := shr(o, roles) if iszero(t) { break } } // Store the length of `ordinals`. mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20)))) // Allocate the memory. mstore(0x40, ptr) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC UPDATE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Allows the owner to grant `user` `roles`. /// If the `user` already has a role, then it will be an no-op for the role. function grantRoles(address user, uint256 roles) public payable virtual onlyOwner { _grantRoles(user, roles); } /// @dev Allows the owner to remove `user` `roles`. /// If the `user` does not have a role, then it will be an no-op for the role. function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner { _removeRoles(user, roles); } /// @dev Allow the caller to remove their own roles. /// If the caller does not have a role, then it will be an no-op for the role. function renounceRoles(uint256 roles) public payable virtual { _removeRoles(msg.sender, roles); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC READ FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the roles of `user`. function rolesOf(address user) public view virtual returns (uint256 roles) { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, user) // Load the stored value. roles := sload(keccak256(0x0c, 0x20)) } } /// @dev Returns whether `user` has any of `roles`. function hasAnyRole(address user, uint256 roles) public view virtual returns (bool) { return rolesOf(user) & roles != 0; } /// @dev Returns whether `user` has all of `roles`. function hasAllRoles(address user, uint256 roles) public view virtual returns (bool) { return rolesOf(user) & roles == roles; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MODIFIERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Marks a function as only callable by an account with `roles`. modifier onlyRoles(uint256 roles) virtual { _checkRoles(roles); _; } /// @dev Marks a function as only callable by the owner or by an account /// with `roles`. Checks for ownership first, then lazily checks for roles. modifier onlyOwnerOrRoles(uint256 roles) virtual { _checkOwnerOrRoles(roles); _; } /// @dev Marks a function as only callable by an account with `roles` /// or the owner. Checks for roles first, then lazily checks for ownership. modifier onlyRolesOrOwner(uint256 roles) virtual { _checkRolesOrOwner(roles); _; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ROLE CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // IYKYK uint256 internal constant _ROLE_0 = 1 << 0; uint256 internal constant _ROLE_1 = 1 << 1; uint256 internal constant _ROLE_2 = 1 << 2; uint256 internal constant _ROLE_3 = 1 << 3; uint256 internal constant _ROLE_4 = 1 << 4; uint256 internal constant _ROLE_5 = 1 << 5; uint256 internal constant _ROLE_6 = 1 << 6; uint256 internal constant _ROLE_7 = 1 << 7; uint256 internal constant _ROLE_8 = 1 << 8; uint256 internal constant _ROLE_9 = 1 << 9; uint256 internal constant _ROLE_10 = 1 << 10; uint256 internal constant _ROLE_11 = 1 << 11; uint256 internal constant _ROLE_12 = 1 << 12; uint256 internal constant _ROLE_13 = 1 << 13; uint256 internal constant _ROLE_14 = 1 << 14; uint256 internal constant _ROLE_15 = 1 << 15; uint256 internal constant _ROLE_16 = 1 << 16; uint256 internal constant _ROLE_17 = 1 << 17; uint256 internal constant _ROLE_18 = 1 << 18; uint256 internal constant _ROLE_19 = 1 << 19; uint256 internal constant _ROLE_20 = 1 << 20; uint256 internal constant _ROLE_21 = 1 << 21; uint256 internal constant _ROLE_22 = 1 << 22; uint256 internal constant _ROLE_23 = 1 << 23; uint256 internal constant _ROLE_24 = 1 << 24; uint256 internal constant _ROLE_25 = 1 << 25; uint256 internal constant _ROLE_26 = 1 << 26; uint256 internal constant _ROLE_27 = 1 << 27; uint256 internal constant _ROLE_28 = 1 << 28; uint256 internal constant _ROLE_29 = 1 << 29; uint256 internal constant _ROLE_30 = 1 << 30; uint256 internal constant _ROLE_31 = 1 << 31; uint256 internal constant _ROLE_32 = 1 << 32; uint256 internal constant _ROLE_33 = 1 << 33; uint256 internal constant _ROLE_34 = 1 << 34; uint256 internal constant _ROLE_35 = 1 << 35; uint256 internal constant _ROLE_36 = 1 << 36; uint256 internal constant _ROLE_37 = 1 << 37; uint256 internal constant _ROLE_38 = 1 << 38; uint256 internal constant _ROLE_39 = 1 << 39; uint256 internal constant _ROLE_40 = 1 << 40; uint256 internal constant _ROLE_41 = 1 << 41; uint256 internal constant _ROLE_42 = 1 << 42; uint256 internal constant _ROLE_43 = 1 << 43; uint256 internal constant _ROLE_44 = 1 << 44; uint256 internal constant _ROLE_45 = 1 << 45; uint256 internal constant _ROLE_46 = 1 << 46; uint256 internal constant _ROLE_47 = 1 << 47; uint256 internal constant _ROLE_48 = 1 << 48; uint256 internal constant _ROLE_49 = 1 << 49; uint256 internal constant _ROLE_50 = 1 << 50; uint256 internal constant _ROLE_51 = 1 << 51; uint256 internal constant _ROLE_52 = 1 << 52; uint256 internal constant _ROLE_53 = 1 << 53; uint256 internal constant _ROLE_54 = 1 << 54; uint256 internal constant _ROLE_55 = 1 << 55; uint256 internal constant _ROLE_56 = 1 << 56; uint256 internal constant _ROLE_57 = 1 << 57; uint256 internal constant _ROLE_58 = 1 << 58; uint256 internal constant _ROLE_59 = 1 << 59; uint256 internal constant _ROLE_60 = 1 << 60; uint256 internal constant _ROLE_61 = 1 << 61; uint256 internal constant _ROLE_62 = 1 << 62; uint256 internal constant _ROLE_63 = 1 << 63; uint256 internal constant _ROLE_64 = 1 << 64; uint256 internal constant _ROLE_65 = 1 << 65; uint256 internal constant _ROLE_66 = 1 << 66; uint256 internal constant _ROLE_67 = 1 << 67; uint256 internal constant _ROLE_68 = 1 << 68; uint256 internal constant _ROLE_69 = 1 << 69; uint256 internal constant _ROLE_70 = 1 << 70; uint256 internal constant _ROLE_71 = 1 << 71; uint256 internal constant _ROLE_72 = 1 << 72; uint256 internal constant _ROLE_73 = 1 << 73; uint256 internal constant _ROLE_74 = 1 << 74; uint256 internal constant _ROLE_75 = 1 << 75; uint256 internal constant _ROLE_76 = 1 << 76; uint256 internal constant _ROLE_77 = 1 << 77; uint256 internal constant _ROLE_78 = 1 << 78; uint256 internal constant _ROLE_79 = 1 << 79; uint256 internal constant _ROLE_80 = 1 << 80; uint256 internal constant _ROLE_81 = 1 << 81; uint256 internal constant _ROLE_82 = 1 << 82; uint256 internal constant _ROLE_83 = 1 << 83; uint256 internal constant _ROLE_84 = 1 << 84; uint256 internal constant _ROLE_85 = 1 << 85; uint256 internal constant _ROLE_86 = 1 << 86; uint256 internal constant _ROLE_87 = 1 << 87; uint256 internal constant _ROLE_88 = 1 << 88; uint256 internal constant _ROLE_89 = 1 << 89; uint256 internal constant _ROLE_90 = 1 << 90; uint256 internal constant _ROLE_91 = 1 << 91; uint256 internal constant _ROLE_92 = 1 << 92; uint256 internal constant _ROLE_93 = 1 << 93; uint256 internal constant _ROLE_94 = 1 << 94; uint256 internal constant _ROLE_95 = 1 << 95; uint256 internal constant _ROLE_96 = 1 << 96; uint256 internal constant _ROLE_97 = 1 << 97; uint256 internal constant _ROLE_98 = 1 << 98; uint256 internal constant _ROLE_99 = 1 << 99; uint256 internal constant _ROLE_100 = 1 << 100; uint256 internal constant _ROLE_101 = 1 << 101; uint256 internal constant _ROLE_102 = 1 << 102; uint256 internal constant _ROLE_103 = 1 << 103; uint256 internal constant _ROLE_104 = 1 << 104; uint256 internal constant _ROLE_105 = 1 << 105; uint256 internal constant _ROLE_106 = 1 << 106; uint256 internal constant _ROLE_107 = 1 << 107; uint256 internal constant _ROLE_108 = 1 << 108; uint256 internal constant _ROLE_109 = 1 << 109; uint256 internal constant _ROLE_110 = 1 << 110; uint256 internal constant _ROLE_111 = 1 << 111; uint256 internal constant _ROLE_112 = 1 << 112; uint256 internal constant _ROLE_113 = 1 << 113; uint256 internal constant _ROLE_114 = 1 << 114; uint256 internal constant _ROLE_115 = 1 << 115; uint256 internal constant _ROLE_116 = 1 << 116; uint256 internal constant _ROLE_117 = 1 << 117; uint256 internal constant _ROLE_118 = 1 << 118; uint256 internal constant _ROLE_119 = 1 << 119; uint256 internal constant _ROLE_120 = 1 << 120; uint256 internal constant _ROLE_121 = 1 << 121; uint256 internal constant _ROLE_122 = 1 << 122; uint256 internal constant _ROLE_123 = 1 << 123; uint256 internal constant _ROLE_124 = 1 << 124; uint256 internal constant _ROLE_125 = 1 << 125; uint256 internal constant _ROLE_126 = 1 << 126; uint256 internal constant _ROLE_127 = 1 << 127; uint256 internal constant _ROLE_128 = 1 << 128; uint256 internal constant _ROLE_129 = 1 << 129; uint256 internal constant _ROLE_130 = 1 << 130; uint256 internal constant _ROLE_131 = 1 << 131; uint256 internal constant _ROLE_132 = 1 << 132; uint256 internal constant _ROLE_133 = 1 << 133; uint256 internal constant _ROLE_134 = 1 << 134; uint256 internal constant _ROLE_135 = 1 << 135; uint256 internal constant _ROLE_136 = 1 << 136; uint256 internal constant _ROLE_137 = 1 << 137; uint256 internal constant _ROLE_138 = 1 << 138; uint256 internal constant _ROLE_139 = 1 << 139; uint256 internal constant _ROLE_140 = 1 << 140; uint256 internal constant _ROLE_141 = 1 << 141; uint256 internal constant _ROLE_142 = 1 << 142; uint256 internal constant _ROLE_143 = 1 << 143; uint256 internal constant _ROLE_144 = 1 << 144; uint256 internal constant _ROLE_145 = 1 << 145; uint256 internal constant _ROLE_146 = 1 << 146; uint256 internal constant _ROLE_147 = 1 << 147; uint256 internal constant _ROLE_148 = 1 << 148; uint256 internal constant _ROLE_149 = 1 << 149; uint256 internal constant _ROLE_150 = 1 << 150; uint256 internal constant _ROLE_151 = 1 << 151; uint256 internal constant _ROLE_152 = 1 << 152; uint256 internal constant _ROLE_153 = 1 << 153; uint256 internal constant _ROLE_154 = 1 << 154; uint256 internal constant _ROLE_155 = 1 << 155; uint256 internal constant _ROLE_156 = 1 << 156; uint256 internal constant _ROLE_157 = 1 << 157; uint256 internal constant _ROLE_158 = 1 << 158; uint256 internal constant _ROLE_159 = 1 << 159; uint256 internal constant _ROLE_160 = 1 << 160; uint256 internal constant _ROLE_161 = 1 << 161; uint256 internal constant _ROLE_162 = 1 << 162; uint256 internal constant _ROLE_163 = 1 << 163; uint256 internal constant _ROLE_164 = 1 << 164; uint256 internal constant _ROLE_165 = 1 << 165; uint256 internal constant _ROLE_166 = 1 << 166; uint256 internal constant _ROLE_167 = 1 << 167; uint256 internal constant _ROLE_168 = 1 << 168; uint256 internal constant _ROLE_169 = 1 << 169; uint256 internal constant _ROLE_170 = 1 << 170; uint256 internal constant _ROLE_171 = 1 << 171; uint256 internal constant _ROLE_172 = 1 << 172; uint256 internal constant _ROLE_173 = 1 << 173; uint256 internal constant _ROLE_174 = 1 << 174; uint256 internal constant _ROLE_175 = 1 << 175; uint256 internal constant _ROLE_176 = 1 << 176; uint256 internal constant _ROLE_177 = 1 << 177; uint256 internal constant _ROLE_178 = 1 << 178; uint256 internal constant _ROLE_179 = 1 << 179; uint256 internal constant _ROLE_180 = 1 << 180; uint256 internal constant _ROLE_181 = 1 << 181; uint256 internal constant _ROLE_182 = 1 << 182; uint256 internal constant _ROLE_183 = 1 << 183; uint256 internal constant _ROLE_184 = 1 << 184; uint256 internal constant _ROLE_185 = 1 << 185; uint256 internal constant _ROLE_186 = 1 << 186; uint256 internal constant _ROLE_187 = 1 << 187; uint256 internal constant _ROLE_188 = 1 << 188; uint256 internal constant _ROLE_189 = 1 << 189; uint256 internal constant _ROLE_190 = 1 << 190; uint256 internal constant _ROLE_191 = 1 << 191; uint256 internal constant _ROLE_192 = 1 << 192; uint256 internal constant _ROLE_193 = 1 << 193; uint256 internal constant _ROLE_194 = 1 << 194; uint256 internal constant _ROLE_195 = 1 << 195; uint256 internal constant _ROLE_196 = 1 << 196; uint256 internal constant _ROLE_197 = 1 << 197; uint256 internal constant _ROLE_198 = 1 << 198; uint256 internal constant _ROLE_199 = 1 << 199; uint256 internal constant _ROLE_200 = 1 << 200; uint256 internal constant _ROLE_201 = 1 << 201; uint256 internal constant _ROLE_202 = 1 << 202; uint256 internal constant _ROLE_203 = 1 << 203; uint256 internal constant _ROLE_204 = 1 << 204; uint256 internal constant _ROLE_205 = 1 << 205; uint256 internal constant _ROLE_206 = 1 << 206; uint256 internal constant _ROLE_207 = 1 << 207; uint256 internal constant _ROLE_208 = 1 << 208; uint256 internal constant _ROLE_209 = 1 << 209; uint256 internal constant _ROLE_210 = 1 << 210; uint256 internal constant _ROLE_211 = 1 << 211; uint256 internal constant _ROLE_212 = 1 << 212; uint256 internal constant _ROLE_213 = 1 << 213; uint256 internal constant _ROLE_214 = 1 << 214; uint256 internal constant _ROLE_215 = 1 << 215; uint256 internal constant _ROLE_216 = 1 << 216; uint256 internal constant _ROLE_217 = 1 << 217; uint256 internal constant _ROLE_218 = 1 << 218; uint256 internal constant _ROLE_219 = 1 << 219; uint256 internal constant _ROLE_220 = 1 << 220; uint256 internal constant _ROLE_221 = 1 << 221; uint256 internal constant _ROLE_222 = 1 << 222; uint256 internal constant _ROLE_223 = 1 << 223; uint256 internal constant _ROLE_224 = 1 << 224; uint256 internal constant _ROLE_225 = 1 << 225; uint256 internal constant _ROLE_226 = 1 << 226; uint256 internal constant _ROLE_227 = 1 << 227; uint256 internal constant _ROLE_228 = 1 << 228; uint256 internal constant _ROLE_229 = 1 << 229; uint256 internal constant _ROLE_230 = 1 << 230; uint256 internal constant _ROLE_231 = 1 << 231; uint256 internal constant _ROLE_232 = 1 << 232; uint256 internal constant _ROLE_233 = 1 << 233; uint256 internal constant _ROLE_234 = 1 << 234; uint256 internal constant _ROLE_235 = 1 << 235; uint256 internal constant _ROLE_236 = 1 << 236; uint256 internal constant _ROLE_237 = 1 << 237; uint256 internal constant _ROLE_238 = 1 << 238; uint256 internal constant _ROLE_239 = 1 << 239; uint256 internal constant _ROLE_240 = 1 << 240; uint256 internal constant _ROLE_241 = 1 << 241; uint256 internal constant _ROLE_242 = 1 << 242; uint256 internal constant _ROLE_243 = 1 << 243; uint256 internal constant _ROLE_244 = 1 << 244; uint256 internal constant _ROLE_245 = 1 << 245; uint256 internal constant _ROLE_246 = 1 << 246; uint256 internal constant _ROLE_247 = 1 << 247; uint256 internal constant _ROLE_248 = 1 << 248; uint256 internal constant _ROLE_249 = 1 << 249; uint256 internal constant _ROLE_250 = 1 << 250; uint256 internal constant _ROLE_251 = 1 << 251; uint256 internal constant _ROLE_252 = 1 << 252; uint256 internal constant _ROLE_253 = 1 << 253; uint256 internal constant _ROLE_254 = 1 << 254; uint256 internal constant _ROLE_255 = 1 << 255; } // src/ovm/ObolValidatorManager.sol /// @title ObolValidatorManager /// @author Obol /// @notice A maximally-composable contract that distributes payments /// based on threshold to it's recipients. /// @dev Only ETH can be distributed for a given deployment. There is a /// recovery method for tokens sent by accident. contract ObolValidatorManager is OwnableRoles { /// ----------------------------------------------------------------------- /// libraries /// ----------------------------------------------------------------------- using SafeTransferLib for address; /// ----------------------------------------------------------------------- /// errors /// ----------------------------------------------------------------------- // Invalid request params, e.g. empty input error InvalidRequest_Params(); // Failed to call system contract get_fee() error InvalidRequest_SystemGetFee(); // Insufficient fee provided in the call's value to conclude the request error InvalidRequest_NotEnoughFee(); // Failed to call system contract add_consolidation_request() error InvalidConsolidation_Failed(); // Failed to call system contract add_withdrawal_request() error InvalidWithdrawal_Failed(); /// Invalid distribution error InvalidDistribution_TooLarge(); /// ----------------------------------------------------------------------- /// events /// ----------------------------------------------------------------------- /// Emitted after principal recipient is changed /// @param newPrincipalRecipient New principal recipient address /// @param oldPrincipalRecipient Old principal recipient address event NewPrincipalRecipient(address indexed newPrincipalRecipient, address indexed oldPrincipalRecipient); /// Emitted after funds are distributed to recipients /// @param principalPayout Amount of principal paid out /// @param rewardPayout Amount of reward paid out /// @param pullOrPush Flag indicating PULL or PUSH flow event DistributeFunds(uint256 principalPayout, uint256 rewardPayout, uint256 pullOrPush); /// Emitted after tokens are recovered to a recipient /// @param nonOVMToken Recovered token (cannot be ETH) /// @param recipient Address receiving recovered token /// @param amount Amount of recovered token event RecoverNonOVMFunds(address indexed nonOVMToken, address indexed recipient, uint256 amount); /// Emitted after funds withdrawn using pull flow /// @param account Account withdrawing funds for /// @param amount Amount withdrawn event Withdrawal(address indexed account, uint256 amount); /// Emitted when a Pectra consolidation request is done /// @param requester Address of the requester /// @param source Source validator public key /// @param target Target validator public key event ConsolidationRequested(address indexed requester, bytes indexed source, bytes indexed target); /// Emitted when a Pectra withdrawal request is done /// @param requester Address of the requester /// @param pubKey Validator public key /// @param amount Withdrawal amount event WithdrawalRequested(address indexed requester, bytes indexed pubKey, uint256 amount); /// ----------------------------------------------------------------------- /// storage /// ----------------------------------------------------------------------- /// ----------------------------------------------------------------------- /// storage - constants /// ----------------------------------------------------------------------- uint256 public constant WITHDRAWAL_ROLE = 0x01; uint256 public constant CONSOLIDATION_ROLE = 0x02; uint256 public constant SET_PRINCIPAL_ROLE = 0x04; uint256 public constant RECOVER_FUNDS_ROLE = 0x08; uint256 internal constant PUSH = 0; uint256 internal constant PULL = 1; uint256 internal constant PUBLIC_KEY_LENGTH = 48; /// ----------------------------------------------------------------------- /// storage - immutable /// ----------------------------------------------------------------------- address public immutable consolidationSystemContract; address public immutable withdrawalSystemContract; address public immutable depositSystemContract; address public immutable rewardRecipient; uint64 public immutable principalThreshold; /// ----------------------------------------------------------------------- /// storage - mutables /// ----------------------------------------------------------------------- /// Address to receive principal funds address public principalRecipient; /// Amount of principal stake (wei) done via deposit() calls uint256 public amountOfPrincipalStake; /// Amount of active balance set aside for pulls /// @dev ERC20s with very large decimals may overflow & cause issues uint128 public fundsPendingWithdrawal; /// Mapping to account balances for pulling mapping(address => uint256) internal pullBalances; /// ----------------------------------------------------------------------- /// constructor /// ----------------------------------------------------------------------- constructor( address _consolidationSystemContract, address _withdrawalSystemContract, address _depositSystemContract, address _owner, address _principalRecipient, address _rewardRecipient, uint64 _principalThreshold ) { consolidationSystemContract = _consolidationSystemContract; withdrawalSystemContract = _withdrawalSystemContract; depositSystemContract = _depositSystemContract; principalRecipient = _principalRecipient; rewardRecipient = _rewardRecipient; principalThreshold = _principalThreshold; _initializeOwner(_owner); } /// ----------------------------------------------------------------------- /// functions /// ----------------------------------------------------------------------- /// ----------------------------------------------------------------------- /// functions - public & external /// ----------------------------------------------------------------------- /// @dev Fallback function to receive ETH /// Because we do not use Clone, we must implement this explicitly receive() external payable {} /// @notice Submit a Phase 0 DepositData object. /// @param pubkey A BLS12-381 public key. /// @param withdrawal_credentials Commitment to a public key for withdrawals. /// @param signature A BLS12-381 signature. /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object. /// Used as a protection against malformed input. /// @dev This function is a proxy to the deposit() function on the depositSystemContract. /// The deposited amount is accounted for in the amountOfPrincipalStake, which is used /// to determine the principalRecipient's share of the funds to be distributed. /// Any deposits made directly to the depositSystemContract will not be accounted for /// and will be sent to the rewardRecipient address. function deposit( bytes calldata pubkey, bytes calldata withdrawal_credentials, bytes calldata signature, bytes32 deposit_data_root ) external payable { amountOfPrincipalStake += msg.value; IDepositContract(depositSystemContract).deposit{value: msg.value}( pubkey, withdrawal_credentials, signature, deposit_data_root ); } /// @notice Set the principal recipient address /// @param newPrincipalRecipient New address to receive principal funds function setPrincipalRecipient(address newPrincipalRecipient) external onlyOwnerOrRoles(SET_PRINCIPAL_ROLE) { if (newPrincipalRecipient == address(0)) { revert InvalidRequest_Params(); } address oldPrincipalRecipient = principalRecipient; principalRecipient = newPrincipalRecipient; emit NewPrincipalRecipient(newPrincipalRecipient, oldPrincipalRecipient); } /// Distributes target token inside the contract to recipients /// @dev pushes funds to recipients function distributeFunds() external { _distributeFunds(PUSH); } /// Distributes target token inside the contract to recipients /// @dev backup recovery if any recipient tries to brick the OVM for /// remaining recipients function distributeFundsPull() external { _distributeFunds(PULL); } /// Request validators consolidation with the EIP7251 system contract /// @dev all source validators will be consolidated into the target validator /// the caller must compute the fee before calling and send a sufficient msg.value amount /// excess amount will be refunded /// @param sourcePubKeys validator public keys to be consolidated /// @param targetPubKey target validator public key function requestConsolidation( bytes[] calldata sourcePubKeys, bytes calldata targetPubKey ) external payable onlyOwnerOrRoles(CONSOLIDATION_ROLE) { if (sourcePubKeys.length == 0 || sourcePubKeys.length > 63 || targetPubKey.length != PUBLIC_KEY_LENGTH) revert InvalidRequest_Params(); uint256 remainingFee = msg.value; uint256 len = sourcePubKeys.length; for (uint256 i; i < len; ) { uint256 _currentFee = _computeSystemContractFee(consolidationSystemContract); if (_currentFee > remainingFee) revert InvalidRequest_NotEnoughFee(); remainingFee -= _currentFee; _requestConsolidation(sourcePubKeys[i], targetPubKey, _currentFee); unchecked { ++i; } } // Future optimization idea: do not send if gas cost exceeds the value. if (remainingFee > 0) payable(msg.sender).transfer(remainingFee); } /// Request partial/full withdrawal from the EIP7002 system contract /// @dev the caller must compute the fee before calling and send a sufficient msg.value amount /// excess amount will be refunded /// withdrawals that leave a validator with (0..32) ether, // will only withdraw an amount that leaves the validator at 32 ether /// @param pubKeys validator public keys /// @param amounts withdrawal amounts in gwei /// any amount below principalThreshold will be distributed as reward /// any amount >= principalThreshold will be distributed as principal function requestWithdrawal( bytes[] calldata pubKeys, uint64[] calldata amounts ) external payable onlyOwnerOrRoles(WITHDRAWAL_ROLE) { if (pubKeys.length != amounts.length) revert InvalidRequest_Params(); uint256 remainingFee = msg.value; uint256 len = pubKeys.length; for (uint256 i; i < len; ) { uint256 _currentFee = _computeSystemContractFee(withdrawalSystemContract); if (_currentFee > remainingFee) revert InvalidRequest_NotEnoughFee(); remainingFee -= _currentFee; _requestWithdrawal(pubKeys[i], amounts[i], _currentFee); unchecked { ++i; } } // Future optimization idea: do not send if gas cost exceeds the value. if (remainingFee > 0) payable(msg.sender).transfer(remainingFee); } /// Recover non-OVM tokens to a recipient /// @param nonOVMToken Token to recover (cannot be OVM token) /// @param recipient Address to receive recovered token function recoverFunds(address nonOVMToken, address recipient) external onlyOwnerOrRoles(RECOVER_FUNDS_ROLE) { uint256 amount = ERC20(nonOVMToken).balanceOf(address(this)); nonOVMToken.safeTransfer(recipient, amount); emit RecoverNonOVMFunds(nonOVMToken, recipient, amount); } /// Withdraw token balance for an account /// @param account Address to withdraw on behalf of function withdraw(address account) external { uint256 amount = pullBalances[account]; unchecked { // shouldn't underflow; fundsPendingWithdrawal = sum(pullBalances) fundsPendingWithdrawal -= uint128(amount); } pullBalances[account] = 0; account.safeTransferETH(amount); emit Withdrawal(account, amount); } /// ----------------------------------------------------------------------- /// functions - view & pure /// ----------------------------------------------------------------------- /// Returns the balance for the account `account` /// @param account Account to return balance for /// @return Account's withdrawable ether balance function getPullBalance(address account) external view returns (uint256) { return pullBalances[account]; } /// ----------------------------------------------------------------------- /// functions - private & internal /// ----------------------------------------------------------------------- /// Compute system contract's fee /// @param systemContractAddress Address of the consolidation system contract /// @return The computed fee function _computeSystemContractFee(address systemContractAddress) internal view returns (uint256) { (bool ok, bytes memory result) = systemContractAddress.staticcall(""); if (!ok) revert InvalidRequest_SystemGetFee(); return uint256(bytes32(result)); } /// Execute a single consolidation request /// @param source Source validator public key /// @param target Target validator public key /// @param fee Fee for the consolidation request function _requestConsolidation(bytes calldata source, bytes calldata target, uint256 fee) private { if (source.length != PUBLIC_KEY_LENGTH || target.length != PUBLIC_KEY_LENGTH) revert InvalidRequest_Params(); // Input data has the following layout: // // +--------+--------+ // | source | target | // +--------+--------+ // 48 48 (bool ok, ) = consolidationSystemContract.call{value: fee}(bytes.concat(source, target)); if (!ok) revert InvalidConsolidation_Failed(); emit ConsolidationRequested(msg.sender, source, target); } /// Executes single withdrawal request function _requestWithdrawal(bytes memory pubkey, uint64 amount, uint256 fee) private { if (pubkey.length != PUBLIC_KEY_LENGTH) revert InvalidRequest_Params(); // Input data has the following layout: // // +--------+--------+ // | pubkey | amount | // +--------+--------+ // 48 8 (bool ret, ) = withdrawalSystemContract.call{value: fee}(abi.encodePacked(pubkey, amount)); if (!ret) revert InvalidWithdrawal_Failed(); emit WithdrawalRequested(msg.sender, pubkey, amount); } /// Distributes target token inside the contract to next-in-line recipients /// @dev can PUSH or PULL funds to recipients function _distributeFunds(uint256 pullOrPush) internal { /// checks /// effects // load storage into memory uint256 currentbalance = address(this).balance; uint256 _memoryFundsPendingWithdrawal = uint256(fundsPendingWithdrawal); uint256 _fundsToBeDistributed = currentbalance - _memoryFundsPendingWithdrawal; uint256 principalThresholdWei = uint256(principalThreshold) * 1e9; // determine which recipeint is getting paid based on funds to be distributed uint256 _principalPayout = 0; uint256 _rewardPayout = 0; unchecked { if (_fundsToBeDistributed >= principalThresholdWei && amountOfPrincipalStake > 0) { if (_fundsToBeDistributed > amountOfPrincipalStake) { // this means there is reward part of the funds to be distributed _principalPayout = amountOfPrincipalStake; // shouldn't underflow _rewardPayout = _fundsToBeDistributed - amountOfPrincipalStake; } else { // this means there is no reward part of the funds to be distributed _principalPayout = _fundsToBeDistributed; } } else { _rewardPayout = _fundsToBeDistributed; } } { if (_fundsToBeDistributed > type(uint128).max) revert InvalidDistribution_TooLarge(); // Write to storage // the principal value // it cannot overflow because _principalPayout < _fundsToBeDistributed if (_principalPayout > 0) amountOfPrincipalStake -= uint128(_principalPayout); } /// interactions // pay outs // earlier tranche recipients may try to re-enter but will cause fn to // revert // when later external calls fail (bc balance is emptied early) // pay out principal _payout(principalRecipient, _principalPayout, pullOrPush); // pay out reward _payout(rewardRecipient, _rewardPayout, pullOrPush); if (pullOrPush == PULL) { if (_principalPayout > 0 || _rewardPayout > 0) { // Write to storage fundsPendingWithdrawal = uint128(_memoryFundsPendingWithdrawal + _principalPayout + _rewardPayout); } } emit DistributeFunds(_principalPayout, _rewardPayout, pullOrPush); } function _payout(address recipient, uint256 payoutAmount, uint256 pullOrPush) internal { if (payoutAmount > 0) { if (pullOrPush == PULL) { // Write to Storage pullBalances[recipient] += payoutAmount; } else if (pullOrPush == PUSH) { recipient.safeTransferETH(payoutAmount); } else { revert InvalidRequest_Params(); } } } } // src/ovm/ObolValidatorManagerFactory.sol /// @title ObolValidatorManagerFactory /// @author Obol /// @notice A factory contract for deploying ObolValidatorManager. contract ObolValidatorManagerFactory { /// ----------------------------------------------------------------------- /// errors /// ----------------------------------------------------------------------- /// Owner cannot be address(0) error Invalid_Owner(); /// Some recipients are address(0) error Invalid__Recipients(); /// Threshold must be positive error Invalid__ZeroThreshold(); /// Threshold must be below 2048 ether error Invalid__ThresholdTooLarge(); /// ----------------------------------------------------------------------- /// events /// ----------------------------------------------------------------------- /// Emitted after a new ObolValidatorManager instance is deployed /// @param ovm Address of newly created ObolValidatorManager instance /// @param owner Owner of newly created ObolValidatorManager instance /// @param principalRecipient Address to distribute principal payment to /// @param rewardRecipient Address to distribute reward payment to /// @param principalThreshold Principal vs rewards classification threshold (gwei) event CreateObolValidatorManager( address indexed ovm, address indexed owner, address principalRecipient, address rewardRecipient, uint64 principalThreshold ); /// ----------------------------------------------------------------------- /// storage - immutable /// ----------------------------------------------------------------------- address public immutable consolidationSystemContract; address public immutable withdrawalSystemContract; address public immutable depositSystemContract; /// ----------------------------------------------------------------------- /// constructor /// ----------------------------------------------------------------------- /// @param _consolidationSystemContract Consolidation system contract address /// @param _withdrawalSystemContract Withdrawal system contract address /// @param _depositSystemContract Deposit system contract address /// @dev System contracts are expected to be deployed at: /// Consolidation: 0x00431F263cE400f4455c2dCf564e53007Ca4bbBb /// https://github.com/ethereum/EIPs/blob/d96625a4dcbbe2572fa006f062bd02b4582eefd5/EIPS/eip-7251.md#constants /// Withdrawal: 0x0c15F14308530b7CDB8460094BbB9cC28b9AaaAA /// https://github.com/ethereum/EIPs/blob/d96625a4dcbbe2572fa006f062bd02b4582eefd5/EIPS/eip-7002.md#configuration /// Deposit Holesky/Devnet: 0x4242424242424242424242424242424242424242 /// Deposit Sepolia: 0x7f02C3E3c98b133055B8B348B2Ac625669Ed295D /// Deposit Mainnet: 0x00000000219ab540356cBB839Cbe05303d7705Fa constructor( address _consolidationSystemContract, address _withdrawalSystemContract, address _depositSystemContract // string memory _ensName, // address _ensReverseRegistrar, // address _ensOwner ) { consolidationSystemContract = _consolidationSystemContract; withdrawalSystemContract = _withdrawalSystemContract; depositSystemContract = _depositSystemContract; // IENSReverseRegistrar(_ensReverseRegistrar).setName(_ensName); // IENSReverseRegistrar(_ensReverseRegistrar).claim(_ensOwner); } /// ----------------------------------------------------------------------- /// functions /// ----------------------------------------------------------------------- /// ----------------------------------------------------------------------- /// functions - public & external /// ----------------------------------------------------------------------- /// Create a new ObolValidatorManager instance /// @param owner Owner of the new ObolValidatorManager instance /// @param principalRecipient Address to distribute principal payments to /// @param rewardRecipient Address to distribute reward payments to /// @param principalThreshold Principal vs rewards classification threshold (gwei) /// @return ovm Address of the new ObolValidatorManager instance function createObolValidatorManager( address owner, address principalRecipient, address rewardRecipient, uint64 principalThreshold ) external returns (ObolValidatorManager ovm) { if (owner == address(0)) revert Invalid_Owner(); if (principalRecipient == address(0) || rewardRecipient == address(0)) revert Invalid__Recipients(); if (principalThreshold == 0) revert Invalid__ZeroThreshold(); if (principalThreshold > 2048 * 1e9) revert Invalid__ThresholdTooLarge(); ovm = new ObolValidatorManager( consolidationSystemContract, withdrawalSystemContract, depositSystemContract, owner, principalRecipient, rewardRecipient, principalThreshold ); emit CreateObolValidatorManager( address(ovm), owner, principalRecipient, rewardRecipient, principalThreshold ); } }
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_consolidationSystemContract","type":"address"},{"internalType":"address","name":"_withdrawalSystemContract","type":"address"},{"internalType":"address","name":"_depositSystemContract","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Invalid_Owner","type":"error"},{"inputs":[],"name":"Invalid__Recipients","type":"error"},{"inputs":[],"name":"Invalid__ThresholdTooLarge","type":"error"},{"inputs":[],"name":"Invalid__ZeroThreshold","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"ovm","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"principalRecipient","type":"address"},{"indexed":false,"internalType":"address","name":"rewardRecipient","type":"address"},{"indexed":false,"internalType":"uint64","name":"principalThreshold","type":"uint64"}],"name":"CreateObolValidatorManager","type":"event"},{"inputs":[],"name":"consolidationSystemContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"principalRecipient","type":"address"},{"internalType":"address","name":"rewardRecipient","type":"address"},{"internalType":"uint64","name":"principalThreshold","type":"uint64"}],"name":"createObolValidatorManager","outputs":[{"internalType":"contract ObolValidatorManager","name":"ovm","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositSystemContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalSystemContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60e060405234801561001057600080fd5b5060405161327338038061327383398181016040528101906100329190610139565b8273ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff16815250508173ffffffffffffffffffffffffffffffffffffffff1660a08173ffffffffffffffffffffffffffffffffffffffff16815250508073ffffffffffffffffffffffffffffffffffffffff1660c08173ffffffffffffffffffffffffffffffffffffffff168152505050505061018c565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610106826100db565b9050919050565b610116816100fb565b811461012157600080fd5b50565b6000815190506101338161010d565b92915050565b600080600060608486031215610152576101516100d6565b5b600061016086828701610124565b935050602061017186828701610124565b925050604061018286828701610124565b9150509250925092565b60805160a05160c0516130a46101cf60003960008181610119015261031701526000818160f501526102f60152600081816102d501526103ef01526130a46000f3fe60806040523480156200001157600080fd5b5060043610620000525760003560e01c806306dbd0cb14620000575780633e5b34b21462000079578063cdba577d146200009b578063f7f8ff7a14620000d1575b600080fd5b62000061620000f3565b60405162000070919062000464565b60405180910390f35b6200008362000117565b60405162000092919062000464565b60405180910390f35b620000b96004803603810190620000b39190620004fc565b6200013b565b604051620000c89190620005d9565b60405180910390f35b620000db620003ed565b604051620000ea919062000464565b60405180910390f35b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b60008073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1603620001a3576040517f22adafa200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614806200020b5750600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16145b1562000243576040517ffca23e2a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008267ffffffffffffffff160362000288576040517f8966ee9500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6501dcd65000008267ffffffffffffffff161115620002d3576040517f12f9b82600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000087878787604051620003489062000411565b6200035a979695949392919062000607565b604051809103906000f08015801562000377573d6000803e3d6000fd5b5090508473ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f5f69e7dce622f5df4a1899cf39ca6eb1216397f2c07206d2ef33aebf960827a3868686604051620003dd9392919062000684565b60405180910390a3949350505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6129ad80620006c283390190565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200044c826200041f565b9050919050565b6200045e816200043f565b82525050565b60006020820190506200047b600083018462000453565b92915050565b600080fd5b62000491816200043f565b81146200049d57600080fd5b50565b600081359050620004b18162000486565b92915050565b600067ffffffffffffffff82169050919050565b620004d681620004b7565b8114620004e257600080fd5b50565b600081359050620004f681620004cb565b92915050565b6000806000806080858703121562000519576200051862000481565b5b60006200052987828801620004a0565b94505060206200053c87828801620004a0565b93505060406200054f87828801620004a0565b92505060606200056287828801620004e5565b91505092959194509250565b6000819050919050565b600062000599620005936200058d846200041f565b6200056e565b6200041f565b9050919050565b6000620005ad8262000578565b9050919050565b6000620005c182620005a0565b9050919050565b620005d381620005b4565b82525050565b6000602082019050620005f06000830184620005c8565b92915050565b6200060181620004b7565b82525050565b600060e0820190506200061e600083018a62000453565b6200062d602083018962000453565b6200063c604083018862000453565b6200064b606083018762000453565b6200065a608083018662000453565b6200066960a083018562000453565b6200067860c0830184620005f6565b98975050505050505050565b60006060820190506200069b600083018662000453565b620006aa602083018562000453565b620006b96040830184620005f6565b94935050505056fe6101206040523480156200001257600080fd5b50604051620029ad380380620029ad83398181016040528101906200003891906200026e565b8673ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff16815250508573ffffffffffffffffffffffffffffffffffffffff1660a08173ffffffffffffffffffffffffffffffffffffffff16815250508473ffffffffffffffffffffffffffffffffffffffff1660c08173ffffffffffffffffffffffffffffffffffffffff1681525050826000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff1660e08173ffffffffffffffffffffffffffffffffffffffff16815250508067ffffffffffffffff166101008167ffffffffffffffff168152505062000176846200018360201b60201c565b5050505050505062000321565b8060601b60601c905080638b78c6d819558060007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a350565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620001f182620001c4565b9050919050565b6200020381620001e4565b81146200020f57600080fd5b50565b6000815190506200022381620001f8565b92915050565b600067ffffffffffffffff82169050919050565b620002488162000229565b81146200025457600080fd5b50565b60008151905062000268816200023d565b92915050565b600080600080600080600060e0888a03121562000290576200028f620001bf565b5b6000620002a08a828b0162000212565b9750506020620002b38a828b0162000212565b9650506040620002c68a828b0162000212565b9550506060620002d98a828b0162000212565b9450506080620002ec8a828b0162000212565b93505060a0620002ff8a828b0162000212565b92505060c0620003128a828b0162000257565b91505092959891949750929550565b60805160a05160c05160e051610100516126166200039760003960008181610e22015261159c0152600081816107a101526116c40152600081816108180152610c0d01526000818161077d01528181610ff1015261183801526000818161093c015281816111d1015261138e01526126166000f3fe6080604052600436106101fd5760003560e01c80634d5be1f81161010d5780637bfafd59116100a05780639fdb5e8a1161006f5780639fdb5e8a14610645578063f04e283e14610670578063f2fde38b1461068c578063f7f8ff7a146106a8578063fee81cf4146106d357610204565b80637bfafd59146105aa5780638da5cb5b146105d55780638f98f67c146106005780639066a3001461062957610204565b80635f097d75116100dc5780635f097d751461051f57806367db90c21461054a5780636c19688614610575578063715018a6146105a057610204565b80634d5be1f814610484578063514e62fc146104af57806351cff8d9146104ec57806354d1f13d1461051557610204565b806322be1ffd116101905780632de948071161015f5780632de94807146103be578063377b2c34146103fb5780633a6a4d2e146104265780633e5b34b21461043d5780634a4ee7b11461046857610204565b806322be1ffd1461035857806324ae6a2714610374578063256929621461039d5780632d7fd507146103a757610204565b8063183a4f6e116101cc578063183a4f6e146102c75780631c10893f146102e35780631cd64df4146102ff578063228951181461033c57610204565b806303615ba314610209578063043edae81461024657806306dbd0cb1461027157806317f333401461029c57610204565b3661020457005b600080fd5b34801561021557600080fd5b50610230600480360381019061022b9190611b21565b610710565b60405161023d9190611b67565b60405180910390f35b34801561025257600080fd5b5061025b610759565b6040516102689190611bad565b60405180910390f35b34801561027d57600080fd5b5061028661077b565b6040516102939190611bd7565b60405180910390f35b3480156102a857600080fd5b506102b161079f565b6040516102be9190611bd7565b60405180910390f35b6102e160048036038101906102dc9190611c1e565b6107c3565b005b6102fd60048036038101906102f89190611c4b565b6107d0565b005b34801561030b57600080fd5b5061032660048036038101906103219190611c4b565b6107e6565b6040516103339190611ca6565b60405180910390f35b61035660048036038101906103519190611d5c565b6107fd565b005b610372600480360381019061036d9190611e7b565b6108b7565b005b34801561038057600080fd5b5061039b60048036038101906103969190611efc565b610a41565b005b6103a5610b60565b005b3480156103b357600080fd5b506103bc610bb4565b005b3480156103ca57600080fd5b506103e560048036038101906103e09190611b21565b610bc0565b6040516103f29190611b67565b60405180910390f35b34801561040757600080fd5b50610410610bdb565b60405161041d9190611bd7565b60405180910390f35b34801561043257600080fd5b5061043b610bff565b005b34801561044957600080fd5b50610452610c0b565b60405161045f9190611bd7565b60405180910390f35b610482600480360381019061047d9190611c4b565b610c2f565b005b34801561049057600080fd5b50610499610c45565b6040516104a69190611b67565b60405180910390f35b3480156104bb57600080fd5b506104d660048036038101906104d19190611c4b565b610c4a565b6040516104e39190611ca6565b60405180910390f35b3480156104f857600080fd5b50610513600480360381019061050e9190611b21565b610c62565b005b61051d610dc0565b005b34801561052b57600080fd5b50610534610dfc565b6040516105419190611b67565b60405180910390f35b34801561055657600080fd5b5061055f610e02565b60405161056c9190611b67565b60405180910390f35b34801561058157600080fd5b5061058a610e07565b6040516105979190611b67565b60405180910390f35b6105a8610e0c565b005b3480156105b657600080fd5b506105bf610e20565b6040516105cc9190611f5f565b60405180910390f35b3480156105e157600080fd5b506105ea610e44565b6040516105f79190611bd7565b60405180910390f35b34801561060c57600080fd5b5061062760048036038101906106229190611b21565b610e52565b005b610643600480360381019061063e9190611fd0565b610f88565b005b34801561065157600080fd5b5061065a61115f565b6040516106679190611b67565b60405180910390f35b61068a60048036038101906106859190611b21565b611164565b005b6106a660048036038101906106a19190611b21565b6111a5565b005b3480156106b457600080fd5b506106bd6111cf565b6040516106ca9190611bd7565b60405180910390f35b3480156106df57600080fd5b506106fa60048036038101906106f59190611b21565b6111f3565b6040516107079190611b67565b60405180910390f35b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600260009054906101000a90046fffffffffffffffffffffffffffffffff1681565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b6107cd338261120e565b50565b6107d861121e565b6107e2828261123b565b5050565b600081826107f385610bc0565b1614905092915050565b346001600082825461080f9190612080565b925050819055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632289511834898989898989896040518963ffffffff1660e01b815260040161087c9796959493929190612121565b6000604051808303818588803b15801561089557600080fd5b505af11580156108a9573d6000803e3d6000fd5b505050505050505050505050565b60026108c28161124b565b60008585905014806108d75750603f85859050115b806108e6575060308383905014155b1561091d576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000349050600086869050905060005b818110156109e65760006109607f0000000000000000000000000000000000000000000000000000000000000000611282565b90508381111561099c576040517f7953952a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80846109a89190612181565b93506109da8989848181106109c0576109bf6121b5565b5b90506020028101906109d291906121f3565b89898561133c565b8160010191505061092d565b506000821115610a38573373ffffffffffffffffffffffffffffffffffffffff166108fc839081150290604051600060405180830381858888f19350505050158015610a36573d6000803e3d6000fd5b505b50505050505050565b6008610a4c8161124b565b60008373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610a879190611bd7565b602060405180830381865afa158015610aa4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ac8919061226b565b9050610af583828673ffffffffffffffffffffffffffffffffffffffff166114ef9092919063ffffffff16565b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f02dc46f45168b3833fa84aa22ee90f4ffec812cf1067be0413353eb95c031b0583604051610b529190611b67565b60405180910390a350505050565b6000610b6a61153e565b67ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b610bbe6001611549565b565b6000638b78c6d8600c52816000526020600c20549050919050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610c096000611549565b565b7f000000000000000000000000000000000000000000000000000000000000000081565b610c3761121e565b610c41828261120e565b5050565b600481565b60008082610c5785610bc0565b161415905092915050565b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905080600260008282829054906101000a90046fffffffffffffffffffffffffffffffff160392506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055506000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610d6e818373ffffffffffffffffffffffffffffffffffffffff1661179b90919063ffffffff16565b8173ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b6582604051610db49190611b67565b60405180910390a25050565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b60015481565b600181565b600281565b610e1461121e565b610e1e60006117bb565b565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000638b78c6d81954905090565b6004610e5d8161124b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610ec3576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050826000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fa4de47033aed06f80e7c49451826fa061ea6eb7a8f7f1f2b0148dc3b0930165160405160405180910390a3505050565b6001610f938161124b565b828290508585905014610fd2576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000349050600086869050905060005b818110156111045760006110157f0000000000000000000000000000000000000000000000000000000000000000611282565b905083811115611051576040517f7953952a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808461105d9190612181565b93506110f8898984818110611075576110746121b5565b5b905060200281019061108791906121f3565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508888858181106110dd576110dc6121b5565b5b90506020020160208101906110f291906122c4565b836117f9565b81600101915050610fe2565b506000821115611156573373ffffffffffffffffffffffffffffffffffffffff166108fc839081150290604051600060405180830381858888f19350505050158015611154573d6000803e3d6000fd5b505b50505050505050565b600881565b61116c61121e565b63389a75e1600c52806000526020600c20805442111561119457636f5e88186000526004601cfd5b60008155506111a2816117bb565b50565b6111ad61121e565b8060601b6111c357637448fbae6000526004601cfd5b6111cc816117bb565b50565b7f000000000000000000000000000000000000000000000000000000000000000081565b600063389a75e1600c52816000526020600c20549050919050565b61121a82826000611984565b5050565b638b78c6d819543314611239576382b429006000526004601cfd5b565b61124782826001611984565b5050565b638b78c6d81954331461127f57638b78c6d8600c5233600052806020600c20541661127e576382b429006000526004601cfd5b5b50565b60008060008373ffffffffffffffffffffffffffffffffffffffff166040516112aa90612322565b600060405180830381855afa9150503d80600081146112e5576040519150601f19603f3d011682016040523d82523d6000602084013e6112ea565b606091505b509150915081611326576040517f448e5c4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8061133090612374565b60001c92505050919050565b603085859050141580611353575060308383905014155b1561138a576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1682878787876040516020016113db9493929190612400565b6040516020818303038152906040526040516113f79190612483565b60006040518083038185875af1925050503d8060008114611434576040519150601f19603f3d011682016040523d82523d6000602084013e611439565b606091505b5050905080611474576040517f1684b73e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b838360405161148492919061249a565b6040518091039020868660405161149c92919061249a565b60405180910390203373ffffffffffffffffffffffffffffffffffffffff167f4ca695eebdc4cd20b1b742b57002d491913323670e87798f75ff63f9af522df860405160405180910390a4505050505050565b81601452806034526fa9059cbb00000000000000000000000060005260206000604460106000875af13d156001600051141716611534576390b8ec186000526004601cfd5b6000603452505050565b60006202a300905090565b60004790506000600260009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff169050600081836115919190612181565b90506000633b9aca007f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff166115cf91906124b3565b90506000808284101580156115e657506000600154115b1561160f576001548411156116065760015491506001548403905061160a565b8391505b611613565b8390505b6fffffffffffffffffffffffffffffffff801684111561165f576040517f502cbcb200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082111561169457816fffffffffffffffffffffffffffffffff166001600082825461168c9190612181565b925050819055505b6116bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683896119de565b6116ea7f000000000000000000000000000000000000000000000000000000000000000082896119de565b600187036117575760008211806117015750600081115b15611756578082866117139190612080565b61171d9190612080565b600260006101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b5b7f8b367b31582d5c638694615c1cfc77ffcf9febcf6e6c793358089f7769cfeee682828960405161178a939291906124f5565b60405180910390a150505050505050565b60005a60005a84865af16117b75763b12d13eb6000526004601cfd5b5050565b638b78c6d8198160601b60601c91508181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a38181555050565b6030835114611834576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16828585604051602001611881929190612562565b60405160208183030381529060405260405161189d9190612483565b60006040518083038185875af1925050503d80600081146118da576040519150601f19603f3d011682016040523d82523d6000602084013e6118df565b606091505b505090508061191a576040517fd3d2fd8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b836040516119289190612483565b60405180910390203373ffffffffffffffffffffffffffffffffffffffff167f1e55aec951b70d2fce6d30fa2f2dfc3c3d280c2a85c04b7060ee6194f75261038560405161197691906125c5565b60405180910390a350505050565b638b78c6d8600c52826000526020600c208054838117836119a757848216821890505b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a3505050505050565b6000821115611ab45760018103611a4a5781600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611a3e9190612080565b92505081905550611ab3565b60008103611a8057611a7b828473ffffffffffffffffffffffffffffffffffffffff1661179b90919063ffffffff16565b611ab2565b6040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b5b505050565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000611aee82611ac3565b9050919050565b611afe81611ae3565b8114611b0957600080fd5b50565b600081359050611b1b81611af5565b92915050565b600060208284031215611b3757611b36611ab9565b5b6000611b4584828501611b0c565b91505092915050565b6000819050919050565b611b6181611b4e565b82525050565b6000602082019050611b7c6000830184611b58565b92915050565b60006fffffffffffffffffffffffffffffffff82169050919050565b611ba781611b82565b82525050565b6000602082019050611bc26000830184611b9e565b92915050565b611bd181611ae3565b82525050565b6000602082019050611bec6000830184611bc8565b92915050565b611bfb81611b4e565b8114611c0657600080fd5b50565b600081359050611c1881611bf2565b92915050565b600060208284031215611c3457611c33611ab9565b5b6000611c4284828501611c09565b91505092915050565b60008060408385031215611c6257611c61611ab9565b5b6000611c7085828601611b0c565b9250506020611c8185828601611c09565b9150509250929050565b60008115159050919050565b611ca081611c8b565b82525050565b6000602082019050611cbb6000830184611c97565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f840112611ce657611ce5611cc1565b5b8235905067ffffffffffffffff811115611d0357611d02611cc6565b5b602083019150836001820283011115611d1f57611d1e611ccb565b5b9250929050565b6000819050919050565b611d3981611d26565b8114611d4457600080fd5b50565b600081359050611d5681611d30565b92915050565b60008060008060008060006080888a031215611d7b57611d7a611ab9565b5b600088013567ffffffffffffffff811115611d9957611d98611abe565b5b611da58a828b01611cd0565b9750975050602088013567ffffffffffffffff811115611dc857611dc7611abe565b5b611dd48a828b01611cd0565b9550955050604088013567ffffffffffffffff811115611df757611df6611abe565b5b611e038a828b01611cd0565b93509350506060611e168a828b01611d47565b91505092959891949750929550565b60008083601f840112611e3b57611e3a611cc1565b5b8235905067ffffffffffffffff811115611e5857611e57611cc6565b5b602083019150836020820283011115611e7457611e73611ccb565b5b9250929050565b60008060008060408587031215611e9557611e94611ab9565b5b600085013567ffffffffffffffff811115611eb357611eb2611abe565b5b611ebf87828801611e25565b9450945050602085013567ffffffffffffffff811115611ee257611ee1611abe565b5b611eee87828801611cd0565b925092505092959194509250565b60008060408385031215611f1357611f12611ab9565b5b6000611f2185828601611b0c565b9250506020611f3285828601611b0c565b9150509250929050565b600067ffffffffffffffff82169050919050565b611f5981611f3c565b82525050565b6000602082019050611f746000830184611f50565b92915050565b60008083601f840112611f9057611f8f611cc1565b5b8235905067ffffffffffffffff811115611fad57611fac611cc6565b5b602083019150836020820283011115611fc957611fc8611ccb565b5b9250929050565b60008060008060408587031215611fea57611fe9611ab9565b5b600085013567ffffffffffffffff81111561200857612007611abe565b5b61201487828801611e25565b9450945050602085013567ffffffffffffffff81111561203757612036611abe565b5b61204387828801611f7a565b925092505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061208b82611b4e565b915061209683611b4e565b92508282019050808211156120ae576120ad612051565b5b92915050565b600082825260208201905092915050565b82818337600083830152505050565b6000601f19601f8301169050919050565b60006120f183856120b4565b93506120fe8385846120c5565b612107836120d4565b840190509392505050565b61211b81611d26565b82525050565b6000608082019050818103600083015261213c81898b6120e5565b905081810360208301526121518187896120e5565b905081810360408301526121668185876120e5565b90506121756060830184612112565b98975050505050505050565b600061218c82611b4e565b915061219783611b4e565b92508282039050818111156121af576121ae612051565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080fd5b600080fd5b600080fd5b600080833560016020038436030381126122105761220f6121e4565b5b80840192508235915067ffffffffffffffff821115612232576122316121e9565b5b60208301925060018202360383131561224e5761224d6121ee565b5b509250929050565b60008151905061226581611bf2565b92915050565b60006020828403121561228157612280611ab9565b5b600061228f84828501612256565b91505092915050565b6122a181611f3c565b81146122ac57600080fd5b50565b6000813590506122be81612298565b92915050565b6000602082840312156122da576122d9611ab9565b5b60006122e8848285016122af565b91505092915050565b600081905092915050565b50565b600061230c6000836122f1565b9150612317826122fc565b600082019050919050565b600061232d826122ff565b9150819050919050565b600081519050919050565b6000819050602082019050919050565b600061235e8251611d26565b80915050919050565b600082821b905092915050565b600061237f82612337565b8261238984612342565b905061239481612352565b925060208210156123d4576123cf7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83602003600802612367565b831692505b5050919050565b60006123e783856122f1565b93506123f48385846120c5565b82840190509392505050565b600061240d8286886123db565b915061241a8284866123db565b915081905095945050505050565b60005b8381101561244657808201518184015260208101905061242b565b60008484015250505050565b600061245d82612337565b61246781856122f1565b9350612477818560208601612428565b80840191505092915050565b600061248f8284612452565b915081905092915050565b60006124a78284866123db565b91508190509392505050565b60006124be82611b4e565b91506124c983611b4e565b92508282026124d781611b4e565b915082820484148315176124ee576124ed612051565b5b5092915050565b600060608201905061250a6000830186611b58565b6125176020830185611b58565b6125246040830184611b58565b949350505050565b60008160c01b9050919050565b60006125448261252c565b9050919050565b61255c61255782611f3c565b612539565b82525050565b600061256e8285612452565b915061257a828461254b565b6008820191508190509392505050565b6000819050919050565b60006125af6125aa6125a584611f3c565b61258a565b611b4e565b9050919050565b6125bf81612594565b82525050565b60006020820190506125da60008301846125b6565b9291505056fea2646970667358221220817927d3197eeb8430978a922bcfef4412fe39366077fd1f40603cbd14c5b2f664736f6c63430008130033a2646970667358221220ab531b7b2d9269abd9d5f90c7614fb672cbb79e27c253415c639013be138896864736f6c6343000813003300000000000000000000000000431f263ce400f4455c2dcf564e53007ca4bbbb0000000000000000000000000c15f14308530b7cdb8460094bbb9cc28b9aaaaa00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa
Deployed Bytecode
0x60806040523480156200001157600080fd5b5060043610620000525760003560e01c806306dbd0cb14620000575780633e5b34b21462000079578063cdba577d146200009b578063f7f8ff7a14620000d1575b600080fd5b62000061620000f3565b60405162000070919062000464565b60405180910390f35b6200008362000117565b60405162000092919062000464565b60405180910390f35b620000b96004803603810190620000b39190620004fc565b6200013b565b604051620000c89190620005d9565b60405180910390f35b620000db620003ed565b604051620000ea919062000464565b60405180910390f35b7f0000000000000000000000000c15f14308530b7cdb8460094bbb9cc28b9aaaaa81565b7f00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa81565b60008073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1603620001a3576040517f22adafa200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614806200020b5750600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16145b1562000243576040517ffca23e2a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008267ffffffffffffffff160362000288576040517f8966ee9500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6501dcd65000008267ffffffffffffffff161115620002d3576040517f12f9b82600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000431f263ce400f4455c2dcf564e53007ca4bbbb7f0000000000000000000000000c15f14308530b7cdb8460094bbb9cc28b9aaaaa7f00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa87878787604051620003489062000411565b6200035a979695949392919062000607565b604051809103906000f08015801562000377573d6000803e3d6000fd5b5090508473ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f5f69e7dce622f5df4a1899cf39ca6eb1216397f2c07206d2ef33aebf960827a3868686604051620003dd9392919062000684565b60405180910390a3949350505050565b7f00000000000000000000000000431f263ce400f4455c2dcf564e53007ca4bbbb81565b6129ad80620006c283390190565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200044c826200041f565b9050919050565b6200045e816200043f565b82525050565b60006020820190506200047b600083018462000453565b92915050565b600080fd5b62000491816200043f565b81146200049d57600080fd5b50565b600081359050620004b18162000486565b92915050565b600067ffffffffffffffff82169050919050565b620004d681620004b7565b8114620004e257600080fd5b50565b600081359050620004f681620004cb565b92915050565b6000806000806080858703121562000519576200051862000481565b5b60006200052987828801620004a0565b94505060206200053c87828801620004a0565b93505060406200054f87828801620004a0565b92505060606200056287828801620004e5565b91505092959194509250565b6000819050919050565b600062000599620005936200058d846200041f565b6200056e565b6200041f565b9050919050565b6000620005ad8262000578565b9050919050565b6000620005c182620005a0565b9050919050565b620005d381620005b4565b82525050565b6000602082019050620005f06000830184620005c8565b92915050565b6200060181620004b7565b82525050565b600060e0820190506200061e600083018a62000453565b6200062d602083018962000453565b6200063c604083018862000453565b6200064b606083018762000453565b6200065a608083018662000453565b6200066960a083018562000453565b6200067860c0830184620005f6565b98975050505050505050565b60006060820190506200069b600083018662000453565b620006aa602083018562000453565b620006b96040830184620005f6565b94935050505056fe6101206040523480156200001257600080fd5b50604051620029ad380380620029ad83398181016040528101906200003891906200026e565b8673ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff16815250508573ffffffffffffffffffffffffffffffffffffffff1660a08173ffffffffffffffffffffffffffffffffffffffff16815250508473ffffffffffffffffffffffffffffffffffffffff1660c08173ffffffffffffffffffffffffffffffffffffffff1681525050826000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff1660e08173ffffffffffffffffffffffffffffffffffffffff16815250508067ffffffffffffffff166101008167ffffffffffffffff168152505062000176846200018360201b60201c565b5050505050505062000321565b8060601b60601c905080638b78c6d819558060007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a350565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620001f182620001c4565b9050919050565b6200020381620001e4565b81146200020f57600080fd5b50565b6000815190506200022381620001f8565b92915050565b600067ffffffffffffffff82169050919050565b620002488162000229565b81146200025457600080fd5b50565b60008151905062000268816200023d565b92915050565b600080600080600080600060e0888a03121562000290576200028f620001bf565b5b6000620002a08a828b0162000212565b9750506020620002b38a828b0162000212565b9650506040620002c68a828b0162000212565b9550506060620002d98a828b0162000212565b9450506080620002ec8a828b0162000212565b93505060a0620002ff8a828b0162000212565b92505060c0620003128a828b0162000257565b91505092959891949750929550565b60805160a05160c05160e051610100516126166200039760003960008181610e22015261159c0152600081816107a101526116c40152600081816108180152610c0d01526000818161077d01528181610ff1015261183801526000818161093c015281816111d1015261138e01526126166000f3fe6080604052600436106101fd5760003560e01c80634d5be1f81161010d5780637bfafd59116100a05780639fdb5e8a1161006f5780639fdb5e8a14610645578063f04e283e14610670578063f2fde38b1461068c578063f7f8ff7a146106a8578063fee81cf4146106d357610204565b80637bfafd59146105aa5780638da5cb5b146105d55780638f98f67c146106005780639066a3001461062957610204565b80635f097d75116100dc5780635f097d751461051f57806367db90c21461054a5780636c19688614610575578063715018a6146105a057610204565b80634d5be1f814610484578063514e62fc146104af57806351cff8d9146104ec57806354d1f13d1461051557610204565b806322be1ffd116101905780632de948071161015f5780632de94807146103be578063377b2c34146103fb5780633a6a4d2e146104265780633e5b34b21461043d5780634a4ee7b11461046857610204565b806322be1ffd1461035857806324ae6a2714610374578063256929621461039d5780632d7fd507146103a757610204565b8063183a4f6e116101cc578063183a4f6e146102c75780631c10893f146102e35780631cd64df4146102ff578063228951181461033c57610204565b806303615ba314610209578063043edae81461024657806306dbd0cb1461027157806317f333401461029c57610204565b3661020457005b600080fd5b34801561021557600080fd5b50610230600480360381019061022b9190611b21565b610710565b60405161023d9190611b67565b60405180910390f35b34801561025257600080fd5b5061025b610759565b6040516102689190611bad565b60405180910390f35b34801561027d57600080fd5b5061028661077b565b6040516102939190611bd7565b60405180910390f35b3480156102a857600080fd5b506102b161079f565b6040516102be9190611bd7565b60405180910390f35b6102e160048036038101906102dc9190611c1e565b6107c3565b005b6102fd60048036038101906102f89190611c4b565b6107d0565b005b34801561030b57600080fd5b5061032660048036038101906103219190611c4b565b6107e6565b6040516103339190611ca6565b60405180910390f35b61035660048036038101906103519190611d5c565b6107fd565b005b610372600480360381019061036d9190611e7b565b6108b7565b005b34801561038057600080fd5b5061039b60048036038101906103969190611efc565b610a41565b005b6103a5610b60565b005b3480156103b357600080fd5b506103bc610bb4565b005b3480156103ca57600080fd5b506103e560048036038101906103e09190611b21565b610bc0565b6040516103f29190611b67565b60405180910390f35b34801561040757600080fd5b50610410610bdb565b60405161041d9190611bd7565b60405180910390f35b34801561043257600080fd5b5061043b610bff565b005b34801561044957600080fd5b50610452610c0b565b60405161045f9190611bd7565b60405180910390f35b610482600480360381019061047d9190611c4b565b610c2f565b005b34801561049057600080fd5b50610499610c45565b6040516104a69190611b67565b60405180910390f35b3480156104bb57600080fd5b506104d660048036038101906104d19190611c4b565b610c4a565b6040516104e39190611ca6565b60405180910390f35b3480156104f857600080fd5b50610513600480360381019061050e9190611b21565b610c62565b005b61051d610dc0565b005b34801561052b57600080fd5b50610534610dfc565b6040516105419190611b67565b60405180910390f35b34801561055657600080fd5b5061055f610e02565b60405161056c9190611b67565b60405180910390f35b34801561058157600080fd5b5061058a610e07565b6040516105979190611b67565b60405180910390f35b6105a8610e0c565b005b3480156105b657600080fd5b506105bf610e20565b6040516105cc9190611f5f565b60405180910390f35b3480156105e157600080fd5b506105ea610e44565b6040516105f79190611bd7565b60405180910390f35b34801561060c57600080fd5b5061062760048036038101906106229190611b21565b610e52565b005b610643600480360381019061063e9190611fd0565b610f88565b005b34801561065157600080fd5b5061065a61115f565b6040516106679190611b67565b60405180910390f35b61068a60048036038101906106859190611b21565b611164565b005b6106a660048036038101906106a19190611b21565b6111a5565b005b3480156106b457600080fd5b506106bd6111cf565b6040516106ca9190611bd7565b60405180910390f35b3480156106df57600080fd5b506106fa60048036038101906106f59190611b21565b6111f3565b6040516107079190611b67565b60405180910390f35b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600260009054906101000a90046fffffffffffffffffffffffffffffffff1681565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b6107cd338261120e565b50565b6107d861121e565b6107e2828261123b565b5050565b600081826107f385610bc0565b1614905092915050565b346001600082825461080f9190612080565b925050819055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632289511834898989898989896040518963ffffffff1660e01b815260040161087c9796959493929190612121565b6000604051808303818588803b15801561089557600080fd5b505af11580156108a9573d6000803e3d6000fd5b505050505050505050505050565b60026108c28161124b565b60008585905014806108d75750603f85859050115b806108e6575060308383905014155b1561091d576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000349050600086869050905060005b818110156109e65760006109607f0000000000000000000000000000000000000000000000000000000000000000611282565b90508381111561099c576040517f7953952a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80846109a89190612181565b93506109da8989848181106109c0576109bf6121b5565b5b90506020028101906109d291906121f3565b89898561133c565b8160010191505061092d565b506000821115610a38573373ffffffffffffffffffffffffffffffffffffffff166108fc839081150290604051600060405180830381858888f19350505050158015610a36573d6000803e3d6000fd5b505b50505050505050565b6008610a4c8161124b565b60008373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610a879190611bd7565b602060405180830381865afa158015610aa4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ac8919061226b565b9050610af583828673ffffffffffffffffffffffffffffffffffffffff166114ef9092919063ffffffff16565b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f02dc46f45168b3833fa84aa22ee90f4ffec812cf1067be0413353eb95c031b0583604051610b529190611b67565b60405180910390a350505050565b6000610b6a61153e565b67ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b610bbe6001611549565b565b6000638b78c6d8600c52816000526020600c20549050919050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610c096000611549565b565b7f000000000000000000000000000000000000000000000000000000000000000081565b610c3761121e565b610c41828261120e565b5050565b600481565b60008082610c5785610bc0565b161415905092915050565b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905080600260008282829054906101000a90046fffffffffffffffffffffffffffffffff160392506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055506000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610d6e818373ffffffffffffffffffffffffffffffffffffffff1661179b90919063ffffffff16565b8173ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b6582604051610db49190611b67565b60405180910390a25050565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b60015481565b600181565b600281565b610e1461121e565b610e1e60006117bb565b565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000638b78c6d81954905090565b6004610e5d8161124b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610ec3576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050826000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fa4de47033aed06f80e7c49451826fa061ea6eb7a8f7f1f2b0148dc3b0930165160405160405180910390a3505050565b6001610f938161124b565b828290508585905014610fd2576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000349050600086869050905060005b818110156111045760006110157f0000000000000000000000000000000000000000000000000000000000000000611282565b905083811115611051576040517f7953952a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808461105d9190612181565b93506110f8898984818110611075576110746121b5565b5b905060200281019061108791906121f3565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508888858181106110dd576110dc6121b5565b5b90506020020160208101906110f291906122c4565b836117f9565b81600101915050610fe2565b506000821115611156573373ffffffffffffffffffffffffffffffffffffffff166108fc839081150290604051600060405180830381858888f19350505050158015611154573d6000803e3d6000fd5b505b50505050505050565b600881565b61116c61121e565b63389a75e1600c52806000526020600c20805442111561119457636f5e88186000526004601cfd5b60008155506111a2816117bb565b50565b6111ad61121e565b8060601b6111c357637448fbae6000526004601cfd5b6111cc816117bb565b50565b7f000000000000000000000000000000000000000000000000000000000000000081565b600063389a75e1600c52816000526020600c20549050919050565b61121a82826000611984565b5050565b638b78c6d819543314611239576382b429006000526004601cfd5b565b61124782826001611984565b5050565b638b78c6d81954331461127f57638b78c6d8600c5233600052806020600c20541661127e576382b429006000526004601cfd5b5b50565b60008060008373ffffffffffffffffffffffffffffffffffffffff166040516112aa90612322565b600060405180830381855afa9150503d80600081146112e5576040519150601f19603f3d011682016040523d82523d6000602084013e6112ea565b606091505b509150915081611326576040517f448e5c4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8061133090612374565b60001c92505050919050565b603085859050141580611353575060308383905014155b1561138a576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1682878787876040516020016113db9493929190612400565b6040516020818303038152906040526040516113f79190612483565b60006040518083038185875af1925050503d8060008114611434576040519150601f19603f3d011682016040523d82523d6000602084013e611439565b606091505b5050905080611474576040517f1684b73e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b838360405161148492919061249a565b6040518091039020868660405161149c92919061249a565b60405180910390203373ffffffffffffffffffffffffffffffffffffffff167f4ca695eebdc4cd20b1b742b57002d491913323670e87798f75ff63f9af522df860405160405180910390a4505050505050565b81601452806034526fa9059cbb00000000000000000000000060005260206000604460106000875af13d156001600051141716611534576390b8ec186000526004601cfd5b6000603452505050565b60006202a300905090565b60004790506000600260009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff169050600081836115919190612181565b90506000633b9aca007f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff166115cf91906124b3565b90506000808284101580156115e657506000600154115b1561160f576001548411156116065760015491506001548403905061160a565b8391505b611613565b8390505b6fffffffffffffffffffffffffffffffff801684111561165f576040517f502cbcb200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082111561169457816fffffffffffffffffffffffffffffffff166001600082825461168c9190612181565b925050819055505b6116bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683896119de565b6116ea7f000000000000000000000000000000000000000000000000000000000000000082896119de565b600187036117575760008211806117015750600081115b15611756578082866117139190612080565b61171d9190612080565b600260006101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b5b7f8b367b31582d5c638694615c1cfc77ffcf9febcf6e6c793358089f7769cfeee682828960405161178a939291906124f5565b60405180910390a150505050505050565b60005a60005a84865af16117b75763b12d13eb6000526004601cfd5b5050565b638b78c6d8198160601b60601c91508181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a38181555050565b6030835114611834576040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16828585604051602001611881929190612562565b60405160208183030381529060405260405161189d9190612483565b60006040518083038185875af1925050503d80600081146118da576040519150601f19603f3d011682016040523d82523d6000602084013e6118df565b606091505b505090508061191a576040517fd3d2fd8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b836040516119289190612483565b60405180910390203373ffffffffffffffffffffffffffffffffffffffff167f1e55aec951b70d2fce6d30fa2f2dfc3c3d280c2a85c04b7060ee6194f75261038560405161197691906125c5565b60405180910390a350505050565b638b78c6d8600c52826000526020600c208054838117836119a757848216821890505b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a3505050505050565b6000821115611ab45760018103611a4a5781600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611a3e9190612080565b92505081905550611ab3565b60008103611a8057611a7b828473ffffffffffffffffffffffffffffffffffffffff1661179b90919063ffffffff16565b611ab2565b6040517f19cdc2fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b5b505050565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000611aee82611ac3565b9050919050565b611afe81611ae3565b8114611b0957600080fd5b50565b600081359050611b1b81611af5565b92915050565b600060208284031215611b3757611b36611ab9565b5b6000611b4584828501611b0c565b91505092915050565b6000819050919050565b611b6181611b4e565b82525050565b6000602082019050611b7c6000830184611b58565b92915050565b60006fffffffffffffffffffffffffffffffff82169050919050565b611ba781611b82565b82525050565b6000602082019050611bc26000830184611b9e565b92915050565b611bd181611ae3565b82525050565b6000602082019050611bec6000830184611bc8565b92915050565b611bfb81611b4e565b8114611c0657600080fd5b50565b600081359050611c1881611bf2565b92915050565b600060208284031215611c3457611c33611ab9565b5b6000611c4284828501611c09565b91505092915050565b60008060408385031215611c6257611c61611ab9565b5b6000611c7085828601611b0c565b9250506020611c8185828601611c09565b9150509250929050565b60008115159050919050565b611ca081611c8b565b82525050565b6000602082019050611cbb6000830184611c97565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f840112611ce657611ce5611cc1565b5b8235905067ffffffffffffffff811115611d0357611d02611cc6565b5b602083019150836001820283011115611d1f57611d1e611ccb565b5b9250929050565b6000819050919050565b611d3981611d26565b8114611d4457600080fd5b50565b600081359050611d5681611d30565b92915050565b60008060008060008060006080888a031215611d7b57611d7a611ab9565b5b600088013567ffffffffffffffff811115611d9957611d98611abe565b5b611da58a828b01611cd0565b9750975050602088013567ffffffffffffffff811115611dc857611dc7611abe565b5b611dd48a828b01611cd0565b9550955050604088013567ffffffffffffffff811115611df757611df6611abe565b5b611e038a828b01611cd0565b93509350506060611e168a828b01611d47565b91505092959891949750929550565b60008083601f840112611e3b57611e3a611cc1565b5b8235905067ffffffffffffffff811115611e5857611e57611cc6565b5b602083019150836020820283011115611e7457611e73611ccb565b5b9250929050565b60008060008060408587031215611e9557611e94611ab9565b5b600085013567ffffffffffffffff811115611eb357611eb2611abe565b5b611ebf87828801611e25565b9450945050602085013567ffffffffffffffff811115611ee257611ee1611abe565b5b611eee87828801611cd0565b925092505092959194509250565b60008060408385031215611f1357611f12611ab9565b5b6000611f2185828601611b0c565b9250506020611f3285828601611b0c565b9150509250929050565b600067ffffffffffffffff82169050919050565b611f5981611f3c565b82525050565b6000602082019050611f746000830184611f50565b92915050565b60008083601f840112611f9057611f8f611cc1565b5b8235905067ffffffffffffffff811115611fad57611fac611cc6565b5b602083019150836020820283011115611fc957611fc8611ccb565b5b9250929050565b60008060008060408587031215611fea57611fe9611ab9565b5b600085013567ffffffffffffffff81111561200857612007611abe565b5b61201487828801611e25565b9450945050602085013567ffffffffffffffff81111561203757612036611abe565b5b61204387828801611f7a565b925092505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061208b82611b4e565b915061209683611b4e565b92508282019050808211156120ae576120ad612051565b5b92915050565b600082825260208201905092915050565b82818337600083830152505050565b6000601f19601f8301169050919050565b60006120f183856120b4565b93506120fe8385846120c5565b612107836120d4565b840190509392505050565b61211b81611d26565b82525050565b6000608082019050818103600083015261213c81898b6120e5565b905081810360208301526121518187896120e5565b905081810360408301526121668185876120e5565b90506121756060830184612112565b98975050505050505050565b600061218c82611b4e565b915061219783611b4e565b92508282039050818111156121af576121ae612051565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080fd5b600080fd5b600080fd5b600080833560016020038436030381126122105761220f6121e4565b5b80840192508235915067ffffffffffffffff821115612232576122316121e9565b5b60208301925060018202360383131561224e5761224d6121ee565b5b509250929050565b60008151905061226581611bf2565b92915050565b60006020828403121561228157612280611ab9565b5b600061228f84828501612256565b91505092915050565b6122a181611f3c565b81146122ac57600080fd5b50565b6000813590506122be81612298565b92915050565b6000602082840312156122da576122d9611ab9565b5b60006122e8848285016122af565b91505092915050565b600081905092915050565b50565b600061230c6000836122f1565b9150612317826122fc565b600082019050919050565b600061232d826122ff565b9150819050919050565b600081519050919050565b6000819050602082019050919050565b600061235e8251611d26565b80915050919050565b600082821b905092915050565b600061237f82612337565b8261238984612342565b905061239481612352565b925060208210156123d4576123cf7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83602003600802612367565b831692505b5050919050565b60006123e783856122f1565b93506123f48385846120c5565b82840190509392505050565b600061240d8286886123db565b915061241a8284866123db565b915081905095945050505050565b60005b8381101561244657808201518184015260208101905061242b565b60008484015250505050565b600061245d82612337565b61246781856122f1565b9350612477818560208601612428565b80840191505092915050565b600061248f8284612452565b915081905092915050565b60006124a78284866123db565b91508190509392505050565b60006124be82611b4e565b91506124c983611b4e565b92508282026124d781611b4e565b915082820484148315176124ee576124ed612051565b5b5092915050565b600060608201905061250a6000830186611b58565b6125176020830185611b58565b6125246040830184611b58565b949350505050565b60008160c01b9050919050565b60006125448261252c565b9050919050565b61255c61255782611f3c565b612539565b82525050565b600061256e8285612452565b915061257a828461254b565b6008820191508190509392505050565b6000819050919050565b60006125af6125aa6125a584611f3c565b61258a565b611b4e565b9050919050565b6125bf81612594565b82525050565b60006020820190506125da60008301846125b6565b9291505056fea2646970667358221220817927d3197eeb8430978a922bcfef4412fe39366077fd1f40603cbd14c5b2f664736f6c63430008130033a2646970667358221220ab531b7b2d9269abd9d5f90c7614fb672cbb79e27c253415c639013be138896864736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000431f263ce400f4455c2dcf564e53007ca4bbbb0000000000000000000000000c15f14308530b7cdb8460094bbb9cc28b9aaaaa00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa
-----Decoded View---------------
Arg [0] : _consolidationSystemContract (address): 0x00431F263cE400f4455c2dCf564e53007Ca4bbBb
Arg [1] : _withdrawalSystemContract (address): 0x0c15F14308530b7CDB8460094BbB9cC28b9AaaAA
Arg [2] : _depositSystemContract (address): 0x00000000219ab540356cBB839Cbe05303d7705Fa
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 00000000000000000000000000431f263ce400f4455c2dcf564e53007ca4bbbb
Arg [1] : 0000000000000000000000000c15f14308530b7cdb8460094bbb9cc28b9aaaaa
Arg [2] : 00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa
Deployed Bytecode Sourcemap
84525:5002:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;86087:49;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;86141:46;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;88615:909;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;86030:52;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;86087:49;;;:::o;86141:46::-;;;:::o;88615:909::-;88789:24;88843:1;88826:19;;:5;:19;;;88822:47;;88854:15;;;;;;;;;;;;;;88822:47;88910:1;88880:32;;:18;:32;;;:65;;;;88943:1;88916:29;;:15;:29;;;88880:65;88876:99;;;88954:21;;;;;;;;;;;;;;88876:99;89008:1;88986:18;:23;;;88982:60;;89018:24;;;;;;;;;;;;;;88982:60;89074:10;89053:18;:31;;;89049:72;;;89093:28;;;;;;;;;;;;;;89049:72;89169:27;89205:24;89238:21;89268:5;89282:18;89309:15;89333:18;89136:222;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;89130:228;;89428:5;89372:146;;89415:3;89372:146;;;89442:18;89469:15;89493:18;89372:146;;;;;;;;:::i;:::-;;;;;;;;88615:909;;;;;;:::o;86030:52::-;;;:::o;-1:-1:-1:-;;;;;;;;:::o;7:126:1:-;44:7;84:42;77:5;73:54;62:65;;7:126;;;:::o;139:96::-;176:7;205:24;223:5;205:24;:::i;:::-;194:35;;139:96;;;:::o;241:118::-;328:24;346:5;328:24;:::i;:::-;323:3;316:37;241:118;;:::o;365:222::-;458:4;496:2;485:9;481:18;473:26;;509:71;577:1;566:9;562:17;553:6;509:71;:::i;:::-;365:222;;;;:::o;674:117::-;783:1;780;773:12;920:122;993:24;1011:5;993:24;:::i;:::-;986:5;983:35;973:63;;1032:1;1029;1022:12;973:63;920:122;:::o;1048:139::-;1094:5;1132:6;1119:20;1110:29;;1148:33;1175:5;1148:33;:::i;:::-;1048:139;;;;:::o;1193:101::-;1229:7;1269:18;1262:5;1258:30;1247:41;;1193:101;;;:::o;1300:120::-;1372:23;1389:5;1372:23;:::i;:::-;1365:5;1362:34;1352:62;;1410:1;1407;1400:12;1352:62;1300:120;:::o;1426:137::-;1471:5;1509:6;1496:20;1487:29;;1525:32;1551:5;1525:32;:::i;:::-;1426:137;;;;:::o;1569:763::-;1654:6;1662;1670;1678;1727:3;1715:9;1706:7;1702:23;1698:33;1695:120;;;1734:79;;:::i;:::-;1695:120;1854:1;1879:53;1924:7;1915:6;1904:9;1900:22;1879:53;:::i;:::-;1869:63;;1825:117;1981:2;2007:53;2052:7;2043:6;2032:9;2028:22;2007:53;:::i;:::-;1997:63;;1952:118;2109:2;2135:53;2180:7;2171:6;2160:9;2156:22;2135:53;:::i;:::-;2125:63;;2080:118;2237:2;2263:52;2307:7;2298:6;2287:9;2283:22;2263:52;:::i;:::-;2253:62;;2208:117;1569:763;;;;;;;:::o;2338:60::-;2366:3;2387:5;2380:12;;2338:60;;;:::o;2404:142::-;2454:9;2487:53;2505:34;2514:24;2532:5;2514:24;:::i;:::-;2505:34;:::i;:::-;2487:53;:::i;:::-;2474:66;;2404:142;;;:::o;2552:134::-;2610:9;2643:37;2674:5;2643:37;:::i;:::-;2630:50;;2552:134;;;:::o;2692:171::-;2779:9;2812:45;2851:5;2812:45;:::i;:::-;2799:58;;2692:171;;;:::o;2869:205::-;2993:74;3061:5;2993:74;:::i;:::-;2988:3;2981:87;2869:205;;:::o;3080:296::-;3210:4;3248:2;3237:9;3233:18;3225:26;;3261:108;3366:1;3355:9;3351:17;3342:6;3261:108;:::i;:::-;3080:296;;;;:::o;3382:115::-;3467:23;3484:5;3467:23;:::i;:::-;3462:3;3455:36;3382:115;;:::o;3503:882::-;3762:4;3800:3;3789:9;3785:19;3777:27;;3814:71;3882:1;3871:9;3867:17;3858:6;3814:71;:::i;:::-;3895:72;3963:2;3952:9;3948:18;3939:6;3895:72;:::i;:::-;3977;4045:2;4034:9;4030:18;4021:6;3977:72;:::i;:::-;4059;4127:2;4116:9;4112:18;4103:6;4059:72;:::i;:::-;4141:73;4209:3;4198:9;4194:19;4185:6;4141:73;:::i;:::-;4224;4292:3;4281:9;4277:19;4268:6;4224:73;:::i;:::-;4307:71;4373:3;4362:9;4358:19;4349:6;4307:71;:::i;:::-;3503:882;;;;;;;;;;:::o;4391:438::-;4538:4;4576:2;4565:9;4561:18;4553:26;;4589:71;4657:1;4646:9;4642:17;4633:6;4589:71;:::i;:::-;4670:72;4738:2;4727:9;4723:18;4714:6;4670:72;:::i;:::-;4752:70;4818:2;4807:9;4803:18;4794:6;4752:70;:::i;:::-;4391:438;;;;;;:::o
Swarm Source
ipfs://ab531b7b2d9269abd9d5f90c7614fb672cbb79e27c253415c639013be1388968
Loading...
Loading
Loading...
Loading
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.